ADR 0013 — MBT: Raxx-native paper-trading engine, displacing per-user Alpaca OAuth for paper
Status: Deferred (2026-05-29)
Date: 2026-04-22
Deciders: product owner (user), software-architect
Related: ADR 0002 (no stored credentials), ADR 0003 (GDPR by default), ADR 0014 (Alpaca scope reframe), docs/architecture/mbt-paper-trading-engine.md, docs/architecture/multi-tenant-alpaca.md
Supersedes: ADR 0008, ADR 0009, ADR 0010, ADR 0011 (original multi-tenant Alpaca design, PR #184)
Parent epic: #183
Status update — 2026-05-29
Current paper-trading path does not use a Raxx-native engine.
paper_order_service.py routes all paper trades to the Alpaca paper endpoint.
The MBT engine described in this ADR was never built.
Why deferred: The 2026-05-23 launch posture is operator-testing-ready, not public launch. At operator-testing scale, building a full paper-trading simulator in parallel with live-trading infrastructure would represent significant dual implementation cost with no customer-facing return. The Alpaca paper endpoint provides sufficient fidelity for the current operator-testing window.
Re-evaluation triggers — any one of these reopens this ADR:
- Broker paper endpoint fidelity issues emerge in production (incorrect fills, missing corporate actions, options expiration failures).
- Launch posture changes from personal-use-only to real customers or multi-broker BYOB — at that point per-user Alpaca paper complexity and data sovereignty concerns return.
- Regulatory pressure on routing paper-trade volume through a real-broker endpoint (e.g. counsel flags that Alpaca paper usage has reporting obligations we did not anticipate).
Current state of the invariants this ADR was designed to satisfy:
-
Invariant #1 (no stored credentials): the live-trading path requires a stored broker credential per ADR-0049. Paper trading currently uses a shared server-side Alpaca credential, not a per-user stored credential. The original motivation for MBT (eliminate per-user paper OAuth tokens) is moot at current scale. ADR-0009 exception scope has not widened.
-
Paper-first gating: still enforced via the graduation gate (PR #3021). The gate reads paper-trade performance from the Alpaca paper endpoint history, not from an MBT database. Same semantic, different data source.
This ADR remains in the record. The motivation, design, and alternatives documented below are correct and should be used as the starting point when any re-evaluation trigger fires.
Context
The original multi-tenant design (PR #184, superseded ADRs 0008-0011) routed every user through Alpaca OAuth for paper and live trading. This over-depended on Alpaca:
- Every user — including free users who may never make a real trade — had to complete OAuth authorization with Alpaca before paper-trading.
- Raxx stored an encrypted Alpaca OAuth token for every user, creating a documented exception to invariant #1 (no stored credentials) that applied across the entire user base.
- Paper-trade history was governed by Alpaca's retention policy, not ours. Tier-gated retention (Free 90d / Pro 3yr / Pro+ unlimited per pricing matrix) was hard to implement cleanly on top of Alpaca's opaque storage.
- Alpaca's paper trading is itself a simulation against their market data. We can run the same simulation ourselves.
Realization: Alpaca's paper trading is a simulator. We can be that simulator. Doing so eliminates per-user OAuth complexity for paper (vast majority of users), restores the "no stored credentials" invariant for that majority, makes tier retention trivial (our DB), gives us data sovereignty, and keeps Raxx out of Broker-API / BD territory forever. We become a research + paper + broker-handoff platform, not a broker.
Decision
Raxx builds MBT — a native paper-trading engine — and uses it as the default execution surface for all users. Alpaca is scoped to (a) one shared server-side market-data account, and (b) optional per-user live-broker handoff for Pro+ users who graduate from paper (see ADR 0014).
Scope
- MBT owns accounts, positions, orders, fills, activities, and end-of-day snapshots for every user.
- v1 feature parity target: market/limit/stop/stop-limit orders; single-leg + common multi-leg options (iron condor, credit spread, strangle, butterfly); fractional shares; cash + basic margin; cash dividends; option expirations; bracket orders.
- v2+: trailing stops, PDT detection, full corporate actions (splits/mergers/symbol changes), DRIP, extended hours, partial fills, short-borrow mechanics.
- Market data: one Alpaca Market Data account, server-side; tier-gated delivery (Free delayed, Pro real-time, Pro+ real-time + archive).
- Async jobs: Celery + Redis for v1 (Temporal deferred to v2 for stateful long-running workflows).
- Sessions: REST
/api/sessions/*, JWT + HTTP-only cookie, server-side revocation, per-tier rate limits (60/600/3000 per minute per pricing.md). Detailed indocs/architecture/session-engine.md.
Out of scope (intentional)
- Alpaca paper-trading for any tier after GA.
- Per-user Alpaca OAuth for paper or for market data.
- Raxx as broker / clearing agent (Broker API path permanently dropped).
- MBT user-to-user order matching (single-book-per-user, real-market-data-informed fills).
Migration
Dark → shadow (double-write MBT alongside Alpaca paper for parity diff) → flagged per-user cutover → GA (Alpaca paper writes disabled). Detailed in mbt-paper-trading-engine.md §10.
Consequences
Positive
- Invariant #1 restored for free + most Pro users. No broker token to store. The ADR 0009 exception narrows to the Pro+ live-handoff subset (re-scoped as ADR 0014).
- Tier-gated retention is trivial. Free 90d / Pro 3yr / Pro+ unlimited is a purge job on our DB, not a dance with Alpaca's policies.
- Data sovereignty. Alpaca cannot change paper policy and break our product overnight. Our simulator, our rules, our retention.
- Regulatory posture cleaner. MBT stays firmly in the user-directed-simulation lane; no BD/Broker-API exposure; Advisers Act flags only arise if we publish individualized strategies (same as before, surfaced for counsel).
- Simpler onboarding. A free user can paper-trade within seconds of account creation; no third-party OAuth flow gates the first-run experience.
- Deterministic replay — every fill records the NBBO snapshot that produced it. Auditable "why did this fill at $X" forever.
Negative
- We build the simulator. Order-matching logic, corporate-actions handling, options expiration, margin math — these are real engineering, not library installs. Multi-month effort.
- Fill realism is our responsibility. Users will compare MBT fills to Alpaca's paper and to real fills. We need to be defensibly close and honest where we differ ("simulated paper trade with realistic but not perfect fills").
- Options complexity. Multi-leg fills, margin calculations for spreads, and expiration handling are the largest implementation surface. v1 scope trims some edge cases (portfolio margin, calendar spreads, partial fills) deliberately.
- Migration cost. Existing users on the shared Alpaca paper account need state imported into MBT. One-shot importer + shadow period mitigate.
- We still depend on Alpaca for market data and for the minority live-handoff path. Narrower surface, but not zero.
Neutral
- Celery + Redis over Temporal is a v1 choice, not a forever choice. Temporal's right answer for stateful long-running workflows arrives in v2 when MBT grows them.
- Single Alpaca data account suffices for v1 user counts; requires re-evaluation (second key, entitlement upgrade) at scale. Not a blocker for GA.
Alternatives considered
Stay with the PR #184 plan (per-user Alpaca OAuth for paper)
Rejected. The reasons to change are the positive consequences above: invariant compliance, tier retention, data sovereignty, onboarding friction. The reasons to stay were mostly "less engineering for us" — real, but outweighed.
MBT for paper, but route all live via our own broker/clearing partnership (Broker API)
Rejected and permanently off-roadmap. BD status + FINRA-regulated clearing is a regulatory lift Raxx does not want. Live routes stay user-to-Alpaca OAuth via the live-handoff pattern (ADR 0014). Pro tier adds a second broker (per pricing matrix — likely IBKR or tastytrade) via the same handoff pattern.
Use a third-party simulator library / service
Considered, rejected. No mature off-the-shelf options simulator exists that covers our scope (multi-leg options, corporate actions, tier retention). Commercial options (e.g. QuantConnect-as-backend) cede too much of the product. Open-source options-pricing libraries (e.g. QuantLib) help at the math layer but do not solve the account/order/fill surface. Build remains correct.
Minimal v1 scope (equities only, no options)
Rejected for product reasons. Persona is options-forward ("Weekly-Income Pat"). Options parity in v1 is a product requirement, not a nice-to-have.
Compliance checklist
- [x] Invariant #1 restored for the majority of users (no broker token).
- [x] GDPR erasure cascades through MBT tables (
mbt_accounts+ children onusers.iddelete). - [x] Audit trail on every order lifecycle event (submit, accept, reject, modify, cancel, fill, corporate action).
- [x] Paper-first gate is MBT's responsibility — live-handoff (ADR 0014) reads MBT cycle-profitability state.
- [x] Kill switches:
MBT_TRADING_DISABLED,MBT_NEW_ORDERS_DISABLED,ALPACA_MARKET_DATA_DISABLED. - [x] Rate limits per-tier (60/600/3000 per minute) enforced per
session-engine.md. - [ ] Regulatory counsel consult for any Raxx-published individualized strategy — blocks hosted-strategy feature, not MBT v1.
- [ ] Alpaca market-data capacity at scale — Alpaca-partnerships inquiry pending (non-blocking for v1 user counts).
Revisit when
- Any re-evaluation trigger listed in the Status update section fires.
- MBT v1 ships and produces real user feedback on fill realism.
- Migration phase 2 (flagged cutover) produces parity-diff data — if discrepancies exceed threshold, the fill model needs tuning.
- Regulatory counsel engages — may narrow or widen the product surface Raxx can ship.
- Market-data account capacity limits are hit at scale.
- Premium-tier compute is scoped → new ADR reframes ADR 0011's conclusions against MBT's execution surface.