FastAPI v1
The local apps/v1 FastAPI app exposes the migrated public asset registry
surface for this repository.
Scope
This API is intentionally thin:
- route declarations, validation, and OpenAPI metadata live under
apps/v1 - reusable asset category workflows live under
src/msm/services - asset, category, and index frontend route composition is backed by
src/msm/services/asset_master_lists.py - pricing curve registry, market-data set, and binding workflows are backed by
msm_pricing.api - portfolio detail and latest-weight workflows are backed by
src/msm_portfolios/services - virtual-fund identity and holdings snapshot workflows are backed by
src/msm/services/accounts/virtual_funds_public_api.py
Route ADRs
- Settings Route: read-only app settings and runtime assumptions for frontend clients.
- Calendar CRUD And Summary Route: route group for calendar identity CRUD, summary, and bounded date, session, and event maintenance.
- Pricing Market Data Routes: route group for pricing market-data set and concept binding management.
- Fixed Income Pricer Routes: route group for method-backed bond pricing operations over assets with current pricing details.
- Pricing Curve Routes: route group for pricing curve registry lists.
- Fixed Income Pricer API: registry-driven pricing workbench API for price, analytics, duration, yield, z-spread, cashflows, carry/roll-down, curve preview, and fixings availability.
- Command Center Adapter Discovery:
additive Adapter from API discovery contract for Command Center without
breaking existing
/api/v1clients. - Portfolio Routes: route group for portfolio identity, detail-page composition, latest weights, and delete operations.
- Virtual Fund Routes: route group for account-owned virtual-fund identity and holdings snapshots.
Runtime Bootstrap
When MSM_AUTO_REGISTER_NAMESPACE is set for local development, apps/v1
now performs startup-time runtime attachment instead of waiting for the first
request to hit a row operation.
Current local-dev behavior:
- the app calls
msm_portfolios.start_engine(...)during startup for theapps/v1table set because this surface includes portfolio-backed account target-position routes - the startup table set includes portfolio-backed target-position tables, so target-position routes resolve against the existing shared markets runtime instead of starting a second portfolio runtime on first request
- the startup table set includes
PortfolioMetadataandPortfolioWeightsStorageso portfolio detail and latest-weights routes use the same shared markets runtime - the startup table set includes
VirtualFund,VirtualFundHoldingsSet, andVirtualFundHoldingsStorageso virtual-fund routes attach to the shared markets runtime - the app calls
msm_pricing.bootstrap.attach_pricing_schemas(...)during startup for the pricing rows used by asset pricing details, curve registry lists, and pricing market-data management - schema mutation must already have been handled by
mainsequence migrations upgrade --provider migrations:migration head - the app uses the real project/session data source already configured for the Main Sequence client session
- if the session cannot resolve a valid DynamicTable data source, startup should fail instead of redirecting writes into an ad hoc local store
Implemented Routes
Command Center Adapter Discovery
GET /health- returns a zero-argument health payload for API discovery
- response is
{ status, service, version } - does not touch MetaTables, pricing runtime data paths, or request identity
GET /.well-known/command-center/connection-contract- returns the Adapter from API discovery contract for the existing
apps/v1FastAPI operations - references
/openapi.json - lists every public
/api/v1/*operation by its existingoperationId - classifies read/calculation operations as
query - classifies create/update/delete/write operations as
mutation - disables cache metadata for mutation operations and non-GET calculations
- keeps provider-native responses provider-native and exposes optional
responseMappingsonly as metadata
Settings
GET /api/v1/settings/- returns read-only public settings and runtime assumptions for frontend clients
- includes app metadata, runtime namespace, docs URLs, and assumption rows
- does not expose request identity, access tokens, refresh tokens, or other secrets
Accounts
GET /api/v1/account/- supports
search,limit, andoffset - returns
{ count, results } resultscontains the librarymsm.api.accounts.Accountcontract:uid,unique_identifier,account_name,is_paper,account_is_active,holdings_data_node_uid, andmetadata_jsonGET /api/v1/account/{uid}/summary/- returns the reusable
FrontEndDetailSummaryresponse for account detail pages - resolves the account by
uid GET /api/v1/account/target-allocation/targets/- supports
search,target_type=all|asset|portfolio,limit, andoffset - returns one paginated candidate list for target-position assignment
- searches valid
TargetPositionsStoragetargets acrossAssetTableandPortfolioTable - backed by one compiled MetaTable
selectoperation usingUNION ALLrather than separate asset and portfolio searches - asset candidates include latest
AssetSnapshotsStoragename/ticker labels when present - each result contains
target_type,target_uid,asset_uid, andportfolio_uid, so the selected row can be written directly into a target position payload GET /api/v1/account/{account_uid}/holdings/- supports
order,limit=1,include_asset_detail, and exactholdings_date - returns one holdings snapshot backed by
AccountHoldingsStorage - each holding exposes the storage
asset_identifier, positivequantity,direction(1long,-1short), and computedsigned_quantity - returns 200 with an empty
holdingslist when the account exists but no holdings snapshot matches - snapshot-level fields are
holdings_set_uid,holdings_date, andholdings GET /api/v1/account/{account_uid}/holdings/by-fund/- supports
order,limit=1,include_asset_detail, and exactholdings_date - selects one
AccountHoldingsSetTablesource snapshot for the account - groups persisted
VirtualFundHoldingsStorageallocation rows byVirtualFundTable.uidwhere eachVirtualFundHoldingsSetTablereferences the selected source account holdings set - returns
account_uid,source_account_holdings_set_uid,holdings_date,funds,residuals, andallocation_warnings - each fund contains
virtual_fund_uid,virtual_fund_unique_identifier,target_portfolio_uid,holdings_set_uid, and grouped holdings rows - grouped holding rows expose the storage
asset_identifier, positive allocatedquantity, first-classallocation_strategy,direction, computedsigned_quantity, andallocationmetadata parsed fromextra_details allocationcontainstarget_gap_signed_quantity,scale,target_row_key, andposition_set_uidwhen those fields were persisted by the virtual-fund allocation apply stepresidualsare derived as source account signed quantity minus total virtual-fund allocated signed quantity per asset- asset labels use latest
AssetSnapshotsStoragerows; OpenFIGI and numeric asset IDs are not used - this read endpoint does not rerun or apply the allocation planner
POST /api/v1/account/{account_uid}/add-holdings/- writes one account holdings snapshot and returns the same
AccountHoldingsSnapshotResponsecontract as the holdings read endpoint - request body contains
holdings_date,overwrite, andpositions - each position uses the storage field name
asset_identifier asset_uid, when provided, is validation only and must match the asset row for the suppliedasset_identifierquantityis stored as a positive magnitude anddirectionstores sidetarget_trade_time, when provided, must matchholdings_dateoverwrite=falserejects an existing snapshot;overwrite=truereplaces rows for the holdings set through one scoped MetaTable operation so the delete and replacement insert share the same backend transaction boundaryPOST /api/v1/account/{account_uid}/add-target-positions/- writes one account target-position snapshot and returns the same
AccountTargetPositionsSnapshotResponsecontract as the target-positions read endpoint - request body contains only
target_positions_date,overwrite, andpositions; account and target-allocation parent identity are derived from the account uid in the path - backend derives a deterministic account allocation model and
account-target-allocation row from the account uid; the frontend does not
send
account_allocation_model_uid, target-allocation uid, display name, or parent metadata - each position uses
target_type,target_uid, and exactly one concrete target reference:asset_uidfor asset rows orportfolio_uidfor portfolio rows - each position must provide exactly one of
weight_notional_exposure,constant_notional_exposure, orsingle_asset_quantity single_asset_quantityis valid only for direct asset target rows; portfolio target rows must useweight_notional_exposureorconstant_notional_exposureoverwrite=falserejects an existing position set at the same timestamp;overwrite=truereplaces rows through one scoped MetaTable operation so the parent upserts, delete, and replacement insert share the same backend transaction boundaryGET /api/v1/account/{account_uid}/target-positions/- supports
order,limit=1,include_asset_detail, and exacttarget_positions_date - resolves active account target allocations, selects one
PositionSetTablesnapshot, and returns itsTargetPositionsStorageexposure rows - each position carries
target_type,target_uid, and exactly one concrete target reference:asset_uidorportfolio_uid - returns 200 with an empty
positionslist when the account exists but no target-position snapshot matches - asset details include
uid,unique_identifier, and latestAssetSnapshotsStoragename/ticker; no OpenFIGI or numeric asset id fields are returned - portfolio details include
uid,unique_identifier, and optionalportfolio_index_uid; portfolio targets are mandate exposure, not custody holdings
Assets
GET /api/v1/asset/- supports
response_format=frontend_list - supports
search,limit,offset - supports
categories__uidfor nested category asset tables - returns the library
msm.api.assets.Assetcontract:uid,unique_identifier, andasset_type GET /api/v1/asset/{uid}/- supports
response_format=frontend_detail - resolves the asset by
uid - returns
AssetDetailResponsewith top-leveluid,unique_identifier,asset_type, andcurrent_snapshot current_snapshotis the latestAssetSnapshotsStoragerow for the assetunique_identifier/ snapshotasset_identifier- does not use numeric asset IDs for asset identity
GET /api/v1/asset/{uid}/summary/- returns a reusable
FrontEndDetailSummaryresponse for detail-page summary cards - resolves the asset by
uid - includes asset identity, badges, inline fields, highlight fields, label management placeholders, and page-specific extensions
GET /api/v1/asset/{uid}/get_pricing_details/- returns the current pricing details row for one asset
- response mirrors
msm_pricing.api.AssetCurrentPricingDetails - reads
AssetCurrentPricingDetailsTable, not the timestampedAssetPricingDetailDataNode - returns 404 when no current pricing details row exists for the asset
DELETE /api/v1/asset/{uid}/- deletes one asset identity row by
uid - returns 200 with
nullon success - returns 404 when the asset
uiddoes not exist - related rows are governed by backend table constraints
Asset Categories
GET /api/v1/asset-category/- supports
response_format=frontend_list - returns the library
msm.api.assets.AssetCategorycontract GET /api/v1/asset-category/{uid}/- supports
response_format=frontend_detail - returns one library
msm.api.assets.AssetCategoryrow POST /api/v1/asset-category/- creates a category
- derives
unique_identifierfromdisplay_namewhen omitted - replaces memberships when
assetsare supplied - returns the created library
msm.api.assets.AssetCategoryrow PATCH /api/v1/asset-category/{uid}/- updates category metadata
- replaces memberships when
assetsare supplied - returns the updated library
msm.api.assets.AssetCategoryrow DELETE /api/v1/asset-category/{uid}/- deletes a single category
- returns
nullon success POST /api/v1/asset-category/bulk-delete/- deletes by explicit
uids - also supports compatibility filters with
select_all=true
Indexes
GET /api/v1/index/- supports
response_format=frontend_list - supports
search,limit, andoffset - returns the library
msm.api.indices.Indexcontract GET /api/v1/index/{uid}/- returns one index registry record by
uid - always includes
index_typeand includesmetadata_jsonwhen present on the underlying row DELETE /api/v1/index/{uid}/- deletes one index registry record
- returns
nullon success
Portfolios
GET /api/v1/portfolio/- supports
response_format=frontend_list - supports
search,calendar_uid,calendar_name,limit, andoffset - returns
PaginatedResponse[Portfolio]using the librarymsm.api.portfolios.Portfoliocontract GET /api/v1/portfolio/{uid}/- returns a composed portfolio detail payload containing the core portfolio
row, optional
PortfolioMetadata, latest-weights tab link, and route links - missing metadata does not make the route return 404
GET /api/v1/portfolio/{uid}/summary/- returns the reusable
FrontEndDetailSummaryresponse for portfolio detail pages - uses the portfolio
uidstring asentity.id GET /api/v1/portfolio/{uid}/weights/- supports
order,limit=1,include_asset_detail, and exactweights_date - resolves weights through
Portfolio.portfolio_index_uid -> Index.unique_identifier - returns one snapshot backed by
PortfolioWeightsStorage - returns 200 with an empty
weightslist when the portfolio exists but no matching weights snapshot exists - asset labels use latest
AssetSnapshotsStoragerows; OpenFIGI is not used DELETE /api/v1/portfolio/{uid}/- deletes one portfolio identity row
- returns 409 when protected rows, such as account target-position history, still reference the portfolio
- does not delete historical portfolio weights or values
POST /api/v1/portfolio/bulk-delete/- deletes multiple portfolio identity rows by explicit
uids - reports protected or missing rows in
failed
Virtual Funds
GET /api/v1/virtualfund/- supports
response_format=frontend_list - supports
search,account_uid,portfolio_uid,limit, andoffset portfolio_uidfiltersVirtualFund.target_portfolio_uid- returns
PaginatedResponse[VirtualFund]using the librarymsm.api.virtual_funds.VirtualFundcontract GET /api/v1/virtualfund/{uid}/- returns a composed virtual-fund detail payload containing the core virtual-fund row, latest-holdings tab link, and route links
GET /api/v1/virtualfund/{uid}/summary/- returns the reusable
FrontEndDetailSummaryresponse for virtual-fund detail pages - uses the virtual-fund
uidstring asentity.id GET /api/v1/virtualfund/{uid}/holdings/- supports
order,limit=1,include_asset_detail, and exactholdings_date - returns one snapshot backed by
VirtualFundHoldingsSetTableandVirtualFundHoldingsStorage - returns 200 with an empty
holdingslist when the virtual fund exists but no matching holdings snapshot exists - holdings rows expose the storage
asset_identifier, positivequantity, first-classallocation_strategy,direction, and computedsigned_quantity - asset labels use latest
AssetSnapshotsStoragerows; OpenFIGI is not used
Calendars
GET /api/v1/calendar/- supports
response_format=frontend_list - supports
search,limit, andoffset - supports exact filters for
unique_identifier,calendar_type,source, andsource_identifier - supports
unique_identifier_contains - returns the library
msm.api.calendars.Calendarcontract POST /api/v1/calendar/- creates one calendar identity row
- request body uses the library
CalendarCreatecontract - returns the created
Calendarrow GET /api/v1/calendar/{uid}/- supports
response_format=frontend_detail - returns one library
Calendarrow by uid GET /api/v1/calendar/{uid}/summary/- returns the reusable
FrontEndDetailSummaryresponse for calendar detail pages - resolves the calendar by
uid - includes calendar identity, type/timezone badges, validity horizon, label management placeholders, and related date/session/event route links
PATCH /api/v1/calendar/{uid}/- updates mutable calendar identity fields
- request body uses the library
CalendarUpdatecontract - returns the updated
Calendarrow DELETE /api/v1/calendar/{uid}/- deletes one calendar identity row
- returns
nullon success - related date, session, and event rows are removed by database cascade
GET /api/v1/calendar/{calendar_uid}/dates/- lists
CalendarDaterows for one calendar - supports
start_date,end_date, flag filters,limit, andoffset POST /api/v1/calendar/{calendar_uid}/dates/- creates one date row under the path calendar uid
POST /api/v1/calendar/{calendar_uid}/dates/bulk-upsert/- bulk upserts date rows under the path calendar uid
GET,PATCH, andDELETE /api/v1/calendar/{calendar_uid}/dates/{date_uid}/- manage one date row and require it to belong to the path calendar uid
GET /api/v1/calendar/{calendar_uid}/sessions/- lists
CalendarSessionrows for one calendar - supports
start_date,end_date,session_label,is_primary,limit, andoffset POST /api/v1/calendar/{calendar_uid}/sessions/- creates one session row under the path calendar uid
POST /api/v1/calendar/{calendar_uid}/sessions/bulk-upsert/- bulk upserts session rows under the path calendar uid
GET,PATCH, andDELETE /api/v1/calendar/{calendar_uid}/sessions/{session_uid}/- manage one session row and require it to belong to the path calendar uid
GET /api/v1/calendar/{calendar_uid}/events/- lists
CalendarEventrows for one calendar - supports
start_date,end_date,event_type,event_label,target_type,target_uid,target_identifier,limit, andoffset POST /api/v1/calendar/{calendar_uid}/events/- creates one event row under the path calendar uid
POST /api/v1/calendar/{calendar_uid}/events/bulk-upsert/- bulk upserts event rows under the path calendar uid
GET,PATCH, andDELETE /api/v1/calendar/{calendar_uid}/events/{event_uid}/- manage one event row and require it to belong to the path calendar uid
Pricing Market Data
GET /api/v1/pricing/curves/- lists pricing curve registry rows from
msm_pricing.api.Curve - supports
limit,offset,search,curve_type,index_uid, andsource - returns
PaginatedResponse[Curve] - does not return timestamped
DiscountCurvesStorageobservations GET /api/v1/pricing/curves/{uid}/summary/- returns the reusable
FrontEndDetailSummarypayload for one pricing curve registry row - resolves the curve by
uid - includes curve identity, type, index uid, interpolation method, compounding, source, and metadata extensions
- does not return timestamped
DiscountCurvesStorageobservations GET /api/v1/pricing/curves/{uid}/discount-curve/- reads discount-curve observations for one curve using
MSDataInterface - requires
market_data_set, accepting either a pricing market-data set uid or set key - accepts optional
valuation_date; when omitted, returns the latest available observation - resolves the
discount_curvesbinding for the selected market-data set and passes itsdata_node_uidintoMSDataInterface -
returns curve nodes, effective date, selected set, and binding metadata
-
GET /api/v1/pricing/market_data/ - returns the discoverability card for pricing market-data set and binding operations
GET /api/v1/pricing/market_data/sets/- supports
limit,offset,status, andset_key - returns
PaginatedResponse[PricingMarketDataSet] - uses the Django REST Framework-style
{ count, next, previous, results }envelope GET /api/v1/pricing/market_data/sets/{uid}/- returns one
PricingMarketDataSetby uid GET /api/v1/pricing/market_data/sets/by-key/{set_key}/- returns one
PricingMarketDataSetby set key POST /api/v1/pricing/market_data/sets/- creates one set with
PricingMarketDataSetCreate - returns
PricingMarketDataSet POST /api/v1/pricing/market_data/sets/upsert/- upserts one set with
PricingMarketDataSetUpsert - returns
PricingMarketDataSet PATCH /api/v1/pricing/market_data/sets/{uid}/- updates one set with
PricingMarketDataSetUpdate - returns
PricingMarketDataSet DELETE /api/v1/pricing/market_data/sets/{uid}/- deletes one set through
PricingMarketDataSet.delete(uid) - returns
{ detail, uid, deleted_count } GET /api/v1/pricing/market_data/bindings/- supports
limit,offset,market_data_set_uid, andconcept_key - returns
PaginatedResponse[PricingMarketDataSetBinding] GET /api/v1/pricing/market_data/sets/{market_data_set_uid}/bindings/- lists bindings owned by one pricing market-data set
- returns
PaginatedResponse[PricingMarketDataSetBinding] GET /api/v1/pricing/market_data/bindings/{uid}/- returns one
PricingMarketDataSetBindingby uid GET /api/v1/pricing/market_data/bindings/resolve/- supports
market_data_setand requiredconcept_key - returns
{ market_data_set, concept_key, data_node_uid } POST /api/v1/pricing/market_data/bindings/- creates one binding with
PricingMarketDataSetBindingCreate - returns
PricingMarketDataSetBinding POST /api/v1/pricing/market_data/bindings/upsert/- upserts one binding with
PricingMarketDataSetBindingUpsert - returns
PricingMarketDataSetBinding PATCH /api/v1/pricing/market_data/bindings/{uid}/- updates one binding with
PricingMarketDataSetBindingUpdate - returns
PricingMarketDataSetBinding DELETE /api/v1/pricing/market_data/bindings/{uid}/- deletes one binding through
PricingMarketDataSetBinding.delete(uid) - returns
{ detail, uid, deleted_count }
Compatibility Notes
The response_format=frontend_list and response_format=frontend_detail
query parameters are still accepted on migrated legacy routes, but list and
direct detail rows now prefer core library API models over frontend projections.
The nested category asset table should use GET /api/v1/asset/ with
categories__uid. The dedicated POST /api/v1/asset/query/ route is still
future work for this local API.
Validation
The focused FastAPI coverage for this surface lives under:
tests/msm/fastapi/v1/
Use /openapi.json, /docs, and /redoc from the local app for contract
inspection.