Orion Finance SDK#
Python SDK and CLI for Orion’s onchain portfolio management infrastructure: deploy vaults, submit strategist intents, and read protocol state, including the whitelisted investment universe, without leaving Python or the shell.
PyPI: orion-finance-sdk-py
Source: GitHub
What you can do
Install the CLI or package, set environment variables, and connect to an RPC.
Interactive orion menu and scriptable commands for every role.
List the onchain whitelist: names, addresses, and how to query it.
Sepolia twins, mainnetSource(), and which address to use where.
Create transparent vaults, update strategist and fees, verify deployment.
Push rebalancing allocations from JSON, CSV, Parquet, or an inline dict.
PIT prices, asset price history, share-price series, and intent vs holdings.
SASR ranking, skfolio Sharpe/vol/CVaR, covariance, PCA, and MeanRisk weights.
Uniswap v3 fee and slippage for a signed asset trade, as of a calendar date.
Quick start#
curl -sSfL https://sdk.orionfinance.ai/cli/install.sh | sh
orion --help
Or install from PyPI:
pip install "orion-finance-sdk-py>=2.1.1"
Running orion with no arguments opens the interactive console.
from orion_finance_sdk_py import OrionConfig
config = OrionConfig()
print(f"Risk-free Rate: {config.risk_free_rate}")
Manager workflow#
Install the SDK (above).
Deploy a vault with
orion deploy-vault(managers).Set strategist if you want to submit intents as the manager (
orion update-strategist).Submit intents with
orion submit-intent.
Configure environment#
Create a .env in your project directory. Keep it private and never commit it.
Task |
Variables |
|---|---|
Deploy / manage a vault |
|
Submit intents |
|
LP deposit / redeem |
|
Read vault data |
Pass |
Estimate execution cost |
Optional |
Orion Console#
The SDK ships a CLI named orion. With no subcommand it opens an interactive menu. With a subcommand it runs that action non-interactively.
orion # interactive console
orion --help # list scriptable commands
Scriptable commands#
orion deploy-vault --help
orion submit-intent --help
orion list-whitelisted-assets
orion list-asset-address-map
Investment universe#
The investment universe is the onchain whitelist in OrionConfig: the only tokens a vault may hold and the only keys allowed in a strategist intent. The list is maintained on the protocol, not in this SDK, so query it rather than copying a static table.
Names come from whitelisted_asset_names (same order as the addresses).
List from the console#
orion list-whitelisted-assets
Or from the interactive menu: Access and assets → List Whitelisted Assets. The table prints name, address, and a total count.
On Sepolia, those addresses are twins. Map them to mainnet with orion list-asset-address-map (Testnet sandbox).
List from Python#
from orion_finance_sdk_py import OrionConfig
config = OrionConfig()
for name, address in zip(
config.whitelisted_asset_names,
config.whitelisted_assets,
strict=True,
):
print(name.strip(), address)
config.is_whitelisted("0x...")
Testnet sandbox#
The operational sandbox is Ethereum Sepolia. Vaults, intents, and the investment universe live there. Twin ERC-20s on Sepolia expose mainnetSource() so you can recover the Ethereum mainnet token they stand in for.
The SDK mapping is testnet → mainnet. Tokens that do not implement the getter, revert, or return address(0) are omitted.
Sepolia twin --mainnetSource()--> Ethereum mainnet token
^ ^
| |
intents, whitelist, vaults get_cost
List the map#
orion list-asset-address-map
Or from the interactive menu: Access and assets → List Asset Address Map. Each twin is printed as a Testnet / Mainnet pair.
from orion_finance_sdk_py import build_asset_address_map
address_map = build_asset_address_map()
# {checksummed Sepolia address: checksummed mainnet address}
Vault operations#
Managers create transparent or encrypted vaults with the CLI. Use the interactive console or the commands below.
Deploy a vault#
orion deploy-vault \
--name "Algorithmic Liquidity Provision & Hedging Agent" \
--symbol "ALPHA" \
--fee-type hard_hurdle \
--performance-fee 100 \
--management-fee 10 \
--strategist-address 0x... \
--vault-type transparent
Use --vault-type encrypted for confidential vaults. Default is transparent.
--deposit-access-control, --holder-access-control, and --transfer-access-control default to the zero address (permissionless). Pass the same plugin address to all three for a unified whitelist.
This deploys an ERC-7540 vault, registers the manager from your .env, and sets fees.
Update strategist or fees#
orion update-strategist --new-strategist-address 0x...
orion update-fee-model \
--fee-type high_water_mark \
--performance-fee 5.5 \
--management-fee 0.1
LP deposit / redeem#
Needs ORION_VAULT_ADDRESS and LP_PRIVATE_KEY. Amounts for deposit/cancel-deposit are human units of the vault underlying.
orion request-deposit --assets 1.5
orion cancel-deposit-request --amount 1.5
orion request-redeem --shares 500000
orion cancel-redeem-request --shares 500000
# After full decommission only:
orion redeem --shares 500000 --receiver 0x... --owner 0x...
Remove / decommission vault (manager)#
orion remove-vault
Submit rebalancing order intents#
Strategists (or managers who set themselves as strategist) submit portfolio allocation intents executed on the next rebalancing cycle.
--order-intent (alias --order-intent-path) accepts a file or an inline string:
JSON file: object mapping token addresses → weights (fractions summing to 1).
CSV / Parquet: tabular; Parquet needs pyarrow (
pip install 'orion-finance-sdk-py[parquet]').Inline: JSON object or Python
dictliteral.
orion submit-intent --order-intent order_intent.json
orion submit-intent --order-intent '{"0x...": 0.5, "0x...": 0.5}'
Intents are collected and executed at the next rebalance (bundling, batching, netting).
Portfolio file schema#
Column Name |
Type |
Description |
|---|---|---|
|
string |
Token contract address (checksummed). |
|
decimal |
Percentage of total vault value to allocate (0-100). |
Aliases: token / addr for address; weight, value, or percentage for weights. Columns named percentage_of_tvl / percentage are treated as 0–100 and normalized to fractions.
Example intent (addresses must be on the current investment universe for the chain you are connected to):
{
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": 0.5,
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2": 0.3,
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599": 0.2
}
Note: On transparent vaults, intents are visible onchain after submission. Confidential vault intents are sealed with Orion HPKE before submission, the chain stores an opaque
OrionCiphertext:
0x693658254630f73ad8da78fb331bf976cd42f90e0e9c9e83f40c51072a6f7417
bdb98ee21c5fbd63b638e332a609dad2c433b7dddcadec1f43586b8df178c488
01bc357ad2b6b530cfd9d63a8da1e0506ad748b7445373c5028131d0133fd503
172331decd5b2c69027a78699d713bb7426278f5cfa7754cb9e608f8cac946be
f38dd79d43391007eb7e87cca2a60b9e8ba869ae6dcdeb9289a09fa9748fc6a7
57160df5b25eb1fab133b18d11865a12f04a6ba70fa9a7f6c1dcf32fdffcabda
cd028fb3317650842264ba149850c24af40f1351564153ed0e0e00594caf6cef
a9981d8f622a44fe2c905be117a8d6a9c2cb51e787d6b27a7676bf6ccb89ed42
e3b8d9b4c24e887030a7caf503ba52d1d44510f73f5e7d30a396b4662a409178
234f21d6d9a3a1256b7ae0aac3a31c5f5242a66db00495f1a46f8d171dd7be9d
60d6dbaf04562614a8176738d66cf746101efeb2721f44ff2ed3ed3c3a1882ac
cbee5712dff0103eca8470ee5774e02e
Analytics and reads#
Point-in-time prices and portfolio weights#
Point-in-time oracle prices for the investment universe, combined with vault holdings for portfolio weights:
from orion_finance_sdk_py import (
OrionTransparentVault,
PriceAdapterRegistry,
)
registry = PriceAdapterRegistry()
prices = registry.get_prices() # address -> price for every whitelisted asset
vault = OrionTransparentVault() # or OrionTransparentVault(contract_address="0x...")
portfolio = vault.get_portfolio() # address -> shares
pct_tvl = vault.get_portfolio_pct_tvl() # address -> weight (sums to ~1)
pit_tvl = vault.point_in_time_total_assets()
share_price = vault.share_price # value of 1 full share in underlying units
PriceAdapterRegistry is resolved from OrionConfig.price_adapter_registry. Pass assets= to get_prices to price a subset.
Investment universe price history#
Screen whitelisted assets before deploying a vault - daily PIT prices from the adapter registry:
from datetime import datetime, timezone, timedelta
from orion_finance_sdk_py import OrionConfig, PriceAdapterRegistry
config = OrionConfig()
registry = PriceAdapterRegistry()
end = datetime.now(timezone.utc)
start = end - timedelta(days=30)
series = registry.price_history(start=start, end=end)
# [{"timestamp": int, "block": int, "prices": {addr: int, ...}}, ...]
# Optional subset:
# series = registry.price_history(
# start=start, end=end, assets=config.whitelisted_assets[:3]
# )
For long series, set a dedicated RPC_URL - public endpoints are rate-limited.
A longer research walkthrough (excess returns, covariance, a sample portfolio) is in notebooks/investment_universe_research.ipynb. Prefer Return series, ranking, and risk measures for ranking and measures so notebooks do not reimplement SASR.
Vault metadata and strategist intent#
from orion_finance_sdk_py import OrionConfig, OrionTransparentVault
config = OrionConfig()
for addr in config.orion_transparent_vaults:
vault = OrionTransparentVault(contract_address=addr)
print(vault.name, vault.symbol, vault.decimals)
print(vault.manager_address, vault.strategist_address)
intent = vault.get_intent() # address -> fraction (sum ≈ 1); {} if unset
current = vault.get_portfolio_pct_tvl()
# Diff intent vs current to reason about expected rebalancing
get_intent() scales onchain weights by OrionConfig.strategist_intent_decimals so they match the fractional weights used when submitting intents.
Return series, ranking, and risk measures#
Turn on-chain price or share-price history into a ReturnSeries, then rank products by SASR (statistically adjusted Sharpe). SASR is the only product ranking score: do not rank by raw Sharpe, window total return, PSR, or MinTRL.
from datetime import datetime, timedelta, timezone
from orion_finance_sdk_py import (
OrionConfig,
PriceAdapterRegistry,
ReturnSeries,
covariance,
measures,
rank_products,
)
from orion_finance_sdk_py.stats import rfr_decimal
config = OrionConfig()
registry = PriceAdapterRegistry()
end = datetime.now(timezone.utc)
start = end - timedelta(days=30)
series = registry.price_history(start=start, end=end)
names = dict(zip(config.whitelisted_assets, config.whitelisted_asset_names))
rs = ReturnSeries.from_price_history(
series, decimals=registry.price_adapter_decimals, names=names
)
rfr = rfr_decimal(config.risk_free_rate) # 410 bps → 0.041
ranking = rank_products(rs, rfr=rfr) # SASR descending
table = measures.product_scoreboard(rs, rfr=rfr)
cov = covariance.sample(rs)
Vault share-price panels use ReturnSeries.from_share_price_histories.
Hygiene. Ranking, Sharpe, covariance, PCA, and MeanRisk use only contiguous one-calendar-day observations: a gap longer than one day drops the gap-boundary return so a multi-day jump is not treated as a daily return. Path stats (total return, CAGR, max drawdown, normalized wealth) use the price path including gaps. Missing prices are not forward-filled.
Annualization. Default periods_per_year=365 (crypto/DeFi calendar). skfolio Portfolio objects default to 252; this pack passes 365. Sample Sharpe uses Bessel-corrected std (ddof=1), matching skfolio standard_deviation(..., biased=False). Bailey vSr skew and kurtosis are population moments.
Risk-free rate. OrionConfig.risk_free_rate is annualized basis points. Ranking and excess returns subtract the compounded daily rate (1 + rfr) ** (1 / 365) - 1.
from orion_finance_sdk_py.stats import factors, portfolio
excess = rs.excess_returns(rfr)
pca_fit = factors.pca(excess)
train, test = portfolio.chronological_split(excess)
mv = portfolio.min_variance(train)
# mv.weights — labeled Series
Estimate execution cost#
Estimate Uniswap v3 execution cost (pool fee plus price impact) for a signed trade in an Orion universe asset versus USDC.
signed_size is human units of the risk asset: positive buys that many tokens (exact-output, matching adapter buy), negative sells them (exact-input, matching adapter sell).
When constructing ExecutionCostEstimator without an explicit rpc_url or MAINNET_RPC_URL, the SDK probes public Ethereum mainnet RPCs (publicnode → Alchemy public → 1rpc → drpc). Set MAINNET_RPC_URL to an archival endpoint for historical timestamp queries and higher rate limits — public RPCs often cannot serve old eth_call snapshots.
from orion_finance_sdk_py import ExecutionCostEstimator
est = ExecutionCostEstimator()
now = est.get_cost("WETH", 1.5)
btc = est.get_cost("WBTC", 0.5)
past = est.get_cost("WETH", 1.5, timestamp="2026-08-01")
netted = est.get_cost("WETH", 1.5, timestamp="2026-08-01", netting_eta=0.3)
# now.fee_pct, now.slippage_pct, now.cost_pct
symbol: ticker (
WETH,WBTC) or mainnet token address. Not a Sepolia twin — see Testnet sandbox.timestamp: UTC
YYYY-MM-DD. Omitted means now. Unix seconds and block numbers are internal.netting_eta: shrinks the swap to
(1 - η) * signed_size, then runs the full non-linear cost model on that size.
Cost coverage is a subset of the onchain investment universe: WETH, WBTC, XAUt, USDT, and DAI versus USDC.