@backtest-kit/ui 14.1.0 → 15.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +273 -273
  2. package/build/index.cjs +28 -8
  3. package/build/index.mjs +28 -8
  4. package/build/modules/frontend/build/3rdparty/ace_1.4.12/ace.js +21337 -21337
  5. package/build/modules/frontend/build/3rdparty/ace_1.4.12/mode-javascript.js +797 -797
  6. package/build/modules/frontend/build/3rdparty/ace_1.4.12/theme-chrome.js +137 -137
  7. package/build/modules/frontend/build/3rdparty/ace_1.4.12/theme-twilight.js +115 -115
  8. package/build/modules/frontend/build/3rdparty/ace_1.4.12/worker-javascript.js +15012 -15012
  9. package/build/modules/frontend/build/3rdparty/qfchart_0.8.7/echarts.min.js +45 -45
  10. package/build/modules/frontend/build/assets/{Article-CpU7DBcT.js → Article-BFa1r6-4.js} +1 -1
  11. package/build/modules/frontend/build/assets/{Background-DISIt7NA.js → Background-C0snDG25.js} +1 -1
  12. package/build/modules/frontend/build/assets/{IconPhoto-Czod_jkW.js → IconPhoto-CTTANJQT.js} +1 -1
  13. package/build/modules/frontend/build/assets/{KeyboardArrowLeft-BUZ_pXI5.js → KeyboardArrowLeft-vg5fS39b.js} +1 -1
  14. package/build/modules/frontend/build/assets/{PictureAsPdfOutlined-BgifaQ0L.js → PictureAsPdfOutlined-B1bBHTk-.js} +1 -1
  15. package/build/modules/frontend/build/assets/{Refresh-3cixHjvW.js → Refresh-BrRXF_OY.js} +1 -1
  16. package/build/modules/frontend/build/assets/emitters-CzPcqIDz.js +1 -0
  17. package/build/modules/frontend/build/assets/{hasRouteMatch-CqJJG3sx.js → hasRouteMatch-D81_5Zia.js} +1 -1
  18. package/build/modules/frontend/build/assets/{html2canvas-Ds2vwzb-.js → html2canvas-CeYsQ7Ia.js} +1 -1
  19. package/build/modules/frontend/build/assets/{index-pxXO4P81.js → index-B8jbArE_.js} +1 -1
  20. package/build/modules/frontend/build/assets/{index-B1plXuf6.js → index-BGch4vTY.js} +1 -1
  21. package/build/modules/frontend/build/assets/index-BNHew1fQ.js +1 -0
  22. package/build/modules/frontend/build/assets/index-BUbFRdFe.js +1 -0
  23. package/build/modules/frontend/build/assets/{index-C8oIDQdm.js → index-CYxbx7rx.js} +1 -1
  24. package/build/modules/frontend/build/assets/{index-BI3k7b1T.js → index-Cgx-XS5d.js} +10 -10
  25. package/build/modules/frontend/build/assets/index-DKA1GI73.js +1 -0
  26. package/build/modules/frontend/build/assets/{index-DwO4-4_F.js → index-DR-YErQ2.js} +1 -1
  27. package/build/modules/frontend/build/assets/{index-BMq2INDq.js → index-D_qcRzkR.js} +1 -1
  28. package/build/modules/frontend/build/assets/{index-VxkdhxAi.js → index-DwTK5Bv3.js} +1 -1
  29. package/build/modules/frontend/build/assets/{index-DJ7B8Och.js → index-JWp04bbN.js} +1 -1
  30. package/build/modules/frontend/build/assets/{index-BYnSX2YX.js → index-lJLcrenO.js} +1 -1
  31. package/build/modules/frontend/build/assets/{index-CA19hrNH.js → index-xjrsOJPu.js} +1 -1
  32. package/build/modules/frontend/build/assets/{index.es-DPOJOdbj.js → index.es-CjcEW2lH.js} +1 -1
  33. package/build/modules/frontend/build/assets/{markdownit-C4X6Sysk.js → markdownit-D4PDy3DR.js} +1 -1
  34. package/build/modules/frontend/build/index.html +87 -87
  35. package/build/modules/frontend/build/manifest.json +23 -23
  36. package/package.json +118 -118
  37. package/build/modules/frontend/build/assets/emitters-DmNf0ttF.js +0 -1
  38. package/build/modules/frontend/build/assets/index-BYiuApxW.js +0 -1
  39. package/build/modules/frontend/build/assets/index-Cqt7co-7.js +0 -1
  40. package/build/modules/frontend/build/assets/index-ruSh1u75.js +0 -1
