πŸ“Š @backtest-kit/ui

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.

screenshot

Ask DeepWiki npm TypeScript

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.


screenshot

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:

  • Application β€” Portfolio Overview, PNL Performance, System Logs
  • Live β€” Notifications, Pending Status, Dump Explorer
  • Other β€” Markdown Reports, Price Charts, Heatmap

screenshot

Closed trading signals, grouped by symbol, split across Backtest and Live tabs at the top.

What's on the page

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.

screenshot

The KPI board. Aggregates trading performance across all symbols for the chosen mode.

What's on the page

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:

  • Four revenue cards (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).
  • Trade-performance donut (SpeedDonutWidget) β€” Failed vs. Successful vs. Total signal counts.
  • Daily-trades chart (ChartWidget) β€” a per-day series of total / resolved / rejected trades.
  • Success-rate panel (SuccessRateWidget) β€” per symbol (icon + display name), broken into take-profit / stop-loss / close-resolved / close-rejected counts.
  • Signal grid (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.

screenshot

A virtualized, filterable feed of the engine's runtime log.

What's on the page

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.

screenshot

The event feed for the entire signal lifecycle.

What's on the page

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.

screenshot

A three-level drill-down for live positions: list β†’ detail β†’ manual control.

What's on the page

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.)

screenshot

Rendered strategy-performance reports for Backtest and Live runs.

What's on the page

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).

screenshot

Interactive candlesticks powered by TradingView Lightweight Charts.

What's on the page

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.

screenshot

A color-coded performance matrix across every tracked symbol.

What's on the page

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.)

screenshot

A file browser for everything the engine wrote to disk.

What's on the page

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.

screenshot

Framework information (/about) and a Setup view (/about/setup) showing the resolved configuration / environment of the running instance.


screenshot

The building blocks
  • ChartWidget β€” Lightweight Charts candlestick/line rendering with signal overlays (Price Charts, signal detail).
  • SignalGridWidget β€” the paginated signal table (offset pagination + async item iterator).
  • StatusWidget β€” live workload status tiles.
  • SuccessRateWidget / SpeedDonutWidget β€” donut gauges for win-rate and throughput.
  • SingleValueWidget / IndicatorValueWidget β€” single-KPI and indicator-value cards.
  • AveragingWidget β€” DCA-ladder / effective-price visualization.
  • PartialWidget β€” partial-close breakdown.
  • FileTreeWidget β€” the dump-explorer tree.

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.

screenshot

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.

What's registered (every modal hook)

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.

The wizard anatomy (shared by all four)

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):

  1. Brief β€” fetches and shows the symbol + current effective price (controlViewService.getAveragePrice) with a "verify the symbol AND the price" warning. Next β†’ /form, Close dismisses.
  2. Form β€” collects direction (long/short), amount in USDT, and a note; live-computes coin quantity = amount Γ· price as a read-only field; warns that the position opens immediately. Back β†’ /brief, Next β†’ /submit.
  3. Submit β€” fires the commit (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.

The 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.

The schemas and who imports them
  • 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).
  • Lifecycle/commit schemas (each β†’ its 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.