docs: spec — expand IBKR paper parity to full UX (overview + account view + multi-strat)
Add D3 (overview EXEC badge + real-vs-sim divergence chip + account rollup), D4 (/paper/ibkr account-level view: total NAV + all positions/fills + per-strategy breakdown), a UX & information-architecture section (3-level progressive disclosure, one divergence signal rendered consistently), and a strategy-tagged data model so a second executor is a new ROW, not a redesign. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -7,24 +7,33 @@
|
||||
## Goal
|
||||
|
||||
Bring the IBKR paper strategies to cockpit parity with the Bybit book: show their **backtest** in the sim
|
||||
and their **real paper-account trading** (actual DU-account fills / positions / NAV) in the cockpit — so the
|
||||
paper trading that is already working is *visible and verifiable*, as the honest pre-live confidence path.
|
||||
and their **real paper-account trading** (actual DU-account fills / positions / NAV) across the cockpit —
|
||||
overview (at-a-glance), strategy detail (the slice), and an account-level view (the whole DU pot) — so the
|
||||
paper trading that is already working is *visible and verifiable* at every level, as the honest pre-live
|
||||
confidence path. UX is a first-class requirement, and the design scales to multiple executing strategies by
|
||||
adding a row, not a redesign.
|
||||
|
||||
## Scope
|
||||
|
||||
**In:**
|
||||
- D1 — the IBKR strategies' honest-cost backtest curve in `/paper/sim`.
|
||||
- D2 — capture the **real** IBKR DU paper-account state (fills, positions, NAV), render it as a cockpit book,
|
||||
and reconcile it against the recomputed (idealized) `multistrat` sim forward.
|
||||
- D2 — capture the **real** IBKR DU paper-account state (fills, positions, NAV), render it on the strategy
|
||||
**detail** page, and reconcile it against the recomputed (idealized) `multistrat` sim forward.
|
||||
- D3 — surface the executed reality on the **overview** page (a per-strategy EXEC + real-vs-sim divergence
|
||||
signal, and an account-rollup summary), so the fleet view shows what is actually paper-trading at a glance.
|
||||
- D4 — an account-level **IBKR paper account view** (the whole DU account: total NAV, all positions/fills, and
|
||||
a per-strategy breakdown) — the design that answers "how do I see it when more strats execute."
|
||||
|
||||
Designed for scale (UX is a first-class requirement): today only `multistrat` executes on DU (vrp exec is
|
||||
suspended on IBKR options permission; sixtyforty is a benchmark), so the account currently *is* the multistrat
|
||||
book. But ALL captured data is tagged by strategy and the views are built at both the per-strategy AND the
|
||||
account level, so a second executor arming is a new ROW, never a redesign.
|
||||
|
||||
**Out (explicit):**
|
||||
- No live / real-capital execution, no arm step, no promotion→allocation lifecycle changes ("still on paper,
|
||||
no live yet"). Gate/promotion/allocation semantics are UNCHANGED.
|
||||
- Bybit stays exactly as-is (a pure simulated book — no EU Bybit-testnet API keys are available, so there is
|
||||
no real Bybit account to ingest). This design does not touch the Bybit `/paper` book or its sim.
|
||||
- Per-strategy attribution of the DU account. Today only `multistrat` executes on DU (vrp exec is suspended on
|
||||
IBKR options permission; sixtyforty is a benchmark), so the DU account *is* the multistrat book. Attribution
|
||||
across multiple executors is deferred until a second executor arms (YAGNI).
|
||||
|
||||
## Current state (grounded)
|
||||
|
||||
@@ -98,12 +107,73 @@ verification: it answers "does the executed paper book track the simulation, and
|
||||
cost?" Surface a compact headline (e.g. cumulative sim-vs-executed divergence + median fill slippage) on the
|
||||
detail page. `multistrat_exec` (the observed envelope-return track) remains the gated exec twin, unchanged.
|
||||
|
||||
## D3 — Overview page: the executed reality at a glance
|
||||
|
||||
The overview (`/`, `dashboard_service.fleet()`/`overview()`) already renders each strategy with gate/health/
|
||||
backtest/venue badges. Add, for strategies that execute on a real paper account:
|
||||
- an **EXEC** badge (distinct from the existing venue "ref" badge) — "this strategy is placing real orders on
|
||||
the IBKR paper account," so a viewer instantly separates *executing* strats from pure-sim ones;
|
||||
- a **real-vs-sim divergence chip** — the executed track's return vs the idealized sim, rendered with the same
|
||||
semantic-color language as the health/gate badges (near-zero = neutral, widening = amber). This is the
|
||||
headline "is execution tracking the model" signal, visible without drilling in.
|
||||
|
||||
Add a compact **IBKR paper account rollup** to the overview (a single card/line): total DU NAV, number of
|
||||
executing strategies, and aggregate real-vs-sim. Today it reflects `multistrat`; it is account-level so it
|
||||
simply grows as strats arm. It links to the account view (D4).
|
||||
|
||||
## D4 — IBKR paper account view (account-level; the multi-strat home)
|
||||
|
||||
A dedicated view of the DU account **as an account** (route e.g. `/paper/ibkr`), reachable from the overview
|
||||
rollup and the exec-venue badge:
|
||||
- the **total** real account NAV curve (from `ibkr_account_nav`),
|
||||
- **all** current positions and recent fills across every executing strategy,
|
||||
- a **per-strategy breakdown table** — one row per executing strat: its attributed executed return, its
|
||||
attributed positions/fills, and its real-vs-sim divergence. One row today (`multistrat`); N rows as strats
|
||||
arm — the answer to "how do I see it with more strats."
|
||||
|
||||
This is the account-level truth (the DU account is one pot of capital); the per-strategy detail pages (D2.2)
|
||||
are the slices. Both are driven by the same strategy-tagged capture.
|
||||
|
||||
## UX & information architecture
|
||||
|
||||
Three levels, progressive disclosure, one consistent divergence signal at every level:
|
||||
|
||||
```
|
||||
Overview / -> fleet scan: per-strat [gate][health][EXEC][sim-vs-exec chip]
|
||||
| + IBKR paper account rollup card (total NAV, N strats, agg divergence)
|
||||
|-- Strategy detail /strategy/multistrat -> this strat's slice: real NAV curve, its positions + fills
|
||||
| (per-fill slippage), reconciliation headline vs its sim
|
||||
'-- Account view /paper/ibkr -> the whole DU account: total NAV, all positions/fills,
|
||||
per-strategy breakdown table (1 row now, N later)
|
||||
```
|
||||
|
||||
UX principles (professional ops UI):
|
||||
- **At-a-glance first:** the overview must answer "which strats are actually paper-trading, and is execution
|
||||
tracking the model?" from badges/chips alone — no drill-in required.
|
||||
- **Progressive disclosure:** overview (fleet) → strategy detail (one slice) → account view (the pot). Each
|
||||
level adds resolution, none duplicates another's job.
|
||||
- **One divergence signal, rendered consistently** at all three levels (chip on overview, headline on detail,
|
||||
breakdown on account) — the same computation, the same color language.
|
||||
- **Reuse the existing visual system:** the gate/health badge palette + `exec_badge` macro; do NOT invent a new
|
||||
palette. The EXEC badge and divergence chip are new *states* in the existing badge vocabulary.
|
||||
- **Scale by row, not redesign:** every view renders a *set* of executing strats (size 1 today); adding a strat
|
||||
never changes the layout, only adds a row/chip.
|
||||
- **Honest empty/degraded states:** no captured account yet, or ib-gateway unreachable, renders a clear "no
|
||||
executed data yet" state — never a 500, never a fabricated number. Consistent with the health axis (a stalled
|
||||
exec track already surfaces as STALE).
|
||||
|
||||
## Data model
|
||||
|
||||
- **Reuse** `exec_fills` (`ExecFillRow`) — populate it (currently empty). No schema change.
|
||||
- **Reuse** `exec_fills` (`ExecFillRow`) — populate it (currently empty). It already carries `rebalance_id`;
|
||||
ensure each rebalance's `rebalance_id` maps to its `strategy_id` (a `strategy_id` column or a rebalance→strat
|
||||
mapping) so fills are attributable per strategy for D4's breakdown. No breaking schema change (add the tag).
|
||||
- **New** `ibkr_account_nav` (per-run real account snapshot): `run_date` PK, `nlv`, `cash`, `gross`,
|
||||
`positions_json`, `at`. Idempotent create + replace-per-run; migrate follows the existing
|
||||
`forward_nav`/`paper_repo`/`strategy_health` pattern (no heavy startup DDL).
|
||||
`positions_json`, `at`. Account-level (the whole DU pot). Idempotent create + replace-per-run; migrate follows
|
||||
the existing `forward_nav`/`paper_repo`/`strategy_health` pattern (no heavy startup DDL).
|
||||
- **Per-strategy executed return** already exists as the `*_exec` observed forward tracks (`multistrat_exec`
|
||||
today; each future executor gets its own, same pattern) — the account view's per-strategy breakdown reads
|
||||
these + the strategy-tagged `exec_fills`. The account NAV rolls up the account; per-strat return stays on the
|
||||
`*_exec` tracks. No new per-strat NAV store.
|
||||
- **No change** to `forward_summary`, `backtest_summary`, `strategy_health`, gate/promotion/allocation.
|
||||
|
||||
## Architecture / components
|
||||
@@ -116,7 +186,12 @@ detail page. `multistrat_exec` (the observed envelope-return track) remains the
|
||||
- `adapters/persistence/` — the `ibkr_account_nav` repo (create/replace/read a NAV series + latest positions),
|
||||
following `forward_nav`/`paper_repo` conventions.
|
||||
- `adapters/web/app.py` + templates — D1 sim book-list/curve-source dispatch; D2 the multistrat-detail "IBKR
|
||||
paper account" section (real NAV curve + positions + fills + reconciliation headline).
|
||||
paper account" section (real NAV curve + positions + fills + reconciliation headline); D3 the overview EXEC
|
||||
badge + divergence chip + account-rollup card (via `dashboard_service.fleet()`/`overview()` + the badge
|
||||
macros); D4 the `/paper/ibkr` account view (total NAV + all positions/fills + per-strategy breakdown table).
|
||||
- `application/dashboard_service.py` — compute the per-strategy exec status + real-vs-sim divergence (one shared
|
||||
function feeding overview chip, detail headline, account breakdown — the "one signal, three places" rule) and
|
||||
the account rollup.
|
||||
- `application/backtest_refs*` / the nightly asset — persist the IBKR recompute-replay curve where the sim can
|
||||
read it cheaply (mirrors `bybit_sleeve_ret`).
|
||||
|
||||
@@ -140,6 +215,14 @@ detail page. `multistrat_exec` (the observed envelope-return track) remains the
|
||||
- **D2.2/2.3:** the multistrat detail page renders the real NAV curve, positions, fills; the sim-vs-executed
|
||||
reconciliation computes the divergence on a fixture (known sim series vs known executed fills → expected
|
||||
divergence + median slippage); empty data → empty section, no 500.
|
||||
- **D3:** the shared divergence function returns the expected value on a fixture; the overview renders the EXEC
|
||||
badge + divergence chip for an executing strat and NOT for a pure-sim strat; the account-rollup card shows
|
||||
total NAV + strat count; no-executed-data → no badge/empty card, no 500.
|
||||
- **D4:** `/paper/ibkr` renders total NAV + all positions/fills + a per-strategy breakdown row for `multistrat`;
|
||||
the breakdown is a SET (assert it renders correctly for one strat and would render N — e.g. a two-strat
|
||||
fixture yields two rows); empty account → clean empty state, no 500.
|
||||
- **Scale test:** a fixture with a second (synthetic) executing strat proves overview/account views render both
|
||||
without layout change — the "new row not redesign" invariant.
|
||||
|
||||
## Constraints
|
||||
|
||||
@@ -152,5 +235,14 @@ detail page. `multistrat_exec` (the observed envelope-return track) remains the
|
||||
|
||||
## Sequencing
|
||||
|
||||
D1 first (low-risk, backtests exist, immediate parity), then D2 (D2.1 capture → D2.2 book view → D2.3
|
||||
reconciliation). Each is independently testable and shippable.
|
||||
1. **D1** — sim curve for IBKR (low-risk, backtests exist, immediate parity).
|
||||
2. **D2.1** — capture the real account (fills → `exec_fills` tagged by strategy; snapshot → `ibkr_account_nav`).
|
||||
The data foundation everything else reads.
|
||||
3. **D2.2 + D2.3** — the multistrat detail "IBKR paper account" section + the sim-vs-executed reconciliation
|
||||
(build the ONE shared divergence function here).
|
||||
4. **D3** — overview EXEC badge + divergence chip (reusing the shared function) + account-rollup card.
|
||||
5. **D4** — the `/paper/ibkr` account view (total NAV + all positions/fills + per-strategy breakdown).
|
||||
|
||||
Each step is independently testable and shippable; D2.1 is the pivot (it fills `exec_fills` + the account
|
||||
snapshot that D2.2/D3/D4 all render). The shared divergence function (built in step 3) is reused by D3 and D4
|
||||
so the signal is computed once and rendered three places.
|
||||
|
||||
Reference in New Issue
Block a user