package/README.md CHANGED
@@ -1,273 +1,273 @@
1
- <img src="https://github.com/tripolskypetr/backtest-kit/raw/refs/heads/master/assets/decart.svg" height="45px" align="right">
2
-
3
- # 📊 @backtest-kit/ui
4
-
5
- > The web cockpit for [backtest-kit](https://www.npmjs.com/package/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.
6
-
7
- ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot16.png)
8
-
9
- [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/tripolskypetr/backtest-kit)
10
- [![npm](https://img.shields.io/npm/v/@backtest-kit/ui.svg?style=flat-square)](https://npmjs.org/package/@backtest-kit/ui)
11
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue)]()
12
-
13
- Interactive dashboard for backtest-kit with signal visualization, candle charts, risk analysis, and notification management. Built with React 18, Material-UI, and Lightweight Charts.
14
-
15
- 📚 **[Backtest Kit Docs](https://backtest-kit.github.io/documents/article_07_ai_news_trading_signals.html)** | 🌟 **[GitHub](https://github.com/tripolskypetr/backtest-kit)**
16
-
17
- > **New here?** The fastest real setup is to clone the [reference implementation](https://github.com/tripolskypetr/backtest-kit/tree/master/example) — a working news-sentiment AI trading system with LLM forecasting, multi-timeframe data, and a documented February 2026 backtest. Start there, not from scratch.
18
-
19
- ```bash
20
- npm install @backtest-kit/ui backtest-kit
21
- ```
22
-
23
- ```typescript
24
- import { serve } from "@backtest-kit/ui";
25
- serve("0.0.0.0", 60050); // → http://localhost:60050
26
- ```
27
-
28
- 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.
29
-
30
- ---
31
-
32
- ## The shell
33
-
34
- ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot19.png)
35
-
36
- 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.
37
-
38
- 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:
39
-
40
- - **Application** — Portfolio Overview, PNL Performance, System Logs
41
- - **Live** — Notifications, Pending Status, Dump Explorer
42
- - **Other** — Markdown Reports, Price Charts, Heatmap
43
-
44
- ---
45
-
46
- ## Portfolio Overview — `/overview`
47
-
48
- ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot1.png)
49
-
50
- Closed trading signals, grouped by symbol, split across **Backtest** and **Live** tabs at the top.
51
-
52
- <details>
53
- <summary>What's on the page</summary>
54
-
55
- 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.
56
-
57
- </details>
58
-
59
- ## PNL Performance (Dashboard) — `/dashboard` · `/dashboard/:mode`
60
-
61
- ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot13.png)
62
-
63
- The KPI board. Aggregates trading performance **across all symbols** for the chosen mode.
64
-
65
- <details>
66
- <summary>What's on the page</summary>
67
-
68
- 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:
69
-
70
- - **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).
71
- - **Trade-performance donut** (`SpeedDonutWidget`) — Failed vs. Successful vs. Total signal counts.
72
- - **Daily-trades chart** (`ChartWidget`) — a per-day series of total / resolved / rejected trades.
73
- - **Success-rate panel** (`SuccessRateWidget`) — per symbol (icon + display name), broken into take-profit / stop-loss / close-resolved / close-rejected counts.
74
- - **Signal grid** (`SignalGridWidget`) — the paginated signal table for the active mode.
75
-
76
- 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.
77
-
78
- </details>
79
-
80
- ## System Logs — `/logs`
81
-
82
- ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot14.png)
83
-
84
- A virtualized, filterable feed of the engine's runtime log.
85
-
86
- <details>
87
- <summary>What's on the page</summary>
88
-
89
- 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.
90
-
91
- </details>
92
-
93
- ## Notifications — `/notifications`
94
-
95
- ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot46.png)
96
-
97
- The event feed for the entire signal lifecycle.
98
-
99
- <details>
100
- <summary>What's on the page</summary>
101
-
102
- 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.
103
-
104
- </details>
105
-
106
- ## Pending Status — `/status` → `/status/:id` → `/status/:id/control`
107
-
108
- ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot18.png)
109
-
110
- A three-level drill-down for *live* positions: list → detail → manual control.
111
-
112
- <details>
113
- <summary>What's on the page</summary>
114
-
115
- **List (`/status`)** — active signals grouped by strategy, rendered as a grid of strategy buttons; click one to inspect it.
116
-
117
- **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."*
118
-
119
- **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.)
120
-
121
- </details>
122
-
123
- ## Markdown Reports — `/report`
124
-
125
- ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot20.png)
126
-
127
- Rendered strategy-performance reports for Backtest and Live runs.
128
-
129
- <details>
130
- <summary>What's on the page</summary>
131
-
132
- 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).
133
-
134
- </details>
135
-
136
- ## Price Charts — `/price_chart` → `/price_chart/:symbol` → `/price_chart/:symbol/:interval`
137
-
138
- ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot33.png)
139
-
140
- Interactive candlesticks powered by TradingView Lightweight Charts.
141
-
142
- <details>
143
- <summary>What's on the page</summary>
144
-
145
- 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`.
146
-
147
- </details>
148
-
149
- ## Heatmap — `/heat`
150
-
151
- ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot31.png)
152
-
153
- A color-coded performance matrix across every tracked symbol.
154
-
155
- <details>
156
- <summary>What's on the page</summary>
157
-
158
- 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.)
159
-
160
- </details>
161
-
162
- ## Dump Explorer — `/dump` · `/dump/:search`
163
-
164
- ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot12.png)
165
-
166
- A file browser for everything the engine wrote to disk.
167
-
168
- <details>
169
- <summary>What's on the page</summary>
170
-
171
- 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`.
172
-
173
- </details>
174
-
175
- ## About & Setup — `/about` · `/about/setup`
176
-
177
- ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot36.png)
178
-
179
- Framework information (`/about`) and a **Setup** view (`/about/setup`) showing the resolved configuration / environment of the running instance.
180
-
181
- ---
182
-
183
- ## Widgets — the reusable pieces screens are built from
184
-
185
- ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot8.png)
186
-
187
- <details>
188
- <summary>The building blocks</summary>
189
-
190
- - **ChartWidget** — Lightweight Charts candlestick/line rendering with signal overlays (Price Charts, signal detail).
191
- - **SignalGridWidget** — the paginated signal table (offset pagination + async item iterator).
192
- - **StatusWidget** — live workload status tiles.
193
- - **SuccessRateWidget** / **SpeedDonutWidget** — donut gauges for win-rate and throughput.
194
- - **SingleValueWidget** / **IndicatorValueWidget** — single-KPI and indicator-value cards.
195
- - **AveragingWidget** — DCA-ladder / effective-price visualization.
196
- - **PartialWidget** — partial-close breakdown.
197
- - **FileTreeWidget** — the dump-explorer tree.
198
-
199
- 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.
200
-
201
- </details>
202
-
203
- ## The modal system — a window manager with navigation history
204
-
205
- ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot26.png)
206
-
207
- The dashboard is, at its core, a **modal window manager**. Two complementary mechanisms sit on top of [react-declarative](https://github.com/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.
208
-
209
- ### 1. Global modal registry — tabbed detail modals on a managed stack
210
-
211
- 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)`) `push`es 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 `pop`s 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.
212
-
213
- 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.
214
-
215
- <details>
216
- <summary>What's registered (every modal hook)</summary>
217
-
218
- 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:
219
-
220
- | Hook | Opens a modal for |
221
- |---|---|
222
- | `useSignalView` | a generic signal (`signal_fields`) |
223
- | `useRiskView` | a risk rejection (`risk_fields`) |
224
- | `useSignalOpenedView` / `useSignalClosedView` / `useSignalScheduledView` / `useSignalCancelledView` | the four signal-notification kinds |
225
- | `useSignalNotifyView` | an info notification |
226
- | `useSignalSyncOpenView` / `useSignalSyncCloseView` | broker-sync open/close |
227
- | `useActivateScheduledView` | a scheduled signal activating |
228
- | `useAverageBuyCommitView` | a DCA-rung commit |
229
- | `useClosePendingView` / `useCancelScheduledView` | close / cancel commits |
230
- | `usePartialProfitAvailableView` / `usePartialProfitCommitView` | partial-profit available + committed |
231
- | `usePartialLossAvailableView` / `usePartialLossCommitView` | partial-loss available + committed |
232
- | `useBreakevenAvailableView` / `useBreakevenCommitView` | breakeven available + committed |
233
- | `useTrailingStopView` / `useTrailingTakeView` | trailing stop / take |
234
- | `useDumpContentView` | a dump-file preview |
235
-
236
- 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.
237
-
238
- </details>
239
-
240
- ### 2. Manual-control wizard modals (per-operation, own history)
241
-
242
- 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.
243
-
244
- <details>
245
- <summary>The wizard anatomy (shared by all four)</summary>
246
-
247
- 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.
248
-
249
- The three steps (Open Position example):
250
-
251
- 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.
252
- 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`.
253
- 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.
254
-
255
- Average Buy, Close Pending, and Breakeven follow the same Brief→Form→Submit shape with operation-specific form fields and StatusCard copy.
256
-
257
- </details>
258
-
259
- ## Field schemas (`assets/*_fields.tsx`) — every DTO rendered as a real form
260
-
261
- 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.
262
-
263
- <details>
264
- <summary>The schemas and who imports them</summary>
265
-
266
- - **`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).
267
- - **`signal_fields`** (→ `StatusView` detail + print, `useSignalView`) and **`status_fields`** (→ `StatusView`) — the full pending/closed signal record.
268
- - **`risk_fields`** (→ `useRiskView`), **`setup_fields`** (→ About/Setup view).
269
- - **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`.
270
-
271
- 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.
272
-
273
- </details>
1
+ <img src="https://github.com/tripolskypetr/backtest-kit/raw/refs/heads/master/assets/decart.svg" height="45px" align="right">
2
+
3
+ # 📊 @backtest-kit/ui
4
+
5
+ > The web cockpit for [backtest-kit](https://www.npmjs.com/package/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.
6
+
7
+ ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot16.png)
8
+
9
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/tripolskypetr/backtest-kit)
10
+ [![npm](https://img.shields.io/npm/v/@backtest-kit/ui.svg?style=flat-square)](https://npmjs.org/package/@backtest-kit/ui)
11
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue)]()
12
+
13
+ Interactive dashboard for backtest-kit with signal visualization, candle charts, risk analysis, and notification management. Built with React 18, Material-UI, and Lightweight Charts.
14
+
15
+ 📚 **[Backtest Kit Docs](https://backtest-kit.github.io/documents/article_07_ai_news_trading_signals.html)** | 🌟 **[GitHub](https://github.com/tripolskypetr/backtest-kit)**
16
+
17
+ > **New here?** The fastest real setup is to clone the [reference implementation](https://github.com/tripolskypetr/backtest-kit/tree/master/example) — a working news-sentiment AI trading system with LLM forecasting, multi-timeframe data, and a documented February 2026 backtest. Start there, not from scratch.
18
+
19
+ ```bash
20
+ npm install @backtest-kit/ui backtest-kit
21
+ ```
22
+
23
+ ```typescript
24
+ import { serve } from "@backtest-kit/ui";
25
+ serve("0.0.0.0", 60050); // → http://localhost:60050
26
+ ```
27
+
28
+ 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.
29
+
30
+ ---
31
+
32
+ ## The shell
33
+
34
+ ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot19.png)
35
+
36
+ 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.
37
+
38
+ 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:
39
+
40
+ - **Application** — Portfolio Overview, PNL Performance, System Logs
41
+ - **Live** — Notifications, Pending Status, Dump Explorer
42
+ - **Other** — Markdown Reports, Price Charts, Heatmap
43
+
44
+ ---
45
+
46
+ ## Portfolio Overview — `/overview`
47
+
48
+ ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot1.png)
49
+
50
+ Closed trading signals, grouped by symbol, split across **Backtest** and **Live** tabs at the top.
51
+
52
+ <details>
53
+ <summary>What's on the page</summary>
54
+
55
+ 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.
56
+
57
+ </details>
58
+
59
+ ## PNL Performance (Dashboard) — `/dashboard` · `/dashboard/:mode`
60
+
61
+ ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot13.png)
62
+
63
+ The KPI board. Aggregates trading performance **across all symbols** for the chosen mode.
64
+
65
+ <details>
66
+ <summary>What's on the page</summary>
67
+
68
+ 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:
69
+
70
+ - **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).
71
+ - **Trade-performance donut** (`SpeedDonutWidget`) — Failed vs. Successful vs. Total signal counts.
72
+ - **Daily-trades chart** (`ChartWidget`) — a per-day series of total / resolved / rejected trades.
73
+ - **Success-rate panel** (`SuccessRateWidget`) — per symbol (icon + display name), broken into take-profit / stop-loss / close-resolved / close-rejected counts.
74
+ - **Signal grid** (`SignalGridWidget`) — the paginated signal table for the active mode.
75
+
76
+ 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.
77
+
78
+ </details>
79
+
80
+ ## System Logs — `/logs`
81
+
82
+ ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot14.png)
83
+
84
+ A virtualized, filterable feed of the engine's runtime log.
85
+
86
+ <details>
87
+ <summary>What's on the page</summary>
88
+
89
+ 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.
90
+
91
+ </details>
92
+
93
+ ## Notifications — `/notifications`
94
+
95
+ ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot46.png)
96
+
97
+ The event feed for the entire signal lifecycle.
98
+
99
+ <details>
100
+ <summary>What's on the page</summary>
101
+
102
+ 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.
103
+
104
+ </details>
105
+
106
+ ## Pending Status — `/status` → `/status/:id` → `/status/:id/control`
107
+
108
+ ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot18.png)
109
+
110
+ A three-level drill-down for *live* positions: list → detail → manual control.
111
+
112
+ <details>
113
+ <summary>What's on the page</summary>
114
+
115
+ **List (`/status`)** — active signals grouped by strategy, rendered as a grid of strategy buttons; click one to inspect it.
116
+
117
+ **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."*
118
+
119
+ **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.)
120
+
121
+ </details>
122
+
123
+ ## Markdown Reports — `/report`
124
+
125
+ ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot20.png)
126
+
127
+ Rendered strategy-performance reports for Backtest and Live runs.
128
+
129
+ <details>
130
+ <summary>What's on the page</summary>
131
+
132
+ 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).
133
+
134
+ </details>
135
+
136
+ ## Price Charts — `/price_chart` → `/price_chart/:symbol` → `/price_chart/:symbol/:interval`
137
+
138
+ ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot33.png)
139
+
140
+ Interactive candlesticks powered by TradingView Lightweight Charts.
141
+
142
+ <details>
143
+ <summary>What's on the page</summary>
144
+
145
+ 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`.
146
+
147
+ </details>
148
+
149
+ ## Heatmap — `/heat`
150
+
151
+ ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot31.png)
152
+
153
+ A color-coded performance matrix across every tracked symbol.
154
+
155
+ <details>
156
+ <summary>What's on the page</summary>
157
+
158
+ 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.)
159
+
160
+ </details>
161
+
162
+ ## Dump Explorer — `/dump` · `/dump/:search`
163
+
164
+ ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot12.png)
165
+
166
+ A file browser for everything the engine wrote to disk.
167
+
168
+ <details>
169
+ <summary>What's on the page</summary>
170
+
171
+ 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`.
172
+
173
+ </details>
174
+
175
+ ## About & Setup — `/about` · `/about/setup`
176
+
177
+ ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot36.png)
178
+
179
+ Framework information (`/about`) and a **Setup** view (`/about/setup`) showing the resolved configuration / environment of the running instance.
180
+
181
+ ---
182
+
183
+ ## Widgets — the reusable pieces screens are built from
184
+
185
+ ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot8.png)
186
+
187
+ <details>
188
+ <summary>The building blocks</summary>
189
+
190
+ - **ChartWidget** — Lightweight Charts candlestick/line rendering with signal overlays (Price Charts, signal detail).
191
+ - **SignalGridWidget** — the paginated signal table (offset pagination + async item iterator).
192
+ - **StatusWidget** — live workload status tiles.
193
+ - **SuccessRateWidget** / **SpeedDonutWidget** — donut gauges for win-rate and throughput.
194
+ - **SingleValueWidget** / **IndicatorValueWidget** — single-KPI and indicator-value cards.
195
+ - **AveragingWidget** — DCA-ladder / effective-price visualization.
196
+ - **PartialWidget** — partial-close breakdown.
197
+ - **FileTreeWidget** — the dump-explorer tree.
198
+
199
+ 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.
200
+
201
+ </details>
202
+
203
+ ## The modal system — a window manager with navigation history
204
+
205
+ ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot26.png)
206
+
207
+ The dashboard is, at its core, a **modal window manager**. Two complementary mechanisms sit on top of [react-declarative](https://github.com/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.
208
+
209
+ ### 1. Global modal registry — tabbed detail modals on a managed stack
210
+
211
+ 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)`) `push`es 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 `pop`s 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.
212
+
213
+ 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.
214
+
215
+ <details>
216
+ <summary>What's registered (every modal hook)</summary>
217
+
218
+ 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:
219
+
220
+ | Hook | Opens a modal for |
221
+ |---|---|
222
+ | `useSignalView` | a generic signal (`signal_fields`) |
223
+ | `useRiskView` | a risk rejection (`risk_fields`) |
224
+ | `useSignalOpenedView` / `useSignalClosedView` / `useSignalScheduledView` / `useSignalCancelledView` | the four signal-notification kinds |
225
+ | `useSignalNotifyView` | an info notification |
226
+ | `useSignalSyncOpenView` / `useSignalSyncCloseView` | broker-sync open/close |
227
+ | `useActivateScheduledView` | a scheduled signal activating |
228
+ | `useAverageBuyCommitView` | a DCA-rung commit |
229
+ | `useClosePendingView` / `useCancelScheduledView` | close / cancel commits |
230
+ | `usePartialProfitAvailableView` / `usePartialProfitCommitView` | partial-profit available + committed |
231
+ | `usePartialLossAvailableView` / `usePartialLossCommitView` | partial-loss available + committed |
232
+ | `useBreakevenAvailableView` / `useBreakevenCommitView` | breakeven available + committed |
233
+ | `useTrailingStopView` / `useTrailingTakeView` | trailing stop / take |
234
+ | `useDumpContentView` | a dump-file preview |
235
+
236
+ 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.
237
+
238
+ </details>
239
+
240
+ ### 2. Manual-control wizard modals (per-operation, own history)
241
+
242
+ 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.
243
+
244
+ <details>
245
+ <summary>The wizard anatomy (shared by all four)</summary>
246
+
247
+ 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.
248
+
249
+ The three steps (Open Position example):
250
+
251
+ 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.
252
+ 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`.
253
+ 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.
254
+
255
+ Average Buy, Close Pending, and Breakeven follow the same Brief→Form→Submit shape with operation-specific form fields and StatusCard copy.
256
+
257
+ </details>
258
+
259
+ ## Field schemas (`assets/*_fields.tsx`) — every DTO rendered as a real form
260
+
261
+ 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.
262
+
263
+ <details>
264
+ <summary>The schemas and who imports them</summary>
265
+
266
+ - **`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).
267
+ - **`signal_fields`** (→ `StatusView` detail + print, `useSignalView`) and **`status_fields`** (→ `StatusView`) — the full pending/closed signal record.
268
+ - **`risk_fields`** (→ `useRiskView`), **`setup_fields`** (→ About/Setup view).
269
+ - **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`.
270
+
271
+ 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.
272
+
273
+ </details>
package/build/index.cjs CHANGED
@@ -62,6 +62,15 @@ router$c.get("/api/v1/health/health_check", async (req, res) => {
62
62
  });
