Stock Trading Architecture
Backpack treats stocks as tokenized real-world assets: actual shares are held in custody by a TradFi broker (Atomic Vaults), and each share is represented 1:1 as a Solana SPL token on-chain. This lets stock positions participate in the same cross-asset margin engine as crypto without requiring a separate settlement rail.
Asset Representation
Stocks exist in two database layers:
tokens— the on-chain token (same table as crypto assets)token_external_symbol— maps each stock to its external identifiers:cusip— for regulatory reportingpyth_symbol/dxfeed_symbol— real-time price feed sourcesatomic_vaults_symbol— identifier used with the custody/settlement broker
A CUSIP is stored per-token, which is necessary for broker-dealer compliance. Crypto tokens have no equivalent field.
Order Routing
Stocks currently trade exclusively via RFQ (request-for-quote). There is no stock order book yet — MarketScope::Stock exists in the codebase and is separated from MarketScope::Spot at the jurisdiction policy layer, but order-book routing for equities is a planned addition.
Access is gated by a per-user is_stocks_enabled feature flag. The @stocks symbol group automatically expands to all entries in token_external_symbol, enabling bulk jurisdiction policy rules without enumerating every ticker.
RFQ Flow
The stock broker is an independent service that manages the lifecycle of each RFQ and bridges Backpack's engine to Atomic Vaults.
State machine
Received → QuoteSubmitted → AcceptedBinding → ReadyToSettle → Filled
↘ WithdrawingToken → Filled (sell side)Step-by-step
User submits RFQ — e.g., "buy 5 AAPL.US". Engine validates, locks USDC, opens a 60-second submission window.
Stock broker receives
RfqEvent::New— validates market session is open (US equities: 9:30–16:00 ET regular hours, plus pre/post/overnight sessions), checks quantity limits, fetches real-time mid price from Pyth Lazer or dxFeed.Quote calculation:
bid = broker_bid × (1 − margin_bps) × (1 − aggression_bps) ask = broker_ask × (1 + margin_bps) × (1 + aggression_bps)Default
margin_bpsis 20 (0.20%).aggression_bpsbiases the price toward faster fills. Both are runtime-configurable per-session.Stock broker submits two-sided quote to the engine via
ExchangeCommand::SubmitQuote.User accepts — engine locks payment, notifies stock broker via
RfqEvent::AcceptedBinding.Stock broker places order at Atomic Vaults — a limit order with
external_id = "rfq-{rfq_id}-001"for idempotent retry. Price is set atquoted_price / (1 ± margin), clamped to quoted price so the user never gets a worse fill than quoted.Atomic Vaults fills the order against a real stock exchange. Fill events stream back over WebSocket.
Post-fill (buy side): stock broker fires
CustodyCommand::MintStockTokens. The custody service mints the corresponding SPL token on Solana — this happens concurrently with settlement, not blocking it.Post-fill (sell side): stock broker fires
WithdrawTokento pull shares back from the bridge account. RFQ entersWithdrawingTokenstate until withdrawal confirms.Stock broker verifies its own balances, then fires
ExchangeCommand::SettleQuotewith optionalsettle_price/settle_quantityfor price improvement if the broker got a better fill than quoted.Engine settles — user receives AAPL tokens (buy) or USDC (sell), locked funds are released. RFQ reaches terminal
Filledstate.
Broker order management
- Orders are polled every 5 seconds; stale orders are queried or cancelled.
- Hard timeout: 30 seconds before cancel.
- Stock broker can reload all state from a Redis event stream on restart and reconciles any non-terminal orders with Atomic Vaults.
Settlement
Settlement is always deferred — unlike crypto spot trades which settle atomically, stocks require the broker fill to complete before funds move.
Key mechanics:
- Payment is locked (moved to
lockedbalance) on quote acceptance, not transferred. SettleQuotecarries optionalsettle_priceandsettle_quantity, allowing the broker to pass through price improvement.- If the broker gets a partial fill,
settle_quantityreflects only the filled portion.
Post-trade equity reconciliation
After settlement the engine may have residual unsettled_equity — the gap between realized P&L and available balance. The handle_settle_equity command resolves this:
- User owes exchange: drain available USDC → redeem USDC lends → create a USDC borrow (bounded by MMF, not IMF)
- Exchange owes user: draw from the liquidity fund up to
min(unsettled_equity, liquidity_fund.available) - An optional
collateral_conversionparameter can force-sell non-USDC collateral first before creating a borrow.
Stocks as Collateral
The collateral system is asset-agnostic. AAPL tokens contribute to margin the same way BTC does:
collateral_value = balance × mark_price × haircut_function(notional)Haircut functions use inverse-square-root curves — larger positions receive a steeper haircut to penalize concentration. IMF (initial margin fraction) controls entry; MMF (maintenance margin fraction) triggers liquidation.
Cross-asset margin means stock holdings can offset futures margin requirements. The liquidator's collateral reconciler handles undercollateralized accounts by converting stock tokens (and other non-USDC collateral) to USDC via spot orders or RFQ, in order of MMF priority.
Market Data
Stocks draw from multiple price sources:
- Pyth Lazer — low-latency on-chain oracle (primary for quoting)
- dxFeed — traditional equities market data provider (fallback)
- Atomic Vaults — custody system, also provides fill prices
There is no explicit NBBO aggregation wired up. Instead, the stock broker applies a local spread markup and places limit orders at Atomic Vaults, letting the broker route to the best available price. Explicit NBBO monitoring is a future enhancement.
Market Hours
Stock trading is session-gated. Pre-configured sessions:
| Session | Hours (ET) | Days |
|---|---|---|
| Pre-market | 04:00–09:30 | Mon–Fri |
| Regular | 09:30–16:00 | Mon–Fri |
| Post-market | 16:00–20:00 | Mon–Fri |
| Overnight | 20:00–04:00 | Sun–Thu |
An external_market_holidays table enforces full and partial-day closures (e.g., early closes). Per-token session overrides and per-session quantity limits (min/max/step_size) are supported.
Compliance & Jurisdiction
PolicyScope::Stockis enforced separately fromPolicyScope::Spot— equities get their own jurisdiction policy rules.- CUSIP codes are stored per-token for regulatory reporting.
- Entity records track LEI numbers, FATCA reportability, and client categorization (Retail / Professional / Eligible Counterparty) — required for broker-dealer operations.
Key Components
| Component | Role |
|---|---|
stock-broker/src/core/processor.rs | RFQ state machine; manages Atomic Vaults orders |
stock-broker/src/core/state.rs | RfqStatus enum |
engine/src/engine/rfq/mod.rs | Engine-side RFQ handling, quote acceptance, locking |
engine/src/engine/settle_equity/commands.rs | Post-trade equity reconciliation |
engine/src/clearing_house/settlement/rfq.rs | RFQ settlement logic |
store/src/token_external_symbol.rs | Stock-to-external-market mapping |
store/src/external_market_sessions.rs | Market hours configuration |
liquidator/src/tasks/collateral_reconciler.rs | Collateral conversion and liquidation |