Skip to content

Phase 4: Add Next.js dashboard for metrics and trade journal#5

Merged
1816x merged 5 commits into
mainfrom
claude/phase-4-continuation-49bk7k
Jul 23, 2026
Merged

Phase 4: Add Next.js dashboard for metrics and trade journal#5
1816x merged 5 commits into
mainfrom
claude/phase-4-continuation-49bk7k

Conversation

@1816x

@1816x 1816x commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Implements the Phase 4 visualization layer — a Next.js (App Router, TypeScript, Tailwind) dashboard that consumes the backtest/ FastAPI backend API.

Summary

Adds a complete frontend dashboard with two main pages:

  • Metrics (/): Real-time microstructure charts (order-flow imbalance, realized volatility, volume, VWAP) with configurable window limits and stat tiles for the latest bucket
  • Journal (/journal): Trade log with market regime context at entry time, a form to log new trades, and a Claude-powered behavioral analysis panel

Key Changes

API Client & Data Handling

  • src/lib/api.ts: Typed client for the backend with ApiError exception class, request/response helpers, and type definitions for all data models (metrics, journal entries, behavioral analysis)
  • src/lib/nsjson.ts: BigInt-safe JSON parsing/serialization for nanosecond timestamps using ES2025 JSON.parse source context and JSON.rawJSON — preserves precision beyond Number.MAX_SAFE_INTEGER
  • src/lib/useApi.ts: Minimal fetch-on-mount hook with manual refetch, keeps previous data on error for graceful degradation

Pages & Components

  • src/app/page.tsx: Metrics dashboard with limit selector, stat tiles, and four chart components
  • src/app/journal/page.tsx: Journal page with entry form, trade table, and analysis panel
  • src/components/EntryForm.tsx: Form to log trades with client-side validation (exit time ≥ entry time)
  • src/components/JournalTable.tsx: Sortable table of trades joined to market regime at entry
  • src/components/AnalysisPanel.tsx: Behavioral analysis UI with severity badges and disclaimer

Charts

  • src/components/charts/OfiChart.tsx: Diverging bar chart (buy/sell colored) for order-flow imbalance
  • src/components/charts/VolatilityChart.tsx: Line chart for realized volatility
  • src/components/charts/VolumeChart.tsx: Stacked bar chart for buy/sell volume
  • src/components/charts/VwapChart.tsx: Line chart for volume-weighted average price
  • src/components/charts/common.tsx: Shared chart chrome (grid, axes, tooltip formatting)

Styling & Layout

  • src/app/globals.css: Dark-only design system with validated color tokens (diverging buy/sell pair, series colors)
  • src/components/Nav.tsx: Navigation between Metrics and Journal pages
  • src/components/StatTile.tsx: Reusable stat display component
  • src/components/states.tsx: Error and empty-state UI components
  • src/lib/format.ts: Number formatting utilities (integers, prices, signed values, significant figures)

Configuration & Build

  • next.config.ts: Same-origin proxy rewrite to backend (no CORS needed)
  • tsconfig.json, package.json, postcss.config.mjs: TypeScript, dependencies (Next.js 16, React 19, Recharts), PostCSS setup
  • eslint.config.mjs, vitest.config.ts: Linting and unit test configuration

Tests

  • src/lib/api.test.ts: API client tests (BigInt parsing, journal rows with null regime, POST serialization)
  • src/lib/nsjson.test.ts: Nanosecond JSON round-trip tests (precision preservation, null handling, array support)
  • src/components/EntryForm.test.tsx: Form validation and submission tests
  • src/components/AnalysisPanel.test.tsx: Analysis rendering and error handling tests
  • src/components/JournalTable.test.tsx: Table rendering with regime columns

CI/CD

  • .github/workflows/ci.yml: Added Node.js linting, type-checking, and test steps for the dashboard