63
63
  });
64
64
 
65
+ const omitValue = (value, keys) => {
66
+ if (functoolsKit.isObject(value)) {
67
+ return omit(value, ...keys);
68
+ }
69
+ if (Array.isArray(value)) {
70
+ return value.map((item) => omitValue(item, keys));
71
+ }
72
+ return value;
73
+ };
65
74
  function omit(obj, ...keys) {
66
75
  const keySet = new Set(keys);
67
76
  return Object.keys(obj).reduce((acc, key) => {
@@ -69,12 +78,7 @@ function omit(obj, ...keys) {
69
78
  return acc;
70
79
  }
71
80
  const value = obj[key];
72
- if (functoolsKit.isObject(value)) {
73
- acc[key] = omit(value, ...keys);
74
- }
75
- else {
76
- acc[key] = value;
77
- }
81
+ acc[key] = omitValue(value, keys);
78
82
  return acc;
79
83
  }, {});
80
84
  }
@@ -1664,7 +1668,13 @@ class ExplorerViewService {
1664
1668
  if (!absPath.startsWith(dir + path.sep) && !absPath.startsWith(dir + "/") && absPath !== dir) {
1665
1669
  throw new Error(`Path is outside of dump dir: ${nodePath}`);
1666
1670
  }
1667
- return await fs.readFile(absPath, "utf-8");
1671
+ // Симлинк внутри dump может указывать наружу — сверяем реальный путь
1672
+ const realDir = await fs.realpath(dir);
1673
+ const realPath = await fs.realpath(absPath);
1674
+ if (!realPath.startsWith(realDir + path.sep) && !realPath.startsWith(realDir + "/") && realPath !== realDir) {
1675
+ throw new Error(`Path is outside of dump dir: ${nodePath}`);
1676
+ }
1677
+ return await fs.readFile(realPath, "utf-8");
1668
1678
  };
