MBT — Investor Profile Model + Educational Overlay
Status: Draft
Owner: software-architect
Last updated: 2026-04-22
Parent epic: #183 — Multi-tenant architecture (reframed)
Related PRs: #189 (MBT engine design), #183 (parent epic)
Related ADRs: 0003, 0013, 0014, 0015
Resolves MBT §14 Qs: 1 (starting cash), 2 (fill aggressiveness), 3 (interest-rate narrative)
1. Context + goal
MBT is the Raxx-native paper-trading engine. On PR #189, the three blocking open questions in §14 (starting cash default, multi-leg fill aggressiveness, interest-rate model) were answered by the user not with fixed numbers, but with a directive: build a profile-driven experience that infers sensible defaults from the user's stated goals, and teach rather than configure.
This doc specifies the investor-profile model: the onboarding question sequence that infers a profile, the three starter profiles and their per-profile MBT defaults, the educational overlay that explains every trade outcome, and the "stretch goals" cadence mechanic. It does NOT implement these — it shapes the boundary so feature-developer sub-cards can be filed.
2. Invariants that apply
These constraints are non-negotiable (ADR 0002, 0003, 0013, 0015):
- Education over advice. Profile adjusts Raxx-controlled defaults; it never recommends a trade, strategy, or security. Copy "Raxx recommends..." is forbidden at every layer. See §11 for the regulatory line.
- User-directed simulation. The user governs every order. Profile influences starting conditions and overlays; it does not auto-submit or auto-adjust positions.
- Paper-first gating. Profiles exist exclusively in MBT (paper). They have no bearing on live-handoff conditions, which are governed by separate paper-profitability gating.
- GDPR by default. Profile data is PII-lite but sensitive (financial goals, risk tolerance). Full DSR rights apply: exportable, erasable, rectifiable. See §11.
- No stored credentials. Profile system touches no broker credential. This constraint is restated because the onboarding flow MUST NOT embed an API-key entry step.
- Audit trail. Every profile creation, update, and onboarding override writes an
audit_logrow. - Credentials into infra. Profile model has no third-party API dependency. No new secrets.
3. Profile schema — three starter profiles
Three profiles ship in v1. Names below are placeholders — the user must confirm final names before implementation (see §12, Open Question #1).
| Attribute | Trial | Income Builder | Diversifier |
|---|---|---|---|
| Placeholder name | Trial | Income Builder | Diversifier |
| Starting cash default | $10,000 | $50,000 | $100,000 |
| User-overridable? | Yes (within tier cap) | Yes (within tier cap) | Yes (within tier cap) |
| Risk temperament | Conservative | Balanced | Aggressive |
| Primary goal family | Learn mechanics | Generate income | Diversify existing book |
| Fill aggressiveness | Conservative (worse-of-mid/offer) | Mid (mid-price) | Aggressive (mid-price + attempt at mid on touch) |
| Interest-rate narrative tone | Teaching-heavy | Balanced | Assume-familiarity |
| Goal cadence default | Weekly | Monthly | Monthly |
| Target persona | New to investing, trialing on a recommendation | Has savings (~$50k), wants income-generating strategies | Already has a trading platform elsewhere; paper-testing here |
Tier caps still apply: Free users have a lower history-retention ceiling regardless of profile. Profile does not override tier limits.
4. Onboarding flow
Onboarding is 3–5 questions. Answers derive the profile; the user can override any default immediately after. Onboarding can also be re-run from Settings at any time.
Question sequence (copy is a placeholder — requires product-writing pass):
-
"What brings you to Raxx?" - "I'm new to options and want to learn the ropes" → signal: Trial - "I have savings I want to put to work through income strategies" → signal: Income Builder - "I already trade elsewhere and want a testing ground for new strategies" → signal: Diversifier
-
"Have you traded options with real money before?" - No → reinforces Trial signal - Yes, occasionally → supports Income Builder signal - Yes, actively → supports Diversifier signal
-
"What's your goal cadence — when do you want to measure your paper results?" - Daily → emphasizes Trial or very active Income Builder - Weekly → Trial or Income Builder - Monthly → Income Builder or Diversifier
-
"What would a meaningful paper-trade win look like for you?" (optional free-form + presets) - "$100 / week income" → Income Builder - "Beat the S&P 500 in 3 months" → Diversifier - "Understand how a vertical spread works" → Trial
-
(Derived + confirm) Show inferred profile card: starting balance, goal cadence, fill style. User accepts or selects a different profile from a dropdown. This is the override gate.
Profile derivation is a simple scoring function — no ML, no external call. Answers are weights; highest-weighted profile wins; ties break toward the more conservative profile.
5. Profile → MBT defaults mapping
Explicit mapping that resolves MBT §14 Qs 1–3:
| MBT parameter (from §14) | Trial | Income Builder | Diversifier |
|---|---|---|---|
| Starting cash balance | $10,000 | $50,000 | $100,000 |
| Multi-leg fill aggressiveness | Conservative: fill at worse-of-mid/offer | Mid: fill at combined NBBO mid | Aggressive: attempt mid; fallback to mid+1 tick |
| Interest-rate narrative tone | Teaching-heavy (full blurb on every accrual event) | Balanced (abbreviated note + tax summary) | Assume-familiarity (dollar amount + tax line only) |
| Paper history retention scope | Tier cap; 90d for Free | Tier cap; 3yr for Pro | Tier cap; unlimited for Pro+ |
| Goal cadence default | Weekly | Monthly | Monthly |
Fill-aggressiveness maps to the legs_fill_aggressiveness field on mbt_accounts. It is user-overridable per-order via a "Fill preference" toggle in Antlers (present but not prominent for Trial; more visible for Diversifier).
6. Educational overlay pattern
The overlay is Raptor-side logic + Antlers-side rendering. It does not require a separate service but does require a new module.
Mechanism:
After POST /api/mbt/orders produces a fill, the response envelope includes a narrative field:
{
"data": { ...order... },
"narrative": {
"headline": "Your vertical spread filled at $1.42 credit",
"body": "...",
"learn_more_key": "vertical_spread_credit_fill",
"tone": "teaching-heavy"
}
}
Antlers renders the narrative block below the fill confirmation. Tone is derived from the user's profile (investor_profiles.narrative_tone). learn_more_key links to a static glossary page (v1: anchor on a /learn page; v2: in-app drawer).
Narrative service: backend_v2/api/services/narrative_service.py. The implementation sub-card will define it. The design contract is:
- Input:
(trade_event: TradeEvent, profile_tone: NarrativeTone) -> Narrative trade_eventvalues (v1):fill,partial_fill(v2),cancel,reject,itm_expiry,otm_expiry_worthless,stop_triggered,margin_interest_accrued,dividend_creditedprofile_tonevalues:teaching_heavy,balanced,assume_familiarity- Templates are stored as Python string templates in the service module (v1); externalized to a content store in v2 when product-writing polish lands
Templates are keyed by (trade_event, profile_tone). Nine event types × 3 tones = 27 base templates. Multi-leg events (iron condor fill, spread expiry) add another ~12 templates. Total v1 template count: ~40.
Content authorship: v1 ships with engineering placeholder copy. A product-manager or marketing-strategist follow-up card should commission polished copy before GA. See Open Question #4.
7. Margin interest — teaching moment
When MBT accrues simulated margin interest on a position (nightly Celery task mbt.accrue_margin_interest), the next Antlers session render surfaces a margin-interest card. This is triggered by a new margin_interest_accrued activity type in mbt_activities.
The card (tone: teaching-heavy) contains:
- The numbers: "$X.XX interest on $Y,YYY notional at Z% annualized for N days."
- Why it exists (plain language): "Margin means you're borrowing to trade. Like any loan, there's a cost — that's this charge. In paper trading, this is simulated; in live trading with a real broker, this would come out of your account."
- Tax strategy note: "Interest on investment loans may be deductible against investment income — consult your CPA. Raxx surfaces the total here so you can bring the number to that conversation. This is not tax advice."
The tax note is ALWAYS present, regardless of tone. The verbosity of points 1 and 2 scales with tone.
Regulatory note: Raxx does NOT advise on whether the user should use margin, which broker to use, or whether the deduction applies to their situation. The note surfaces the fact and defers to a CPA. This is the correct framing under §202(a)(11) — educational disclosure, not individualized advice.
8. Stretch goals mechanic
Profile sets baseline goal (e.g., "earn $100/week in paper income"). After N consecutive weeks of the user meeting or exceeding the goal, MBT surfaces a stretch prompt:
- "You've hit your weekly goal 4 weeks in a row. Want to try $150/week?" (with Accept / Not yet)
- Accept: updates
investor_profiles.goal_targetand resets the streak counter. - Not yet: dismissed; re-surfaces after another N weeks.
Implementation:
investor_profiles.goal_target(numeric, nullable — null means no goal set)investor_profiles.goal_cadence(enum:daily/weekly/monthly)investor_profiles.goal_streak_count(integer)investor_profiles.goal_streak_threshold(integer, default 4 for weekly, 2 for monthly)- Celery task
mbt.evaluate_goal_streakruns after each cadence period closes (EOD for daily, week-close for weekly, month-close for monthly) - No ML. No external signals. Pure counter on realized P&L vs target.
The stretch prompt is delivered via the activity feed as a system_nudge activity type — not email, not push. User can suppress all nudges from Settings.
9. Data model
Separate investor_profiles table (1:1 with users). Separate because: profile can grow (new fields, new tones) without touching the users table; cleaner GDPR export scope; easier to wipe on erasure.
investor_profiles
id PK
user_id FK -> users.id ON DELETE CASCADE, UNIQUE
profile_type ENUM ('trial' | 'income_builder' | 'diversifier')
starting_cash NUMERIC(15,2) -- user-overridable at onboarding
narrative_tone ENUM ('teaching_heavy' | 'balanced' | 'assume_familiarity')
fill_aggressiveness ENUM ('conservative' | 'mid' | 'aggressive')
goal_target NUMERIC(15,2) NULL -- NULL = no goal set
goal_cadence ENUM ('daily' | 'weekly' | 'monthly') DEFAULT 'weekly'
goal_streak_count INTEGER DEFAULT 0
goal_streak_threshold INTEGER DEFAULT 4
onboarding_completed BOOLEAN DEFAULT FALSE
created_at TIMESTAMPTZ
updated_at TIMESTAMPTZ
mbt_accounts.cash_balance is seeded from investor_profiles.starting_cash at account-creation time (not a live FK — a one-time copy). This preserves MBT account independence if the user later changes their profile starting cash.
Migration: additive — new table, no changes to existing mbt_accounts or users columns. Rollback: DROP TABLE investor_profiles (safe; MBT accounts already seeded fall back to their stored cash_balance).
10. Rollout plan
Feature gated behind FLAG_MBT_INVESTOR_PROFILES (independent of FLAG_MBT_PAPER_TRADING).
- Dark. Schema migration lands; service + API endpoints land; flag off. No user-facing change.
- Beta (flag on for allowlisted users). Run onboarding flow for new accounts; existing users see a "Complete your profile" banner in Settings. Collect feedback on question clarity + profile accuracy.
- GA. Flag on globally. Existing users without a profile are auto-assigned Diversifier ($100k) with a "Review your profile" prompt on next login — this is the safest default for existing users (they've already been operating at the $100k Alpaca-paper level).
- Post-GA. Copy polish pass (marketing-strategist card). Stretch-goals mechanic can ship as a separate flag (
FLAG_MBT_STRETCH_GOALS) after GA if deferred.
11. Security + regulatory considerations
PII scope: investor_profiles contains financial goals and risk tolerance. This is PII-lite — not SSN, not income, not net worth — but it is sensitive. Treat with GDPR Article 9 caution (financial data is not explicitly special-category, but risk tolerance + goal is commercially sensitive).
- GDPR DSR access/portability:
investor_profilesrows included in thembt.export_user_dataCelery task. Export format: JSON alongsidembt_accounts,mbt_positions,mbt_orders. - GDPR erasure:
ON DELETE CASCADEfromusers.id. 30-day cooling per ADR 0003. - Retention:
investor_profilesrow persists as long as the user account persists. No independent retention clock needed; it follows the user-account lifecycle. - Audit: every
investor_profilescreate/update writes anaudit_logrow with redactedgoal_target(store "goal updated" not the dollar value, to avoid PII proliferation in audit logs). - Breach notification: profile rows are in scope for GDPR 72-hour breach notification (ADR 0003 breach-automation covers this).
Regulatory line — education vs advice (§202(a)(11)):
The profile system adjusts Raxx-controlled defaults based on user-stated preferences. It does NOT: - Recommend a specific security - Recommend a specific strategy - Claim to optimize the user's portfolio - Base its defaults on analysis of market conditions
The "stretch goals" mechanic nudges users toward higher self-stated targets. It does NOT tell them how to achieve those targets. The nudge is: "you said you wanted X; you exceeded it; want to aim for more?" — user-directed throughout.
The educational overlay explains what happened; it does not prescribe what to do next.
Flag for counsel (deferred): if future product iterations introduce profile-based strategy suggestions ("users with Income Builder profiles often run covered calls — here's one for you"), that crosses the §202(a)(11) line. That feature MUST NOT ship without securities-attorney sign-off. This is a standing block on all feature cards that propose profile-based strategy recommendations.
12. Open questions for the user — RESOLVED 2026-04-22
All five resolved by the user on PR #193 (2026-04-22). Decisions below are load-bearing for implementation sub-cards.
- Profile names. ✅ Locked: "Trial / Income Builder / Diversifier". The placeholder names are final. No rename pass from marketing.
- Profile count. ✅ Locked: three profiles. No fourth tier in v1. (Future "Pro" tier considered separately post-launch.)
- Onboarding skip. ✅ Locked: skip-path included. Users can skip the question sequence and pick a profile directly from a dropdown; defaults flow from profile selection. Question sequence remains available for users who want guided selection.
- Narrative copy authorship. ⚠️ Blocked on securities-attorney review. MBT v1 does not launch with engineering placeholder copy. Narrative copy must be reviewed by a securities attorney before lock — Investment Advisers Act §202(a)(11) exposure around "investment advice" framing. This creates a launch-blocking dependency: - Follow-up card: email Matthew Crosby (trademark counsel) for a securities-attorney referral - Follow-up card: "attorney review of profile narrative copy" — tracked as launch-blocker
- Stretch goals ship timing. ✅ Locked: ship in MBT v1. Auto-profile-promotion + profile-switch flow are in v1 scope, not deferred to v2. Adds ~2–3 architect sub-cards and extends the v1 engineering window but keeps the UX loop closed at launch.
See ADR 0015 for the decision record. See mbt-paper-trading-engine.md §14 for the original open questions — Qs 1, 2, and 3 are resolved by this doc.