Notable Implementation Details

  • Nanosecond precision: Uses ES2025 `JSON.

claude added 5 commits July 23, 2026 00:53
Raw generated output of `create-next-app@16.2.11 --ts --tailwind --eslint
--app --src-dir --import-alias "@/*" --use-npm --disable-git`, unmodified,
in its own commit so the hand-written Phase 4 work stays reviewable on top
of it (the same reason the scaffold was deferred out of Phase 0).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A8BE1pjFaGWKzeQcY2baYu
parseNs/stringifyNs round-trip the backend's *_ns nanosecond epochs
(~1.76e18 > 2^53) without precision loss, using the ES2025 reviver
context.source on the way in and JSON.rawJSON on the way out — the
pydantic models expect JSON integers, so strings are not an option on
POST. ensureSupported fails loudly on runtimes that would silently
round. This closes the "Phase 4 concern" recorded in backtest.api's
module docstring.

The typed client (api.ts) mirrors every endpoint contract the UI needs
and maps FastAPI error bodies — string details and 422 validation
lists — into ApiError{status, detail}. All calls go through the
same-origin /api/backend prefix, rewritten in next.config.ts to
BACKEND_URL (default localhost:8000): the backend serves no CORS
headers and, behind the proxy, doesn't need any, so Phases 1-3 stay
untouched.

Vitest covers exact BigInt round-trips (2^53+1, digits inside strings,
null timestamps) and the error mapping. tsconfig target moves
ES2017 → ES2022: BigInt literals require ES2020+, tsc is typecheck-only
here, and BigInt cannot be downleveled anyway.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A8BE1pjFaGWKzeQcY2baYu
Metrics page: stat tiles for the latest window plus four charts — OFI as
diverging bars (sign-aware 4px rounded data-ends), realized volatility
and VWAP as single-series lines, buy/sell volume stacked. VWAP gets its
own frame rather than a second axis on the volume chart: a price and a
contract count never share one plot. The buy↔sell pair doubles as the
diverging palette and was machine-validated against the dark surface
(CVD ΔE 19.2, normal-vision 29.0, contrast >= 3:1); single-series charts
carry no legend — the title names the series — and text never wears a
series color. Window-count selector and manual refresh; refetches hold
the previous frame at reduced opacity instead of flashing empty.

Journal page: entries joined to their market regime (regime columns dash
out when no bucket covers the entry), a log-a-trade form that mirrors
the server's exit>=entry rule client-side and surfaces 422 details
verbatim, and the behavioral-analysis panel behind an explicit button —
one Claude call per click — with distinct 503 (no key) and 502 (no
structured analysis) states.

useApi derives loading from the not-yet-settled (fetcher, generation)
pair instead of setting state synchronously in the effect, which is both
what eslint-config-next 16's set-state-in-effect rule demands and
race-free under limit switches. System sans everywhere per the dataviz
spec — dropping next/font's Google fetch also keeps builds offline.

27 Vitest cases cover the data layer round-trips and the three
interactive components against a stubbed fetch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A8BE1pjFaGWKzeQcY2baYu
Third CI job (dashboard, Node 22) runs the same gate as locally:
npm ci, lint, typecheck, vitest, next build — mirroring the
working-directory pattern of the engine and backtest jobs.

dashboard/README.md replaces the create-next-app boilerplate with the
actual run/verify instructions (backend prerequisite, BACKEND_URL, the
*_ns BigInt strategy). Root README ticks Phase 4, adds the dashboard
step to "Running locally" and its check line to the CI block, and
records the Phase 4 design decisions: Recharts (and the no-dual-axis
rule), same-origin proxy over CORS, BigInt end-to-end for *_ns, and the
read+create+analyze journal scope.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A8BE1pjFaGWKzeQcY2baYu
The rendered pages surfaced two things the unit gate can't see: the
volatility y-axis clipped its leading zero (0.000032 → ".000032") at
56px, so the axis gets 72px; and the journal's timestamp cells repeated
the UTC suffix the column header already carries, pushing the regime
columns behind the horizontal scroll at common widths.

Verified against the real pipeline: engine over the sample tape (5000
ticks → 501 buckets), uvicorn serving it, journal seeded and a trade
logged through the form (regime join populated on the new row), zero
browser console errors on either page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A8BE1pjFaGWKzeQcY2baYu
@1816x
1816x merged commit 8706e03 into main Jul 23, 2026
3 checks passed
@1816x
1816x deleted the claude/phase-4-continuation-49bk7k branch July 23, 2026 17:34
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.

2 participants