1669
1679
  this.getTree = async () => {
1670
1680
  this.loggerService.log("explorerViewService getTree");
@@ -1874,7 +1884,12 @@ class SymbolConnectionService {
1874
1884
  symbol,
1875
1885
  icon,
1876
1886
  logo: logo ?? icon,
1877
- priority: priority ?? idx,
1887
+ // -idx (not idx): with the sort's ascending-index tiebreak this keeps
1888
+ // symbols WITHOUT an explicit priority in their original list order.
1889
+ // Must match cli FrontendProviderService.MAP_SYMBOL_CONFIG_FN, which
1890
+ // feeds the same UI dropdown when it injects the symbol list itself;
1891
+ // plain `idx` reversed the fallback order relative to that path.
1892
+ priority: priority ?? -idx,
1878
1893
  displayName: displayName ?? symbol,
1879
1894
  index: idx,
1880
1895
  ...other,
@@ -5539,6 +5554,11 @@ const serveInternal = functoolsKit.singleshot((host = CC_WWWROOT_HOST, port = CC
5539
5554
  });
5540
5555
  server.addListener("error", (err) => {
5541
5556
  console.error("Server error:", err);
5557
+ // Сервер не поднялся — сбрасываем singleshot, иначе повторный serve()
5558
+ // вернёт мёртвый инстанс и слушать порт больше никто не попробует
5559
+ if (!server.listening) {
5560
+ serveInternal.clear();
5561
+ }
5542
5562
  callback && callback(err);
5543
5563
  });
5544
5564
  server.maxConnections = MAX_CONNECTIONS;