Skip to content

Trading console UI, shared primitives, and frontend tests - #1

Merged
ggange merged 1 commit into
mainfrom
feat/trading-console-ui
Aug 2, 2026
Merged

ggange merged 1 commit into
mainfrom
feat/trading-console-ui

Conversation

@ggange

@ggange ggange commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Why

The Horizon 4 trading track — scheduler, risk guardrails, kill switch, order lifecycle, reconciliation, soak reporting — landed entirely in the API with no UI. 18 of 26 routes had no frontend at all, so the only way to see whether the loop was healthy (is the kill switch engaged? did the 16:30 cron fire? is the ledger drifting from the venue?) was curl.

The next roadmap step is a paper soak. A soak you can't watch is a soak you won't watch.

What's in it

Trading console — a fourth tab wiring every /trade/* route:

  • Status strip — broker + live/paper, simulated fill config, scheduler cron and next/last run, reconciliation state. Two conditions get a loud banner rather than a pill: an engaged kill switch, and a live broker missing the limits the API fails closed on. These mirror the warnings the API already logs at boot.
  • Controls — plan editor (create/edit/delete, reusing StrategyPicker), risk limits showing the plan/global merge as effective so it's visible rather than guessed, kill switch, manual tick and cycle triggers.
  • Live state — positions, orders (open-only filter), intents including guardrail rejections, soak report, reconciliation drift.

Two deliberate safety asymmetries, both covered by tests and exercised in a browser:

  • Engaging the kill switch is one click; releasing requires typing RELEASE. Stopping should never be harder than starting, and releasing is the direction that re-arms capital.
  • Both order-placing buttons require a confirm step and turn destructive-red when the broker is live.

Design primitives — Input/Select/Button/Table/StatusPill/EmptyState, plus lib/chartTheme.ts and lib/metrics.ts. Removes a 9-utility class string copy-pasted across 8 files, chart hex across 6, and metric helpers duplicated between the sweep and walk-forward panels. Also extracts ParamRangeGrid, shared by both forms.

Bugs fixed

Three of these only surfaced while verifying against a live paper_sim broker:

Bug Impact
RunListItem.kind omitted "scheduled_tick" The scheduler writes those rows to the same runs table; they rendered as backtests with a click handler that silently did nothing
Share quantities rounded to whole numbers The loop sizes by cash, so a real position is 32.37188786847498 shares — displayed as "32"
strategy stored as a JSON blob Rendered raw as {"period": 5, "type": "rsi"} in table cells
useTradeConsole used Promise.all One failing endpoint blanked the whole console, including the kill switch. Now degrades per-call and names what failed
Every asset row emitted id="strategy-select" Duplicate ids meant labels focused the wrong control

Tests

Vitest + Testing Library — 107 tests across 12 files, added to the existing CI frontend job. Coverage is weighted toward where a bug is expensive: the confirmation gates, the limits merge, the safety banners, API error decoding, and buildRequest() (previously untested).

Verification

Beyond the suite, this was run end-to-end against a live backend on paper_sim:

  • Plan create → tick → orders, positions, intents populate; reconciliation reports in-sync
  • max_position_value lowered → intent rejected with the guardrail reason surfaced in the UI
  • Kill switch engaged → tick emits no intent; release blocked until the phrase is typed
  • All three pre-existing tabs regression-checked (portfolio backtest, sweep, walk-forward) — no console errors

Housekeeping

Untracks tsconfig*.tsbuildinfo and the generated vite.config.js/.d.ts — tsc -b rewrites them on every build, so they added unrelated churn to every commit. Bumps postcss to clear a pre-existing high-severity advisory (npm audit now clean).

Known, not addressed

  • The console's control column runs taller than the content pane at desktop width, leaving dead space on the right. Worth collapsible sections, but that's a design call.
  • JS bundle is 794 kB (recharts + lightweight-charts). Predates this work.

🤖 Generated with Claude Code

The Horizon 4 trading track (scheduler, guardrails, kill switch, order
lifecycle, reconciliation, soak reporting) landed entirely in the API with
no UI. 18 of 26 routes had no frontend at all, so the only way to see
whether the loop was healthy was curl. The next roadmap step is a paper
soak, and a soak you can't watch is a soak you won't watch.

Trading console (new tab):
- Status strip: broker + live/paper, scheduler cron and next/last run,
  reconciliation state. An engaged kill switch and a live broker missing
  the limits the API fails closed on each get a banner, not a pill.
- Controls: plan editor, risk limits showing the plan/global merge as
  `effective`, kill switch, manual tick and cycle triggers.
- Live state: positions, orders, intents (including guardrail rejections),
  soak report, reconciliation drift.

Two deliberate safety asymmetries, both tested: engaging the kill switch
is one click while releasing requires typing a confirmation phrase, and
both order-placing buttons need a confirm step and turn destructive-red
when the broker is live.

Design primitives: Input/Select/Button/Table/StatusPill/EmptyState, plus
lib/chartTheme.ts and lib/metrics.ts. Removes a 9-utility class string
copy-pasted across 8 files, chart hex across 6, and metric helpers
duplicated between the sweep and walk-forward panels. Also extracts
ParamRangeGrid, shared by both forms.

Bugs fixed, three found while verifying against a live paper broker:
- RunListItem.kind omitted "scheduled_tick", which the scheduler writes to
  the same runs table; those rows rendered as backtests with a click
  handler that silently did nothing.
- Share quantities were rounded to whole numbers. The loop sizes by cash,
  so a real position is 32.37188786847498 shares, displayed as "32".
- The ledger stores strategy as a JSON blob, rendered raw in table cells.
- useTradeConsole used Promise.all, so one failing endpoint blanked the
  whole console including the kill switch. Now degrades per-call.
- Every asset row emitted id="strategy-select", so labels focused the
  wrong control.

Tests: vitest + testing-library, 107 tests, added to the CI frontend job.

Housekeeping: untrack tsbuildinfo and the generated vite.config.js/.d.ts
(tsc -b rewrites them every build, so they added churn to every commit);
bump postcss to clear a pre-existing advisory.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ggange
ggange merged commit 8d8b773 into main Aug 2, 2026
3 checks passed
ggange added a commit that referenced this pull request Aug 13, 2026
Trading console UI, shared primitives, and frontend tests
@ggange
ggange deleted the feat/trading-console-ui branch August 13, 2026 09:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant