The web cockpit for backtest-kit. A self-hosted dashboard that turns a running backtest or live session into screens you can read: portfolio cards, KPI boards, candlestick charts with signal overlays, a notification feed, a strategy heatmap, markdown reports, a dump-file explorer, a live manual-control panel, and an in-browser Pine Script editor.

Interactive dashboard for backtest-kit with signal visualization, candle charts, risk analysis, and notification management. Built with React 18, Material-UI, and Lightweight Charts.
π Backtest Kit Docs | π GitHub
New here? The fastest real setup is to clone the reference implementation β a working news-sentiment AI trading system with LLM forecasting, multi-timeframe data, and a documented February 2026 backtest. Start there, not from scratch.
npm install @backtest-kit/ui backtest-kit
import { serve } from "@backtest-kit/ui";
serve("0.0.0.0", 60050); // β http://localhost:60050
One call boots the server and serves the dashboard. Everything below describes what each screen shows you β the layout, the data on it, and what you can do there.

Every page sits inside a common frame: a top app bar with the logo (click to toggle home), a horizontally-scrollable row of section tabs, a live notification bell, a fullscreen toggle, and a GitHub link. A thin progress bar under the app bar animates whenever a screen is loading data. Below the bar, most screens open with a breadcrumb strip (Main βΊ Section βΊ β¦) that doubles as the action bar β refresh, download, print, and mode-switch buttons live there.
The home screen (/main) is a launchpad: three labelled groups of large colored tiles, each tile a doorway to one section, with a status banner up top summarizing the engine. The groups:
/overview
Closed trading signals, grouped by symbol, split across Backtest and Live tabs at the top.
Each symbol gets a section of cards; every card is one closed signal showing position type (long/short), entry price, take-profit and stop-loss levels, and the realized PNL in both amount and percent. Where a position used dollar-cost averaging or partial closes, the card displays the DCA entry count and partial-close count. A Backtest/Live tab toggle switches the dataset; the list supports JSON export and manual refresh. Layout is a scrollable tabbed container with a decorative background.
/dashboard Β· /dashboard/:mode
The KPI board. Aggregates trading performance across all symbols for the chosen mode.
Inside a breadcrumb header (KPI BACKTEST / KPI LIVE) the body is a declarative field grid (dashboard_fields) of live widgets fed by four aggregated measures pulled per-symbol and summed:
SingleValueWidget) β Today / Yesterday / 7 days / 31 days, each showing profit in USDT with a trade-count caption, color-coded red (loss) / green (profit) / orange (flat).SpeedDonutWidget) β Failed vs. Successful vs. Total signal counts.ChartWidget) β a per-day series of total / resolved / rejected trades.SuccessRateWidget) β per symbol (icon + display name), broken into take-profit / stop-loss / close-resolved / close-rejected counts.SignalGridWidget) β the paginated signal table for the active mode.The breadcrumb actions: Download (exports the raw signals as a timestamped JSON blob), Switch to LIVE / BACKTEST (jumps between modes), and Refresh manually (clears the signal cache and reloads). A modal loader covers the board while it recomputes.
/logs
A virtualized, filterable feed of the engine's runtime log.
Each entry is a row with a type badge (Debug / Info / Warn / Log), the log topic, a timestamp, and the raw JSON arguments rendered in monospace. A search prompt filters by keyword or regex. The whole log exports as a JSON file. Virtualized so it stays smooth over very long histories.
/notifications
The event feed for the entire signal lifecycle.
Color-coded cards, one per event β opens, closes, schedules, errors β each showing symbol, position, PNL, and the entry / exit / TP / SL prices. Infinite-scroll pagination loads more as you go; clicking a card opens a detailed modal for that event. Manual refresh pulls the latest activity. The same notifications drive the bell badge in the app bar.
/status β /status/:id β /status/:id/control
A three-level drill-down for live positions: list β detail β manual control.
List (/status) β active signals grouped by strategy, rendered as a grid of strategy buttons; click one to inspect it.
Detail (/status/:id) β the full state of one pending signal as a structured field view (status_fields): entry, exit, effective (blended) price, DCA entry count and partial-close count, and live PnL. Header actions: Manual Control, Print (renders the signal's fields to a downloadable PDF/markdown), Download (JSON), Refresh. Empty states read "Loadingβ¦" or "No pending signal."
Manual Control (/status/:id/control) β an operator panel (RecordView, expand-all) where you intervene in a live position by hand through four multi-step wizard modals: Open Pending, Average Buy, Close Pending, and Breakeven. Each is launched from the panel via an emitter, walks you through Briefing β Input β Submit, fires the commit, and reloads the record. Export to JSON is available. (See The modal system below for how these are built.)
/report
Rendered strategy-performance reports for Backtest and Live runs.
A grid of strategy buttons, grouped by type and sorted by signal volume; selecting one renders its markdown report inline. Each report downloads as markdown, PDF, or raw JSON. Manual refresh regenerates the content. This is the human-facing view of the engine's analytics (strategy / breakeven / risk / partial / drawdown / schedule / performance / sync reports).
/price_chart β /price_chart/:symbol β /price_chart/:symbol/:interval
Interactive candlesticks powered by TradingView Lightweight Charts.
You navigate by symbol, then by interval (1m / 15m / 1h) to view price history. The chart overlays the active signal's lines: entry, take-profit (green), and stop-loss (red). Supports chart-image export and clicking through to signal detail. Built on the shared ChartWidget.
/heat
A color-coded performance matrix across every tracked symbol.
Cells are colored by performance; each shows win rate, profit factor, Sharpe ratio and other aggregated metrics per symbol. The whole heatmap exports as JSON, markdown report, or PDF, with manual refresh to recalculate the statistics. (The same heat report download lives on the home screen's action bar.)
/dump Β· /dump/:search
A file browser for everything the engine wrote to disk.
A tree-structured browser of backtest output and artifact files. Icons mark file type β image, JSON, plain text, or generic. Clicking a file opens a full-screen preview modal. Keyword search filters the tree; manual refresh rebuilds it. Backed by the FileTreeWidget.
/about Β· /about/setup
Framework information (/about) and a Setup view (/about/setup) showing the resolved configuration / environment of the running instance.

Shared chrome lives in components/common/: AppHeader, a Markdown renderer, the CodeEditor, NotificationView, Tooltip, IconPhoto, ErrorView, decorative Background / BottomImage. Providers (AlertProvider, ErrorProvider, LayoutModalProvider, StatusInfo, Translate) wrap the app for alerts, error boundaries, modal stacking, the status banner, and i18n.

The dashboard is, at its core, a modal window manager. Two complementary mechanisms sit on top of react-declarative's useModalManager / useWizardModal, both of which keep their own navigation history so modals stack, go back, and close as a managed stack rather than ad-hoc dialogs.
One provider wraps the whole app and registers ~25 detail modals β one per signal-lifecycle event and commit type β each wired to a layoutService subject. Anywhere in the app, pushing to a subject (e.g. pickSignalSubject.next(signal)) pushes the matching modal onto the managed stack; because the stack tracks depth, opening a modal from inside another (e.g. clicking a related signal) stacks it, and the title-bar Back arrow pops you to the previous one. closeModalSubject / ctx.clear() tears the whole stack down centrally. The provider also hosts global prompt, alert, and document-download/preview (useOpenDocument) flows.
These are the modals that open when you click a notification card, a signal in a grid, or a risk event β every point in a position's life has a dedicated, fielded modal, each with the same Back / Print / Search / Copy / ActionMenu / Close title bar and the same StockChart candle tabs.
Each hook below is a pick* opener subscribed to its layoutService.pick*Subject; the hook renders a modal that displays the event's DTO through its assets/*_fields.tsx schema, inside the tabbed shell + <ActionMenu /> title bar described above:
| Hook | Opens a modal for |
|---|---|
useSignalView |
a generic signal (signal_fields) |
useRiskView |
a risk rejection (risk_fields) |
useSignalOpenedView / useSignalClosedView / useSignalScheduledView / useSignalCancelledView |
the four signal-notification kinds |
useSignalNotifyView |
an info notification |
useSignalSyncOpenView / useSignalSyncCloseView |
broker-sync open/close |
useActivateScheduledView |
a scheduled signal activating |
useAverageBuyCommitView |
a DCA-rung commit |
useClosePendingView / useCancelScheduledView |
close / cancel commits |
usePartialProfitAvailableView / usePartialProfitCommitView |
partial-profit available + committed |
usePartialLossAvailableView / usePartialLossCommitView |
partial-loss available + committed |
useBreakevenAvailableView / useBreakevenCommitView |
breakeven available + committed |
useTrailingStopView / useTrailingTakeView |
trailing stop / take |
useDumpContentView |
a dump-file preview |
These are the modals that open when you click a notification card, a signal in a grid, or a risk event β every point in a position's life has a dedicated, fielded modal, each with the same Back / Print / Search / Copy / ActionMenu / Close title bar.
The four operations on the Manual Control screen are multi-step wizards, each built with useWizardModal over a private createMemoryHistory() β so each modal has its own back/forward navigation through three routed steps.
Folders: useOpenPendingModal, useAverageBuyModal, useClosePendingModal, useBreakevenModal. Each contains useXModal.tsx (the useWizardModal config β title, fullscreen size request, close button, onSubmit β reload), routes.tsx (three IWizardModal routes), steps.ts (the stepper labels), view/{BriefView,FormView,SubmitView}.tsx, and components/StatusCard.tsx. The page (ControlView) subscribes to four emitters (commitOpenPendingEmitter, commitAverageBuyEmitter, commitClosePendingEmitter, commitBreakevenEmitter) and calls pickData() to launch the matching wizard.
The three steps (Open Position example):
controlViewService.getAveragePrice) with a "verify the symbol AND the price" warning. Next β /form, Close dismisses.Back β /brief, Next β /submit.controlViewService.commitOpenPending / commitAverageBuy / commitClosePending / commitBreakeven) and shows a StatusCard in loading β success / error state; Close finalizes (beginSave) and the underlying record reloads.Average Buy, Close Pending, and Breakeven follow the same BriefβFormβSubmit shape with operation-specific form fields and StatusCard copy.
assets/*_fields.tsx) β every DTO rendered as a real formThe 24 files in assets/ are the declarative field schemas that drive both the detail pages and every modal above. Each exports a default TypedField[] array (react-declarative), so a DTO is shown with typed, labeled, validated, grouped fields β and, where useful, live widgets β instead of a raw JSON dump. Nothing in a signal's life is hidden from the operator.
dashboard_fields (β DashboardPage) β not a text form but a widget grid: revenue cards, donut, daily-trades chart, success-rate panel, signal grid (see PNL Performance above).signal_fields (β StatusView detail + print, useSignalView) and status_fields (β StatusView) β the full pending/closed signal record.risk_fields (β useRiskView), setup_fields (β About/Setup view).use*View hook in the global registry): signal_opened_fields, signal_closed_fields, signal_scheduled_fields, signal_cancelled_fields, signal_notify_fields, signal_sync_open_fields, signal_sync_close_fields, activate_scheduled_fields, average_buy_commit_fields, close_pending_commit_fields, cancel_scheduled_commit_fields, breakeven_available_fields, breakeven_commit_fields, partial_profit_available_fields, partial_profit_commit_fields, partial_loss_available_fields, partial_loss_commit_fields, trailing_stop_fields, trailing_take_fields.These are the largest source files in the package (most 27β37 KB) precisely because they describe every field of every lifecycle DTO β the schemas are the UI's knowledge of the engine's data model.