Accounts and Holdings
Publish and inspect account positions: account holdings, target positions, and virtual-fund allocation. Target positions can reference either direct assets or portfolio sleeves, which connects this chapter to Portfolios.
For the runtime model behind these row APIs, see Core Concepts.
Accounts, virtual funds, and portfolios
import msm
from msm.api.accounts import Account
from msm.api.calendars import Calendar
from msm.api.portfolios import Portfolio
from msm.api.virtual_funds import VirtualFund
msm.start_engine(
models=["Account", "Calendar", "CalendarDate", "CalendarSession", "Portfolio", "VirtualFund"]
)
account = Account.upsert(
unique_identifier="acct-main",
account_name="Main Account",
)
calendar = Calendar.create_from_pandas_calendar(
source_identifier="24/7",
unique_identifier="CRYPTO_24_7",
display_name="Crypto 24/7",
valid_from="2026-05-25",
valid_to="2026-05-25",
timezone="UTC",
)
portfolio = Portfolio.upsert(
unique_identifier="btc-eth-target",
calendar_uid=calendar.uid,
)
virtual_fund = VirtualFund.upsert(
unique_identifier="vf-core",
account_uid=account.uid,
target_portfolio_uid=portfolio.uid,
)
Account holdings workflow
Use this workflow when publishing and inspecting account positions:
- Before runtime, run the admin migration flow with
mainsequence migrations upgrade --provider migrations:migration headso the package schema is finalized. - Attach account holdings and target positions through
msm.start_engine(...). When target positions can reference portfolios, includePortfolioandTargetPositionsStoragein the coremsmmodel list. - Create or upsert the account allocation model and account group, then create
the account with
account_group_uid. - Create the
AccountTargetAllocationrelation for the account and allocation model, then create a UTCPositionSetsnapshot under that relation. - Build target-position rows with
msm.services.build_target_positions_frame(...)usingposition_set.uidasposition_set_uid, and useasset_uidfor direct asset targets orportfolio_uidfor portfolio sleeve targets. - Build holdings rows with
build_account_holdings_frame(...)and attach the real combined frame toAccountHoldingswithset_frame(...). For a single account,set_account_holdings_frame(...)is the convenience path. - Run the node and unpack the SDK result:
error_on_last_update, holdings_frame = holdings_node.run(...). - Pass only
holdings_frametoAccount.pretty_print_positions(...).
Holdings and target positions
from msm.api.accounts import (
AccountAllocationModel,
AccountHoldingsSet,
AccountTargetAllocation,
PositionSet,
)
from msm.api.assets import Asset
from msm.api.portfolios import Portfolio
from msm.services import build_account_holdings_frame
from msm.services import build_target_positions_frame
holdings_set = AccountHoldingsSet.upsert(
account_uid=account.uid,
time_index="2026-05-25T00:00:00Z",
)
holdings = build_account_holdings_frame(
holdings_date="2026-05-25T00:00:00Z",
account_uid=account.uid,
holdings_set_uid=holdings_set.uid,
positions=[
{"asset_identifier": "BTC", "quantity": 1.0, "direction": 1},
{"asset_identifier": "ETH", "quantity": 10.0, "direction": -1},
],
)
allocation_model = AccountAllocationModel.upsert(
allocation_model_name="balanced-allocation-model"
)
account_target_allocation = AccountTargetAllocation.upsert(
unique_identifier="account-main-balanced-target",
account_uid=account.uid,
account_allocation_model_uid=allocation_model.uid,
)
position_set = PositionSet.upsert(
account_target_allocation_uid=account_target_allocation.uid,
position_set_time="2026-05-25T00:00:00Z",
)
btc_asset = Asset.upsert(unique_identifier="BTC", asset_type="crypto")
portfolio_sleeve = Portfolio.upsert(
unique_identifier="account-main-sleeve",
calendar_uid=calendar.uid,
)
targets = build_target_positions_frame(
target_positions_date="2026-05-25T00:00:00Z",
position_set_uid=position_set.uid,
positions=[
{"asset_uid": btc_asset.uid, "weight_notional_exposure": 0.6},
{"portfolio_uid": portfolio_sleeve.uid, "weight_notional_exposure": 0.4},
],
)
Account, target-position, and virtual-fund response helpers enrich asset rows through the shared asset reference service. Current asset snapshot labels are resolved as backend latest rows, so response construction does not scan asset snapshot history in Python.
The DataNode frame helpers validate the dynamic-table contract locally. The actual table provisioning and writes remain generic TDAG/DataNode behavior.
Virtual-fund allocation
Virtual-fund allocation is a separate policy workflow. Start from the
PositionSet.uid, pass valuation_time, valuation_asset_uid,
holdings_selection_policy, valuation_resolver, and allocation_policy,
inspect the dry-run AccountVirtualFundAllocationPlan, and only then call
apply_account_virtual_fund_allocation_plan(...). The full account workflow
supports this as an extension: run
examples/msm/accounts/account_portfolio_full_workflow.py --with-virtual-fund-allocation
for dry-run planning, or add --apply-virtual-fund-allocation to publish the
virtual-fund holdings after the plan is printed.
See examples/msm/accounts/account_portfolio_full_workflow.py for the full
account plus portfolio path. The default runner prepares only the contributed
interpolated-price output storage revision needed by the portfolio example,
upgrades it, chains
examples/msm_portfolios/portfolio_equal_weights_example.py to create a
reusable portfolio sleeve, assigns that sleeve to an example portfolio group,
then creates the account group, two accounts, canonical asset snapshots with
ticker/name metadata, one shared account allocation model, account-owned target
allocation relationships, direct asset plus portfolio PositionSet target-row
publication, holdings publication, and pretty-printed account positions. Use
--skip-schema-prep only when that
contributed interpolated-price output table has already been migrated.
Next → Portfolios