@tradecanvas/chart 1.19.0 → 1.19.1

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.
package/README.md CHANGED
@@ -6,10 +6,12 @@
6
6
  <a href="https://www.npmjs.com/package/@tradecanvas/chart"><img src="https://img.shields.io/npm/v/@tradecanvas/chart?style=flat-square&labelColor=0b0e13&color=f2a93b&label=npm" alt="npm version"></a>
7
7
  <a href="https://www.npmjs.com/package/@tradecanvas/chart"><img src="https://img.shields.io/npm/dm/@tradecanvas/chart?style=flat-square&labelColor=0b0e13&color=3ccf91" alt="npm downloads"></a>
8
8
  <a href="https://github.com/bonguynvan/tradecanvas/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/bonguynvan/tradecanvas/ci.yml?branch=main&style=flat-square&labelColor=0b0e13&label=CI" alt="CI status"></a>
9
+ <a href="https://pkg.pr.new/~/bonguynvan/tradecanvas"><img src="https://pkg.pr.new/badge/bonguynvan/tradecanvas" alt="pkg.pr.new previews"></a>
9
10
  <img src="https://img.shields.io/badge/third--party%20deps-0-3ccf91?style=flat-square&labelColor=0b0e13" alt="No third-party dependencies">
10
11
  <img src="https://img.shields.io/badge/TypeScript-strict-4c8dff?style=flat-square&labelColor=0b0e13" alt="TypeScript">
11
12
  <a href="https://github.com/bonguynvan/tradecanvas/blob/main/LICENSE"><img src="https://img.shields.io/github/license/bonguynvan/tradecanvas?style=flat-square&labelColor=0b0e13&color=a9b0bd" alt="MIT license"></a>
12
13
  <a href="https://github.com/bonguynvan/tradecanvas/stargazers"><img src="https://img.shields.io/github/stars/bonguynvan/tradecanvas?style=flat-square&labelColor=0b0e13&color=f2a93b" alt="GitHub stars"></a>
14
+ <a href="https://github.com/sponsors/bonguynvan"><img src="https://img.shields.io/github/sponsors/bonguynvan?style=flat-square&labelColor=0b0e13&color=db61a2&label=sponsors&logo=githubsponsors&logoColor=db61a2" alt="GitHub Sponsors"></a>
13
15
  </p>
14
16
 
15
17
  <p align="center">
@@ -25,25 +27,38 @@
25
27
  <b>English</b> · <a href="https://github.com/bonguynvan/tradecanvas/blob/main/README.vi.md">Tiếng Việt</a> · <a href="https://github.com/bonguynvan/tradecanvas/blob/main/README.zh-CN.md">简体中文</a> · <a href="https://github.com/bonguynvan/tradecanvas/blob/main/README.ja.md">日本語</a> · <a href="https://github.com/bonguynvan/tradecanvas/blob/main/README.ko.md">한국어</a> · <a href="https://github.com/bonguynvan/tradecanvas/blob/main/README.es.md">Español</a>
26
28
  </p>
27
29
 
28
- **A complete trading chart for the web.** Candlesticks to Renko, 111 indicators, 69 drawing tools, live exchange feeds and orders on the chart, drawn with Canvas 2D or WebGL, with zero dependencies. Drop in the full `ChartWidget`, or build your own UI on the headless `Chart`, in plain TypeScript, React, Vue or Svelte.
30
+ **A complete trading chart for the web.** Candlesticks to Renko, 111 indicators, 69 drawing tools, live exchange feeds and orders on the chart, drawn with Canvas 2D or WebGL and free of third-party dependencies. Drop in the full `ChartWidget`, or build your own interface on the headless `Chart`, in TypeScript, React, Vue or Svelte.
29
31
 
30
32
  <p align="center">
31
33
  <a href="https://bonguynvan.github.io/tradecanvas/"><img src="https://raw.githubusercontent.com/bonguynvan/tradecanvas/main/.github/assets/hero.png" alt="ChartWidget with live BTCUSDT from Binance: EMA 21 and 55, RSI, a trend line, a long position and a watchlist with live quotes" width="100%"></a>
32
34
  </p>
33
35
 
34
- ## Quick Start
36
+ ## Highlights
37
+
38
+ - **18 chart types**, from candles and Heikin-Ashi to Renko, Kagi, point & figure and Equivolume, plus six finance charts (sparkline, depth, equity, heatmap, waterfall, gauge).
39
+ - **111 indicators and 69 drawing tools**: indicators on indicators, alerts on trend lines, templates, groups, undo and redo.
40
+ - **Trading on the chart**: positions with live P&L, orders and stop-loss/take-profit you drag, an order ticket, an account panel, and a paper broker or your own execution adapter.
41
+ - **Live data**: Binance, Bybit, Coinbase and Kraken built in, any other feed in about twenty lines; older history while scrolling back, any interval, tick charts.
42
+ - **Canvas 2D or WebGL**: a million bars panned at 60 fps, crisp at every screen scale.
43
+ - **A full workspace**: multi-chart grids, named layouts, bar replay with paper trading, and a backtester in `@tradecanvas/analytics`.
44
+ - **Yours to shape**: 101 style keys, 112 feature switches, three looks, and plugins for indicators, drawing tools, chart types and overlays.
45
+ - **For everyone**: the widget in 30 languages, right to left included, with screen reader and keyboard support.
46
+
47
+ ## Install
35
48
 
36
49
  ```bash
37
- npm install @tradecanvas/chart # or: pnpm add / yarn add
50
+ npm install @tradecanvas/chart
38
51
  ```
39
52
 
40
- `ChartWidget` is the whole trading UI in one component: toolbar, drawing sidebar, settings dialog and status bar.
53
+ ## Quick start
54
+
55
+ `ChartWidget` is the whole trading interface in one call: toolbar, drawing sidebar, settings, status bar.
41
56
 
42
57
  ```typescript
43
58
  import { ChartWidget } from '@tradecanvas/chart/widget'
44
59
  import { BinanceAdapter } from '@tradecanvas/chart'
45
60
 
46
- const widget = new ChartWidget(document.getElementById('chart')!, {
61
+ new ChartWidget(document.getElementById('chart')!, {
47
62
  symbol: 'BTCUSDT',
48
63
  timeframe: '5m',
49
64
  theme: 'dark',
@@ -52,1125 +67,126 @@ const widget = new ChartWidget(document.getElementById('chart')!, {
52
67
  })
53
68
  ```
54
69
 
55
- That's it. Live data, all 111 indicators, all 69 drawing tools, command palette (`Ctrl+K`), symbol search (`Ctrl+P`), hotkey sheet (`?`), shift-drag measure, alt-click tooltip pin, and drag-drop CSV/JSON loading.
70
+ To own the interface, use the headless `Chart` and its API:
56
71
 
57
- Using a framework? [`@tradecanvas/react`](https://github.com/bonguynvan/tradecanvas/tree/main/packages/react/), [`@tradecanvas/vue`](https://github.com/bonguynvan/tradecanvas/tree/main/packages/vue/) and [`@tradecanvas/svelte`](https://github.com/bonguynvan/tradecanvas/tree/main/packages/svelte/) wrap the headless `Chart` as a component, and the widget above mounts the same way in any framework; see [Framework Integration](#framework-integration). Or fork a [StackBlitz sandbox](https://bonguynvan.github.io/tradecanvas/examples/) and start from there.
72
+ ```typescript
73
+ import { Chart } from '@tradecanvas/chart'
74
+
75
+ const chart = new Chart(document.getElementById('chart')!, { theme: 'dark' })
76
+ chart.setData(bars) // [{ time, open, high, low, close, volume }, …]
77
+ chart.addIndicator('ema', { period: 50 })
78
+ chart.addIndicator('rsi')
79
+ ```
58
80
 
59
- ## A Look Around
81
+ Your own feed plugs in through `WebSocketAdapter` or `PollingAdapter`; see [Realtime](https://bonguynvan.github.io/tradecanvas/docs/realtime/).
82
+
83
+ ## Frameworks
84
+
85
+ ```bash
86
+ npm install @tradecanvas/react # or @tradecanvas/vue, @tradecanvas/svelte
87
+ ```
88
+
89
+ ```tsx
90
+ import { TradeCanvas } from '@tradecanvas/react'
91
+
92
+ <TradeCanvas symbol="BTCUSDT" timeframe="5m" indicators={['rsi', { id: 'ema', params: { period: 50 } }]} />
93
+ ```
94
+
95
+ The three components share the same props and hand you the underlying `Chart`. See [Frameworks](https://bonguynvan.github.io/tradecanvas/docs/frameworks/).
96
+
97
+ ## A look around
60
98
 
61
99
  <table>
62
100
  <tr>
63
101
  <td width="50%" valign="top">
64
102
  <a href="https://raw.githubusercontent.com/bonguynvan/tradecanvas/main/.github/assets/drawings.png"><img src="https://raw.githubusercontent.com/bonguynvan/tradecanvas/main/.github/assets/drawings.png" alt="Drawing tools: a Fibonacci retracement with a note, a trend line, an Elliott impulse wave and a long position"></a>
65
103
  <br><b>69 drawing tools</b><br>
66
- Fibonacci, Gann, pitchforks, Elliott waves, harmonic patterns, notes and brushes, and a Long/Short position that sizes the trade. Alerts on trend lines, groups, undo and redo.
104
+ Fibonacci, Gann, pitchforks, Elliott waves, harmonic patterns, notes, and a long/short position that sizes the trade.
67
105
  </td>
68
106
  <td width="50%" valign="top">
69
107
  <a href="https://raw.githubusercontent.com/bonguynvan/tradecanvas/main/.github/assets/trading.png"><img src="https://raw.githubusercontent.com/bonguynvan/tradecanvas/main/.github/assets/trading.png" alt="Trading on the chart: a long position with stop-loss and take-profit, a buy stop and a sell limit, and the account panel"></a>
70
108
  <br><b>Trading on the chart</b><br>
71
- Positions with live P&amp;L, orders you drag to a new price, SL and TP, reverse and close buttons, an order ticket and an account panel. A paper broker is built in; connect your own.
109
+ Positions with live P&amp;L, orders you drag to a new price, an order ticket and an account panel.
72
110
  </td>
73
111
  </tr>
74
112
  <tr>
75
113
  <td width="50%" valign="top">
76
114
  <a href="https://raw.githubusercontent.com/bonguynvan/tradecanvas/main/.github/assets/grid.png"><img src="https://raw.githubusercontent.com/bonguynvan/tradecanvas/main/.github/assets/grid.png" alt="A two-by-two workspace of BTC, ETH, SOL and BNB charts on mixed intervals"></a>
77
115
  <br><b>Multi-chart workspace</b><br>
78
- Up to six full charts side by side, linked by symbol, interval, crosshair, time or drawings, and saved as one layout.
116
+ Up to six charts side by side, linked by symbol, interval, crosshair, time or drawings.
79
117
  </td>
80
118
  <td width="50%" valign="top">
81
119
  <a href="https://raw.githubusercontent.com/bonguynvan/tradecanvas/main/.github/assets/looks.png"><img src="https://raw.githubusercontent.com/bonguynvan/tradecanvas/main/.github/assets/looks.png" alt="One chart in three looks: studio, terminal, and capsule on the light theme"></a>
82
120
  <br><b>Your own look</b><br>
83
- Three presets (studio, terminal, capsule) or your own corner radius, density, fonts, toolbar and price tags, on light and dark themes.
121
+ Three presets or your own corners, density, fonts and price tags, on light and dark themes.
84
122
  </td>
85
123
  </tr>
86
124
  </table>
87
125
 
88
- ## Why TradeCanvas?
89
-
90
- Most chart libraries make you choose: pretty charts with no trading features, or trading features with an ugly API. TradeCanvas gives you both.
91
-
92
- - **111 built-in indicators** — SMA, EMA, TEMA, VWMA, Hull MA, RSI, MACD, Bollinger, Envelope, Ichimoku, Pivot Points, Anchored VWAP, ZigZag, Linear Regression Channel, Awesome / Chaikin Oscillator, and more. Any indicator can read another one's line (an SMA of RSI). No separate calculation library needed.
93
- - **69 drawing tools** — Trendlines (info line, trend angle, cross line), Fibonacci (retracement, extension, channel, time zones, speed resistance fan and arcs, circles, spiral, wedge), horizontal/vertical lines, channels, pitchforks and pitchfan, Gann fan / box / square, cycles, harmonic patterns (XABCD, cypher, ABCD, three drives, head and shoulders), Elliott waves, notes, callouts and marks, brush and path, forecast and projection, Long/Short Position with position sizing, Volume Profile range. Each with its own settings, alerts on trend lines, groups and layers, undo/redo and full serialization.
94
- - **18 chart types** — Candlestick, line, area, bar, hollow candle, baseline, High-Low, Heikin-Ashi, Renko, Kagi, Line Break, Point & Figure, Range Bars, Volume Candles, **Equivolume**, HLC Area, Step Line, Line+Markers. Renko's box, Kagi's reversal and the like are yours to set.
95
- - **Pro-grade interaction** — pan freely past the last bar into empty future space (drawings can go there too), drag the price/time axes to scale, double-click to auto-fit, `Ctrl/⌘+drag` to select several drawings (then move, restyle or delete them together), `Shift+drag` to measure (bars × price Δ × %), `Alt+click` to pin a comparison tooltip, context cursors (crosshair, grabbing hand, resize arrows), axis-following price/time pill labels under the cursor, bar-hover highlight.
96
- - **Trading overlay** — Render open positions with entry line, P&L zone, and SL/TP markers. Orders as dashed lines. Drag SL/TP to modify, cancel / close / reverse from the buttons on each line, and see every fill marked on its bar. ChartWidget adds an order ticket that checks the order as you fill it in, and an account panel with positions, working orders and history. Cleanly opt-out via `features.trading: false` for non-trading projects.
97
- - **Real-time streaming** — Built-in Binance, Coinbase, Bybit, and Kraken adapters, plus generic `WebSocketAdapter` / `PollingAdapter` bases so any feed plugs in with ~20 lines. Older bars load as you scroll back, any interval (`7m`, `90m`, `2d`) is built from the feed's own, tick charts (`100T`) from its trades, and symbol search and quotes come from the feed.
98
- - **Time zones** — any IANA zone with daylight saving time (`'America/New_York'`), a fixed offset, or the exchange's own zone, for the axis, crosshair, day breaks and session hours.
99
- - **30 languages** — `ChartWidget` in English, Vietnamese, Simplified and Traditional Chinese, Japanese, Korean, Spanish, Portuguese, French, German, Italian, Dutch, Polish, Czech, Slovak, Hungarian, Romanian, Greek, Swedish, Danish, Norwegian, Estonian, Russian, Turkish, Indonesian, Malay, Thai, Arabic, Hebrew and Persian; Arabic, Hebrew and Persian mirror right to left.
100
- - **Accessible** — keyboard navigation, zoom and scroll buttons over the chart, and a screen-reader summary that reads the view and the bars one at a time.
101
- - **Live execution** — connect an `ExecutionAdapter` to turn the trading overlay into a real trading surface, drag on the chart to create orders, and reconcile fills. Ships a `PaperExecutionAdapter` sandbox.
102
- - **Plugin SDK** — register custom indicators, drawing tools, chart types, and overlays — globally or per-chart.
103
- - **Strategy backtester** — `@tradecanvas/analytics` ships a bar-by-bar `Backtester` with virtual fills, commission/slippage models, portfolio tracking, and risk metrics (Sharpe, Sortino, Calmar, max drawdown). **Now with 4 ready-to-use reference strategies + Monte Carlo path-dependence analysis.**
104
- - **Replay mode** — replay the chart's own bars from any point, in finer steps if you like (an hourly chart forming from 5-minute bars), with play / pause / step / seek / speed, and paper-trade on the replayed prices. The widget has a replay bar for it; `ReplayController` drives bars headless too.
105
- - **Alerts** — on a price level, an indicator line, a drawing, or one line crossing another; on a move of some percent within some bars; only on closed bars; with an expiry. The widget's alerts panel sets all of them.
106
- - **Compare and spread** — other symbols in percent on the price scale, on a scale or pane of their own, or as a spread or ratio, lined up with the chart by time.
107
- - **Price formats** — prices in your own format or in fractions of a point (a bond in 32nds: 110'165) on every label; times your way; extended hours on or off; data export with the indicator lines.
108
- - **Volume Profile** — optional horizontal histogram of traded volume bucketed by price over the visible range, with point-of-control highlighting.
109
- - **Watchlists and symbol info** — lists of symbols to switch between, edit and reorder, with live quotes; a symbol panel with price, the market's status, the day's numbers, hours and news.
110
- - **CSV / JSON drag-and-drop** — drop a file onto the chart, it parses and loads instantly. Detects header layouts, ISO/unix-s/unix-ms timestamps, and array-vs-object JSON shapes.
111
- - **Named layouts** — save the chart under a name (symbol, interval, scale, indicators, drawings, alerts), open, rename, delete, auto-save the open one, `Ctrl/⌘+S`. Kept in the browser, or on your server through a four-call `LayoutStorage`. Per-symbol auto-persistence (`persistLayouts`) is there too.
112
- - **Multi-chart** — `ChartWidgetGrid` puts up to six full widgets side by side, linked by symbol, interval, crosshair, time or drawings as you choose, and saves them as one layout. `ChartGrid` does the same for bare charts.
113
- - **Signal markers & trade zones** — render bot/algorithm output (directional arrows, entry→exit rectangles) as a first-class chart layer.
114
- - **Hotkey sheet** — press `?` in the widget to open a categorized keyboard-shortcut reference.
115
- - **Extensible widget** — add your own toolbar buttons and right-click menu entries (`addToolbarButton`, `chartMenuItems`).
116
- - **Save/load chart state** — Persist drawings, indicators, theme, and chart type to JSON. Restore with one call.
117
- - **Zero dependencies** — The entire library is self-contained. No `d3`, no `chart.js`, no `fancy-canvas`.
118
-
119
- ## Headless Chart
120
-
121
- For projects that want to own the surrounding UI (custom toolbar, framework-specific controls), use the lower-level `Chart` class directly:
122
-
123
- ```typescript
124
- import { Chart, BinanceAdapter } from '@tradecanvas/chart'
125
-
126
- const chart = new Chart(document.getElementById('chart')!, {
127
- theme: 'dark',
128
- autoScale: true,
129
- features: {
130
- drawings: true,
131
- indicators: true,
132
- trading: true, // set false to disable orders/positions entirely
133
- tradingContextMenu: true, // opt-in right-click order menu (off by default)
134
- volume: true,
135
- },
136
- })
137
-
138
- const adapter = new BinanceAdapter()
139
- chart.connect({ adapter, symbol: 'BTCUSDT', timeframe: '5m', historyLimit: 300 })
140
- ```
141
-
142
- ### Widget Options
143
-
144
- | Option | Type | Default | Description |
145
- |---|---|---|---|
146
- | `symbol` | `string` | `'BTCUSDT'` | Initial trading symbol |
147
- | `timeframe` | `TimeFrame` | `'5m'` | Initial timeframe |
148
- | `theme` | `'dark' \| 'light' \| Theme` | `'dark'` | Chart theme |
149
- | `adapter` | `DataAdapter` | — | Data source adapter |
150
- | `toolbar` | `boolean` | `true` | Show top toolbar |
151
- | `drawingTools` | `boolean` | `true` | Show left drawing sidebar |
152
- | `settings` | `boolean` | `true` | Show settings button |
153
- | `trading` | `boolean` | `true` | Enable trading overlay |
154
- | `statusBar` | `boolean` | `true` | Show bottom status bar |
155
- | `rangeBar` | `boolean` | `true` | Range presets (1D … All) and go to date (Alt+G) on the status bar |
156
- | `indicatorLegend` | `boolean` | `true` | Indicators listed on the chart (under the OHLCV legend and atop their panes) with show / settings / remove |
157
- | `fullscreen` | `boolean` | `true` | Fullscreen button in the toolbar |
158
- | `features` | `WidgetFeatures` | all on | 112 switches for the widget's parts, changeable while running — see **Widget switches and your own parts** |
159
- | `symbols` | `string[]` | BTC/ETH/SOL/BNB | Searchable symbol catalog |
160
- | `timeframes` | `TimeFrame[]` | 1m to 1M | Timeframes on offer; pin favourites from the ▾ menu |
161
- | `chartTypes` | `ChartType[]` | 18 types | Available chart types |
162
- | `watchlist` | `boolean` | `false` | Right-side watchlist sidebar |
163
- | `dragDropImport` | `boolean` | `true` | Drop CSV / JSON files onto the chart to load data |
164
- | `persistLayouts` | `boolean \| { keyPrefix, debounceMs }` | `false` | Save per-symbol indicators / drawings / chart type to localStorage |
165
- | `onSymbolChange` | `(symbol) => void` | — | Symbol change callback |
166
- | `onTimeframeChange` | `(tf) => void` | — | Timeframe change callback |
167
- | `onReady` | `(chart) => void` | — | Fired when chart is ready |
168
- | `locale` | `string` | `'en'` | UI language — `'en'` and `'vi'` built in, 12 more from the locales entry, see **Widget i18n** below |
169
- | `messages` | `Partial<Record<MessageKey, string>>` | — | Override or add individual UI strings on top of `locale` |
170
-
171
- ### Icons
172
-
173
- The widget's icon set is exported for your own UI: `createIcon(name)`,
174
- `createToolIcon(drawingTool)`, `createChartTypeIcon(chartType)` return inline
175
- SVG strings drawn in `currentColor` (24 px grid, 1.75 px strokes).
176
-
177
- ```ts
178
- import { createToolIcon } from '@tradecanvas/chart/widget'
179
- button.innerHTML = createToolIcon('fibRetracement', 16)
180
- ```
181
-
182
- ### Widget i18n
183
-
184
- `ChartWidget` speaks 30 languages: English, Vietnamese, Simplified and Traditional Chinese, Japanese, Korean, Spanish, Portuguese, French, German, Italian, Dutch, Polish, Czech, Slovak, Hungarian, Romanian, Greek, Swedish, Danish, Norwegian, Estonian, Russian, Turkish, Indonesian, Malay, Thai, Arabic, Hebrew and Persian (the last three right to left; `dir` sets the direction yourself). Every string it shows is translated: toolbar, settings, drawing tools, alerts, dialogs, the command palette, the hotkey sheet and notices. Indicator names (SMA, RSI…) stay as they are. Set at construction.
185
-
186
- English and Vietnamese are built in. The others load from `@tradecanvas/chart/widget/locales`, so a page ships only the languages it imports:
187
-
188
- ```ts
189
- import { ja } from '@tradecanvas/chart/widget/locales'
190
-
191
- new ChartWidget(el, {
192
- locale: 'ja',
193
- messages: ja, // or registerWidgetLocales() for all of them
194
- chartOptions: { numberLocale: 'ja-JP' }, // separate: number/date formatting (see below)
195
- });
196
- ```
197
-
198
- `messages` also overrides single keys on top of `locale` (`{ 'watchlist.title': 'Theo dõi' }`). A locale with a region falls back to its language (`ja-JP` → `ja`; `zh-TW` → Traditional Chinese).
199
-
200
- `locale`/`messages` cover the **text**; `chartOptions.numberLocale` controls number and date **formatting** (price axis, legend, watchlist prices, current-price tag, session-break dates) via `Intl`.
201
-
202
- See `packages/library/src/widget/locales/en.ts` for the full key list (`MessageKey`).
203
-
204
- ### Widget vs Headless
205
-
206
- | | `Chart` (headless) | `ChartWidget` |
207
- |---|---|---|
208
- | Import | `@tradecanvas/chart` | `@tradecanvas/chart/widget` |
209
- | UI included | None — build your own | Complete toolbar, sidebar, settings |
210
- | Bundle impact | ~50 KB gzip | ~65 KB gzip (includes UI) |
211
- | Framework | Any (React, Vue, Svelte, vanilla) | Vanilla JS DOM (works everywhere) |
212
- | Customization | Full control | Toggle sections on/off |
213
- | Advanced access | Direct API | `widget.getChart()` for direct API |
214
-
215
- ### Widget Look
216
-
217
- The widget's shapes and sizes — corners, control heights, type, borders, shadows, how a chosen button shows, bars docked or floating — are a look of their own, apart from the colours. Three presets: **Studio** (the default), **Terminal** (dense and square) and **Capsule** (pills, floating bars). Start from one and change what you like:
218
-
219
- ```ts
220
- const widget = new ChartWidget(host, { ui: 'terminal' });
221
- widget.setUI({ preset: 'studio', radius: { md: 10 }, density: 'compact', toolbar: 'floating', active: 'solid' });
222
- ```
223
-
224
- The chart's price tags take the same corners (`tagRadius`; on a bare `Chart`, `chart.setShapes({ tagRadius })`). The widget loads no fonts: load the ones a look names. See [Styling](https://bonguynvan.github.io/tradecanvas/docs/styling).
225
-
226
- ### Widget switches and your own parts
227
-
228
- Every part of the widget has a switch, on until you turn it off and changeable while it runs. A name without a dot is a whole capability, off wherever it shows (`alerts`, `settings`, `hotkeys`); a dotted name is one place (`toolbar.screenshot`, `sidebar.magnet`, `menu.chart.order`). `WIDGET_FEATURES` lists all 112; the older options (`toolbar: false`, `drawingTools: false`…) are the same switches. Your own buttons, menus and status items go into the widget's bars and follow its theme and look.
229
-
230
- ```ts
231
- const widget = new ChartWidget(el, {
232
- features: { 'toolbar.replay': false, 'sidebar.patterns': false, hotkeys: false }, // one button, a section of drawing tools, every key
233
- drawingMenuItems: ({ id, type }) => [{ label: 'Share', icon: 'link', onSelect: () => share(id) }],
234
- })
235
- widget.setFeatures({ sidebar: false, statusBar: false }) // while running
236
-
237
- widget.addToolbarDropdown({ id: 'scans', label: 'Scans', icon: 'layers', items: () => [
238
- { label: 'Breakouts', onSelect: () => runScan('breakouts') },
239
- ] })
240
- widget.addSidebarButton({ id: 'ruler', label: 'Ruler', icon: 'ruler', onClick: () => toggleRuler() })
241
- widget.addStatusBarItem({ id: 'latency', text: '12 ms', label: 'Latency' })
242
- widget.getSlot('chart') // a layer of your own over the chart
243
- widget.addHotkey({ keys: 'Alt+N', label: 'New note', onPress: () => addNote() }) // a shortcut of yours, in the shortcut sheet too
244
- ```
245
-
246
- Switches show and hide the widget's parts; to stop something on the chart itself (drawing, trading, zoom), use `chartOptions.features`. Every route — colours, looks, style overrides, switches, your parts, words, plugins — is laid out on the [Customization](https://bonguynvan.github.io/tradecanvas/docs/customization) page.
247
-
248
- ### Widget Theming
249
-
250
- `ChartWidget`'s own chrome (toolbar, sidebars, settings panel, watchlist — everything *outside* the canvas) is styled entirely through CSS custom properties on `.tcw-root`, the widget's own root element. These are a **stable, documented contract**: additive-only across minor/patch releases — a property is never renamed or removed without a major version bump. Override them from the host page; no build step or theme object needed.
251
-
252
- ```css
253
- /* Dark is the default (no attribute needed); light sets data-tcw-theme="light" */
254
- .my-app .tcw-root:not([data-tcw-theme="light"]) {
255
- --tcw-bg: #0a0a0f;
256
- --tcw-accent: #7c5cff;
257
- --tcw-radius: 0px;
258
- --tcw-radius-lg: 0px;
259
- }
260
- ```
261
-
262
- | Variable | Default (dark) | Purpose |
263
- |---|---|---|
264
- | `--tcw-bg` | `#080b10` | Root background |
265
- | `--tcw-bg-surface` | `#0c1016` | Panel / toolbar surface |
266
- | `--tcw-bg-elevated` | `#141922` | Popovers, dropdowns, modals |
267
- | `--tcw-bg-overlay` | `rgba(20,25,34,.5)` | Backdrop behind overlays |
268
- | `--tcw-border` | `#1f2630` | Default border |
269
- | `--tcw-border-strong` | `#2a323e` | Emphasized border (focus rings, dividers) |
270
- | `--tcw-text` | `#e7e9ee` | Primary text |
271
- | `--tcw-text-dim` | `#aab1bd` | Secondary text |
272
- | `--tcw-text-muted` | `#758091` | Tertiary / placeholder text |
273
- | `--tcw-accent` | `#f2a93b` | Primary accent (active tab, focus, links) |
274
- | `--tcw-accent-ink` | `#1a1204` | Text and icons on an accent fill |
275
- | `--tcw-accent-hover` | `#f5b95c` | Accent hover state |
276
- | `--tcw-accent-soft` | `rgba(242,169,59,.14)` | Accent tint (selected row background) |
277
- | `--tcw-accent-glow` | `rgba(242,169,59,.22)` | Accent glow (focus halo) |
278
- | `--tcw-accent-line` | `rgba(242,169,59,.55)` | Accent border/underline |
279
- | `--tcw-red` / `--tcw-red-soft` | `#e8505b` / tint | Down/sell/negative |
280
- | `--tcw-green` / `--tcw-green-soft` | `#1fa874` / tint | Up/buy/positive |
281
- | `--tcw-amber` | `#ff9f43` | Warning |
282
- | `--tcw-hover-bg` | `rgba(255,255,255,.05)` | Row/button hover background |
283
- | `--tcw-active-bg` | `rgba(255,255,255,.08)` | Row/button pressed background |
284
- | `--tcw-divider` | `rgba(255,255,255,.06)` | Hairline dividers |
285
- | `--tcw-ease` / `--tcw-ease-out` | cubic-bezier | Transition easing |
286
- | `--tcw-dur-fast` / `-normal` / `-slow` | `120ms` / `180ms` / `260ms` | Transition durations |
287
- | `--tcw-radius-xs` / `-sm` / `--tcw-radius` / `-lg` / `-xl` | `3px` / `5px` / `7px` / `11px` / `16px` | The corner scale — set to `0` for a square look |
288
- | `--tcw-control-radius` / `--tcw-input-radius` / `--tcw-menu-radius` / `--tcw-dialog-radius` / `--tcw-panel-radius` / `--tcw-tooltip-radius` / `--tcw-tag-radius` / `--tcw-toast-radius` | from the scale | Each kind of part's corners |
289
- | `--tcw-toolbar-h` / `--tcw-control-h` / `--tcw-control-h-sm` / `--tcw-icon` / `--tcw-sidebar-w` / `--tcw-menu-item-h` | `46px` / `30px` / `24px` / `18px` / `48px` / `30px` | Sizes |
290
- | `--tcw-font` / `--tcw-font-size` / `--tcw-weight` / `--tcw-weight-strong` | `'Manrope', 'Inter', …` / `13px` / `500` / `600` | Type |
291
- | `--tcw-label-case` / `--tcw-label-tracking` | `none` / `0em` | Small labels (section titles) |
292
- | `--tcw-border-w` / `--tcw-sep-w` | `1px` / `0px` | Border width; rules between toolbar groups |
293
- | `--tcw-menu-shadow` / `--tcw-dialog-shadow` / `--tcw-tooltip-shadow` | the elevation shadows | Shadows of menus, dialogs, tooltips |
294
- | `--tcw-blur` / `--tcw-surface-opacity` | `0px` / `100%` | Frosted menus |
295
- | `--tcw-shadow-sm` / `-md` / `-lg` / `-xl` | box-shadow values | Elevation |
296
- | `--tcw-ring` | `0 0 0 2px rgba(242,169,59,.45)` | Focus ring |
297
- | `--tcw-font-mono` | `'JetBrains Mono', …` | Monospace font stack (price ladder, code) |
298
-
299
- Light theme (`[data-tcw-theme="light"]`) redefines the color group (`--tcw-bg*`, `--tcw-border*`, `--tcw-text*`, `--tcw-accent*`, `--tcw-hover-bg`, `--tcw-active-bg`, `--tcw-divider`, `--tcw-shadow*`) with its own defaults — override both selectors if you support both themes. With the `ui` option set, the widget writes its look's variables on the element, so they win over your CSS; without it they stay yours to set.
300
-
301
- ## Features
302
-
303
- ### Chart Types
304
-
305
- | Type | Description |
306
- |---|---|
307
- | Candlestick | Standard OHLC candles |
308
- | Hollow Candle | Open/close determines fill |
309
- | Bar (OHLC) | Classic open-high-low-close bars |
310
- | Line | Close price line |
311
- | Area | Filled area below close |
312
- | Baseline | Two-tone area split at a reference price |
313
- | Heikin-Ashi | Smoothed candles for trend identification |
314
- | Renko | Fixed-size bricks that ignore time |
315
- | Kagi | Reversal-based line chart |
316
- | Point & Figure | X/O columns for supply/demand analysis |
317
- | Line Break | Three-line break charts |
318
- | Range Bars | Fixed price-range bars — each bar's high − low equals a configured range |
319
- | Volume Candles | Candlesticks with width proportional to volume |
320
- | Equivolume | Full-range boxes with width proportional to volume share (Richard Arms style) |
321
- | HLC Area | High-low-close area band with close line |
322
- | Step Line | Staircase/step pattern from close prices |
323
- | Line with Markers | Close line with circular markers at each data point |
324
-
325
- ### Multi-Chart Grid
326
-
327
- Display multiple synchronized charts side-by-side with linked crosshairs and time axis:
328
-
329
- ```typescript
330
- import { ChartGrid, BinanceAdapter } from '@tradecanvas/chart'
331
-
332
- const grid = new ChartGrid(document.getElementById('grid')!, {
333
- layout: '2x2',
334
- syncCrosshair: true,
335
- syncTimeAxis: true,
336
- })
337
-
338
- // An adapter keeps one stream: give each chart its own
339
- grid.connectAll(() => new BinanceAdapter(), ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT'], '5m')
340
- ```
341
-
342
- With the full widget on each chart, a bar to pick the arrangement and sync, and the whole grid saved as a named layout:
343
-
344
- ```typescript
345
- import { ChartWidgetGrid } from '@tradecanvas/chart/widget'
346
-
347
- const workspace = new ChartWidgetGrid(document.getElementById('grid')!, {
348
- layout: '1x2',
349
- adapter: () => new BinanceAdapter(),
350
- cells: [{ symbol: 'BTCUSDT' }, { symbol: 'ETHUSDT', timeframe: '1h' }],
351
- sync: { crosshair: true, interval: false, symbol: false, time: false, drawings: false },
352
- })
353
- workspace.setSync({ time: true })
354
- ```
355
-
356
- Supported layouts: `'1x1'`, `'1x2'`, `'2x1'`, `'2x2'`, `'1x3'`, `'3x1'`, `'2x3'`, `'3x2'`.
357
-
358
- ### Command Palette
359
-
360
- Press `Ctrl+K` (or `Cmd+K`) inside ChartWidget to open a searchable command palette. Quickly find and toggle indicators, change chart types, activate drawing tools, switch timeframes, or trigger actions (screenshot, theme toggle, settings).
126
+ ## Documentation
361
127
 
362
- ### Finance Charts
363
-
364
- | Chart | Description |
128
+ | Topic | |
365
129
  |---|---|
366
- | SparklineChart | Tiny inline line/area chart from a number array — for dashboards and KPI cards |
367
- | DepthChart | Bid/ask order book visualization with cumulative volume areas |
368
- | EquityCurveChart | Portfolio equity line with drawdown shading and benchmark comparison |
369
- | HeatmapChart | Colored cell grid with treemap layout — for sector/market performance |
370
- | WaterfallChart | Running cumulative bars — P&L attribution, revenue bridge, cash flow |
371
- | GaugeChart | Speedometer-style gauge — KPIs, risk scores, Fear & Greed index |
372
-
373
- ```typescript
374
- import {
375
- SparklineChart, DepthChart, EquityCurveChart, HeatmapChart,
376
- WaterfallChart, GaugeChart,
377
- } from '@tradecanvas/chart'
378
-
379
- // Sparkline in a 120x48 container
380
- new SparklineChart(el, { data: [100, 102, 98, 105, 103], mode: 'area', color: '#1fa874' })
381
-
382
- // Equity curve with drawdown
383
- new EquityCurveChart(el, { data: equityPoints, drawdown: true, benchmark: spyData })
384
-
385
- // Order book depth
386
- new DepthChart(el, { data: { bids, asks }, crosshair: true })
387
-
388
- // Market heatmap (treemap weighted by market cap)
389
- new HeatmapChart(el, { data: cells, weighted: true })
390
-
391
- // P&L waterfall
392
- new WaterfallChart(el, {
393
- data: [
394
- { label: 'Start', value: 10000, type: 'total' },
395
- { label: 'Gain', value: 1850 },
396
- { label: 'Loss', value: -620 },
397
- { label: 'End', value: 11230, type: 'total' },
398
- ],
399
- })
400
-
401
- // Fear & Greed gauge: zones light up to the value, the label shows the current zone
402
- const gauge = new GaugeChart(el, {
403
- value: 72,
404
- label: 'Fear & Greed',
405
- zones: [
406
- { from: 0, to: 25, color: '#e8505b', label: 'Extreme fear' },
407
- { from: 25, to: 45, color: '#f2a93b', label: 'Fear' },
408
- { from: 45, to: 55, color: '#8a93a3', label: 'Neutral' },
409
- { from: 55, to: 75, color: '#62c895', label: 'Greed' },
410
- { from: 75, to: 100, color: '#1fa874', label: 'Extreme greed' },
411
- ],
412
- // pointer: 'needle', // classic needle instead of the ring marker
413
- })
414
- gauge.setValue(85) // animates smoothly
415
- ```
416
-
417
- ### Indicators (built-in)
418
-
419
- 111 indicators — moving averages (SMA, EMA, WMA, Hull, DEMA, TEMA, ALMA, KAMA,
420
- LSMA, McGinley, SMMA, MA Cross, MTF MA, Hamming, MA Double/Triple), bands and channels (Bollinger,
421
- Keltner, Donchian, Envelope, Linear Regression, MA Channel), trend and stops (Ichimoku,
422
- Supertrend, Parabolic SAR, Chandelier, Chande Kroll Stop, Alligator, ZigZag,
423
- Fractals, Pivot Points, Volatility Index), VWAPs and Volume Profile on the price pane; RSI, MACD,
424
- Stochastic, ATR, ADX, CCI, OBV, MFI, Bollinger %B and BandWidth, Historical
425
- Volatility, Ulcer Index and 48 more oscillators, volume and volatility
426
- indicators in panes. The [indicator catalog](https://bonguynvan.github.io/tradecanvas/docs/indicators)
427
- lists every id with its inputs, lines and levels.
428
-
429
- ```typescript
430
- import { indicatorSource } from '@tradecanvas/chart'
431
-
432
- const rsi = chart.addIndicator('rsi', { period: 14 })!
433
- chart.addIndicator('ema', { period: 21, source: 'hlc3' }) // another price
434
- chart.addIndicator('sma', { period: 9, source: indicatorSource(rsi, 'value') }) // RSI's own average, in RSI's pane
435
- chart.setIndicatorLevels(rsi, [20, 50, 80])
436
- ```
130
+ | [Getting started](https://bonguynvan.github.io/tradecanvas/docs/getting-started/) | Install, the widget, the headless chart |
131
+ | [Chart types](https://bonguynvan.github.io/tradecanvas/docs/chart-types/) · [Indicators](https://bonguynvan.github.io/tradecanvas/docs/indicators/) · [Drawing tools](https://bonguynvan.github.io/tradecanvas/docs/drawing-tools/) | What the chart draws, and the settings of each |
132
+ | [Realtime](https://bonguynvan.github.io/tradecanvas/docs/realtime/) | Built-in feeds, your own adapter, history, tick charts |
133
+ | [Trading](https://bonguynvan.github.io/tradecanvas/docs/trading/) | Orders, positions, execution adapters, replay |
134
+ | [Styling](https://bonguynvan.github.io/tradecanvas/docs/styling/) · [Customization](https://bonguynvan.github.io/tradecanvas/docs/customization/) | Themes, style keys, looks, feature switches, your own parts |
135
+ | [Plugins](https://bonguynvan.github.io/tradecanvas/docs/plugins/) | Your own indicators, drawing tools, chart types and overlays |
136
+ | [Analytics](https://bonguynvan.github.io/tradecanvas/docs/analytics/) · [Finance charts](https://bonguynvan.github.io/tradecanvas/docs/finance/) | Backtesting and risk metrics; depth, equity and other charts |
137
+ | [Performance](https://bonguynvan.github.io/tradecanvas/docs/performance/) · [API reference](https://bonguynvan.github.io/tradecanvas/docs/api/) | Measurements and the full API |
437
138
 
438
- - **Sources**: close, open, high, low, hl2, hlc3, ohlc4, hlcc4, or another indicator's line.
439
- - **Panes**: one value scale per pane for lines, levels, axis and crosshair; move an indicator into another pane, a new one or back to the price pane; fold, maximise and reorder panes (`moveIndicatorToPane`, `setPaneCollapsed`, `setMaximizedPane`, `movePane`).
440
- - **Undo and templates**: Ctrl/Cmd+Z undoes indicator changes, in the same history as drawings; ChartWidget saves the indicators as named templates (`getIndicatorSetup` / `applyIndicatorSetup`).
441
- - **Levels**: editable per instance (RSI 30/70, CCI ±100 …), kept in saved layouts.
442
- - **Value tags**: each line's latest value on its axis, in its colour.
443
- - **Custom indicators** declare their lines (`plots`), scale, levels and inputs; the chart draws and labels them.
444
-
445
- Invalid parameters (NaN, Infinity, non-numeric strings, missing keys) fall back
446
- to the defaults instead of reaching the calculations.
447
-
448
- ### Drawing Tools
449
-
450
- Trendline, Horizontal Line, Vertical Line, Ray, Extended Line, Parallel Channel, Fibonacci Retracement, Fibonacci Extension, **Fibonacci Time Zones**, Rectangle, Ellipse, Triangle, Arrow, Pitchfork, Gann Fan, Gann Box, Elliott Wave, Regression Channel, Date Range, Price Range, Measure, Anchored VWAP, Volume Profile Range, Text Annotation
451
-
452
- All drawing tools support:
453
- - Click-to-place with magnet snapping to OHLC values
454
- - Undo / redo (Ctrl+Z / Ctrl+Y)
455
- - Serialization for save/load
456
- - Custom styles (color, width, dash pattern)
457
-
458
- ### Trading Overlay
459
-
460
- Render open positions and pending orders directly on the chart, like MT4/MT5.
461
-
462
- ```typescript
463
- import type { TradingPosition, TradingOrder } from '@tradecanvas/chart'
464
-
465
- chart.setPositions([{
466
- id: 'pos-1',
467
- side: 'buy',
468
- entryPrice: 3500,
469
- quantity: 1.5,
470
- closedQuantity: 0.5, // partial close — visualized as a left-edge dim band
471
- stopLoss: 3400,
472
- takeProfit: 3700,
473
- }])
474
-
475
- chart.setOrders([{
476
- id: 'order-1',
477
- side: 'sell',
478
- type: 'limit',
479
- price: 3800,
480
- quantity: 0.5,
481
- label: 'TP',
482
- draggable: true,
483
- }])
484
-
485
- // Customize the position zone color via P&L thresholds
486
- chart.setTradingConfig({
487
- pnlThresholds: [
488
- { pnl: -Infinity, color: '#b91c1c' },
489
- { pnl: 0, color: '#94a3b8' },
490
- { pnl: 50, color: '#16a34a' },
491
- { pnl: 200, color: '#15803d' },
492
- ],
493
- // Custom label template — tokens: {side} {qty} {openQty} {closedQty} {entry} {price} {pnl} {pnlPct} {pnlSign}
494
- positionLabel: '{side} {openQty}/{qty} @ {entry} | {pnlSign}{pnl} ({pnlPct})',
495
- })
496
-
497
- // Listen for user drag-to-modify
498
- chart.on('positionModify', (e) => console.log('SL/TP moved:', e.payload))
499
- chart.on('orderModify', (e) => console.log('Order moved:', e.payload))
500
-
501
- // The × and ⇅ buttons on the lines raise these; so can your own UI
502
- chart.cancelOrderIntent('order-1')
503
- chart.reversePositionIntent('pos-1')
504
- chart.on('executionFill', (e) => console.log(e.payload.reason, e.payload.pnl))
505
- ```
506
-
507
- ### Signal Markers
508
-
509
- Visualize buy/sell signals from bots, indicators, or manual analysis.
510
-
511
- ```typescript
512
- chart.addSignalMarker({
513
- time: 1715692800000,
514
- price: 62500,
515
- direction: 'long',
516
- confidence: 0.85,
517
- source: 'ema-crossover',
518
- label: 'EMA Cross',
519
- })
139
+ The docs are also in Tiếng Việt, 简体中文, 日本語, 한국어 and Español.
520
140
 
521
- // Color-code by source
522
- chart.setSignalMarkerStyle({
523
- sourceColors: {
524
- 'ema-crossover': '#4c8dff',
525
- 'rsi-divergence': '#f2a93b',
526
- 'whale-flow': '#9C27B0',
527
- },
528
- })
529
- ```
530
-
531
- ### Trade Zones
532
-
533
- Render entry→exit rectangles with P&L coloring for executed trades.
534
-
535
- ```typescript
536
- const zoneId = chart.addTradeZone({
537
- entryTime: 1715692800000,
538
- entryPrice: 62500,
539
- exitTime: 1715700000000,
540
- exitPrice: 63200,
541
- direction: 'long',
542
- pnl: 140,
543
- pnlPercent: 1.12,
544
- })
545
-
546
- // Update a live trade when it closes
547
- chart.updateTradeZone(zoneId, {
548
- exitTime: Date.now(),
549
- exitPrice: 63500,
550
- pnl: 200,
551
- })
552
- ```
553
-
554
- ### Real-Time Streaming
555
-
556
- ```typescript
557
- // Built-in Binance adapter (free, no API key)
558
- chart.connect({
559
- adapter: new BinanceAdapter(),
560
- symbol: 'ETHUSDT',
561
- timeframe: '1m',
562
- historyLimit: 500,
563
- })
564
-
565
- // Or manual data feed
566
- chart.setData(historicalBars)
567
- chart.appendBar(newBar)
568
- chart.updateLastBar(updatedBar)
569
- chart.setCurrentPrice(3500.42)
570
- ```
571
-
572
- **Built-in adapters** (all free, no API key): `BinanceAdapter`, `CoinbaseAdapter`, `BybitAdapter`, `KrakenAdapter`, plus `MockAdapter` for offline/testing.
573
-
574
- ```typescript
575
- import { BybitAdapter, KrakenAdapter, CoinbaseAdapter } from '@tradecanvas/chart'
576
-
577
- chart.connect({ adapter: new BybitAdapter(), symbol: 'BTCUSDT', timeframe: '1m' })
578
- chart.connect({ adapter: new KrakenAdapter(), symbol: 'BTC/USD', timeframe: '5m' })
579
- chart.connect({ adapter: new CoinbaseAdapter(), symbol: 'BTC-USD', timeframe: '15m' })
580
- ```
581
-
582
- **Any feed in ~20 lines.** Extend `WebSocketAdapter` (live + REST history) or `PollingAdapter` (REST-only feeds) — the base handles the connection lifecycle, reconnect, decoding, and event emission. You supply a URL and a parse function:
583
-
584
- ```typescript
585
- import { WebSocketAdapter } from '@tradecanvas/chart'
586
-
587
- const myAdapter = new WebSocketAdapter({
588
- name: 'myexchange',
589
- wsUrl: (c) => `wss://api.myexchange.com/ws/${c.symbol}@kline_${c.timeframe}`,
590
- fetchHistory: (symbol, tf, limit) => fetch(`/candles?...`).then((r) => r.json()),
591
- parseMessage: (raw) => ({ bar: toBar(raw), closed: raw.k.x }),
592
- })
593
- ```
594
-
595
- ### Live Execution
596
-
597
- Connect an `ExecutionAdapter` to turn the display-only trading overlay into a real trading surface. The chart routes its order/position intents into the adapter, and renders the authoritative `orders` / `positions` the adapter emits back — the **adapter is the single source of truth**. With no adapter connected, those intents stay plain events (backward-compatible).
598
-
599
- ```typescript
600
- import { PaperExecutionAdapter } from '@tradecanvas/chart'
601
-
602
- chart.connectExecution(new PaperExecutionAdapter({ markPrice: 64000 }))
603
-
604
- // Drag-to-create an order, then confirm:
605
- chart.startOrderDraft('buy') // draggable line at the latest close
606
- chart.confirmOrderDraft() // emits orderPlace → adapter fills → chart renders the position
607
- // chart.cancelOrderDraft()
608
-
609
- // One channel for failures (adapter-reported or a failed command):
610
- chart.on('executionError', (e) => toast(e.payload.message))
611
- ```
612
-
613
- Implement `ExecutionAdapter` (it mirrors `DataAdapter`) to wire a real broker / OMS: `placeOrder`, `modifyOrder`, `cancelOrder`, `modifyPosition`, `closePosition`, plus `orders` / `positions` / `fill` / `error` events. `PaperExecutionAdapter` is a virtual-fill sandbox for demos and tests. The order type of a drag-to-create order (limit vs stop) is inferred from where you drop the line relative to the current price.
614
-
615
- ### Plugins — extend the chart
616
-
617
- Register custom **indicators**, **drawing tools**, **chart types**, and **overlays** — globally (every chart created afterward inherits) or per-chart.
618
-
619
- ```typescript
620
- import { Chart, registerPlugin, IndicatorBase } from '@tradecanvas/chart'
621
-
622
- class MyIndicator extends IndicatorBase { /* descriptor, calculate(), render() */ }
623
-
624
- // 1) Global — available to every chart created afterward:
625
- registerPlugin({ kind: 'indicator', plugin: new MyIndicator() })
626
-
627
- // 2) Per-chart at construction:
628
- const chart = new Chart(el, { plugins: [{ kind: 'overlay', plugin: myHeatmap }] })
629
-
630
- // 3) Imperative on an instance:
631
- chart.plugins.register({ kind: 'chartType', plugin: myCustomCandles })
632
- chart.setChartType('my-custom-candles') // custom chart types render via the plugin
633
- ```
634
-
635
- | Plugin kind | Contract |
636
- |---|---|
637
- | `indicator` | `IndicatorPlugin` — `calculate()` + `render()` |
638
- | `drawing` | `DrawingPlugin` — `render()` + `hitTest()` |
639
- | `chartType` | `ChartTypePlugin` — `createRenderer()` + optional `transform()` |
640
- | `overlay` | `OverlayPlugin` — `render(ctx, { viewport, data, theme })` on the `main` / `overlay` / `ui` layer |
641
-
642
- ### Indicators outside the chart
643
-
644
- `IndicatorWorkerHost` computes an indicator from bars with the same messages a
645
- Web Worker would use. The worker script is not part of the published packages
646
- yet; pass `null` and register the plugins to compute in place (SSR, tests,
647
- scripts):
648
-
649
- ```typescript
650
- import { IndicatorWorkerHost, RSIIndicator } from '@tradecanvas/core'
651
-
652
- const host = new IndicatorWorkerHost(null)
653
- host.registerFallbackPlugin(new RSIIndicator())
654
- const output = await host.calculate('rsi', { id: 'rsi', instanceId: 'rsi-1', params: { period: 14 } }, bars)
655
- ```
656
-
657
- ### Save / Load
658
-
659
- ```typescript
660
- const json = chart.saveState()
661
- localStorage.setItem('my-chart', json!)
662
-
663
- chart.loadState(localStorage.getItem('my-chart')!)
664
-
665
- // Download / upload files
666
- chart.downloadState('my-chart.json')
667
- await chart.loadStateFromFile()
668
-
669
- // Or keep a layout saved as it changes (debounced)
670
- chart.setAutoSave('my-chart', 1500)
671
- ```
672
-
673
- A saved layout holds the chart type, theme, drawings, indicators (inputs, pane,
674
- colours, visibility) and alerts, including alerts on indicator lines.
675
-
676
- ### Themes
677
-
678
- ```typescript
679
- import { DARK_THEME, LIGHT_THEME, DARK_TERMINAL, volumeColor } from '@tradecanvas/chart'
680
-
681
- // Built-in presets: DARK_THEME, LIGHT_THEME, DARK_TERMINAL
682
- chart.setTheme(DARK_TERMINAL) // fintech terminal: #0E0E0E bg, #00FF87/#FF3B4D candles, monospace
683
-
684
- // Or customize any preset
685
- chart.setTheme({
686
- ...DARK_THEME,
687
- candleUp: '#1fa874',
688
- candleDown: '#e8505b',
689
- volumeUp: volumeColor('#1fa874'), // volume bars: the candle colour, see-through
690
- volumeDown: volumeColor('#e8505b'),
691
- background: '#0a0a0f',
692
- })
693
- ```
694
-
695
- ### Style overrides
696
-
697
- Any one part of what the chart draws can be set apart from the theme, by key — the grid's lines each way, the crosshair, the axes, the panes, the legend, the last price, the volume and the main series as each chart type draws it. Keys left alone follow the theme. Your app's overrides stay through theme switches; the user's (`layer: 'user'`, what the widget's Settings write) are kept per theme and saved with `saveState()`.
698
-
699
- ```typescript
700
- chart.applyOverrides({
701
- 'series.candlestick.upColor': '#26a69a', // every up/down chart type falls back to it
702
- 'grid.vertical.visible': false,
703
- 'grid.horizontal.style': 'dotted',
704
- 'crosshair.vertical.style': 'solid',
705
- 'panes.background': '#0d1117',
706
- })
707
- chart.getStyleValue('series.bar.upColor') // '#26a69a'
708
-
709
- // Indicator plots and panes
710
- chart.updateIndicatorStyle(macdId, { plots: { signal: { lineStyle: 'dashed' } } })
711
- chart.setIndicatorDefaults('ema', { colors: ['#f5a623'] })
712
- chart.setPaneStyle(rsiId, { background: '#101418' })
713
- ```
714
-
715
- `CHART_STYLE_KEYS` lists every key. The React, Vue and Svelte components take them as an `overrides` prop. Orders, positions, signal markers, trade zones and a selected drawing's handles take keys too (`trading.buyColor`, `markers.longColor`, `tradeZones.activeColor`, `drawings.handleColor`…). In the widget, Settings → Style sets every key, the colours of the chart type on view included, and Settings → Trading the colours of orders, positions, signal markers and trade zones (Auto until one is picked).
716
-
717
- ### Events
718
-
719
- ```typescript
720
- chart.on('crosshairMove', (e) => { /* { point, bar, barIndex, indicatorValues } — also over indicator panes */ })
721
- chart.on('crosshairLeave', () => { /* the pointer left the plot */ })
722
- chart.on('drawingToolChange', (e) => { /* { tool } — null once a drawing is finished or cancelled */ })
723
- chart.on('indicatorUpdate', (e) => { /* { from } — indicator values recomputed from this bar on */ })
724
- chart.on('paneResize', (e) => { /* { instanceId, size } — an indicator pane was resized */ })
725
- chart.on('indicatorChange', (e) => { /* { instanceId, change } — shown/hidden, restyled, levels, inputs or pane changed */ })
726
- chart.on('barClick', (e) => { /* { bar, barIndex, point } */ })
727
- chart.on('visibleRangeChange', (e) => { /* { from, to } — bar indices, not timestamps */ })
728
- chart.on('priceRangeChange', (e) => { /* { min, max } — visible price bounds */ })
729
- chart.on('zoomChange', (e) => { /* { barWidth } — pixels per bar */ })
730
- chart.on('drawingCreate', (e) => { /* ... */ })
731
- chart.on('orderModify', (e) => { /* ... */ })
732
- chart.on('positionModify', (e) => { /* ... */ })
733
- ```
734
-
735
- `visibleRangeChange`, `priceRangeChange`, and `zoomChange` fire on every pan,
736
- zoom, resize, and data update — but only when that piece of viewport state
737
- actually changed. Resolve a `visibleRangeChange` index to time with
738
- `chart.getData()[e.payload.from].time`.
739
-
740
- ### Replay Mode
741
-
742
- `ReplayController` plays a historical `DataSeries` forward at controlled speed. Decoupled from `Chart` — wire it into any sink (chart for UI playback, or a strategy fn for headless backtests).
743
-
744
- ```typescript
745
- import { ReplayController } from '@tradecanvas/chart'
746
-
747
- const replay = new ReplayController({
748
- data: historicalBars,
749
- speed: 10, // bars per second
750
- startIndex: 0,
751
- })
752
-
753
- // Seed the chart with the prefix before replay starts
754
- chart.setData(replay.getPrefix())
755
-
756
- // Each emitted bar drives the chart forward
757
- replay.on('bar', ({ bar }) => chart.appendBar(bar))
758
- replay.on('finished', () => console.log('done'))
759
-
760
- replay.start()
761
- // replay.pause(); replay.resume(); replay.step(5); replay.seek(200); replay.setSpeed(20)
762
- ```
763
-
764
- The chart replays its own series too: `chart.replayStart()`. Signal markers and trade zones show as the replay reaches them (a trade drawn open until its exit), `startTime` starts at a time, `hideHistory` leaves the bars before the start out until the replay stops, and `duration` plays to the end in about that long, however many bars:
765
-
766
- ```typescript
767
- chart.setSignalMarkers(signals)
768
- chart.setTradeZones(trades)
769
- chart.replayStart({ startTime: Date.now() - 30 * 86_400_000, hideHistory: true, duration: 3500 }) // the last 30 days, only them, in about 3.5 s
770
- chart.on('replayComplete', () => chart.replayStop()) // then the whole series
771
- ```
772
-
773
- ### Chart Interaction
774
-
775
- Every gesture you'd expect from a desktop trading chart is built in:
776
-
777
- | Gesture | Result |
778
- |---|---|
779
- | Drag chart body left/right | Pan through time |
780
- | Drag chart body up/down | Pan the price scale (freezes auto-scale; double-click price axis to restore) |
781
- | Drag price axis up/down | Compress / expand vertical scale (freezes auto-scale) |
782
- | Drag time axis left/right | Zoom time axis |
783
- | Double-click price axis | Re-enable auto-scale |
784
- | Double-click time axis | Fit all data to viewport |
785
- | Wheel | Zoom around cursor |
786
- | Drag a pane divider | Resize the indicator pane (`ns-resize` cursor on hover) |
787
- | `Shift` + drag | Measure ruler (bars × time × price Δ × %) |
788
- | `Alt` + click | Pin OHLC tooltip; live crosshair shows Δ to pinned bar |
789
- | Hover | Price + time pill labels follow on both axes |
790
- | `Esc` | Unpin tooltip / cancel drawing |
791
- | `?` | Show keyboard-shortcut sheet *(widget)* |
792
- | `Ctrl/⌘ + K` | Command palette *(widget)* |
793
- | `Ctrl/⌘ + P` | Symbol search *(widget)* |
794
- | `Ctrl/⌘ + Z` / `Shift + Z` | Undo / redo drawings |
795
-
796
- ### Data import — drag-and-drop or programmatic
797
-
798
- ```typescript
799
- import { parseOHLCV } from '@tradecanvas/chart'
800
-
801
- const { data, rowCount, skipped } = parseOHLCV(csvText)
802
- chart.setData(data)
803
- ```
804
-
805
- Drop a CSV or JSON file onto the widget and it loads instantly. Auto-detects
806
- delimiter (`,` / `;` / tab / `|`), header vs. headerless, ISO 8601 timestamps,
807
- and array-of-arrays vs. array-of-objects JSON.
808
-
809
- ### Backtesting (`@tradecanvas/analytics`)
810
-
811
- Bar-by-bar strategy backtester with virtual fills, commission/slippage models, and a full risk-metrics report.
812
-
813
- ```typescript
814
- import { Backtester, PercentCommission, PercentSlippage } from '@tradecanvas/analytics'
815
-
816
- const bt = new Backtester({
817
- initialCash: 10_000,
818
- commission: new PercentCommission(0.0005),
819
- slippage: new PercentSlippage(0.0003),
820
- })
821
-
822
- const result = bt.run(historicalBars, (ctx) => {
823
- // Strategy fn runs at close of each bar; orders fill on the NEXT bar.
824
- if (!ctx.position && smaFast > smaSlow) {
825
- ctx.placeOrder({ side: 'long', type: 'market', quantity: 1 })
826
- } else if (ctx.position && smaFast < smaSlow) {
827
- ctx.close()
828
- }
829
- })
830
-
831
- console.log(result.metrics.sharpe) // 1.42
832
- console.log(result.metrics.maxDrawdownPct) // 0.087
833
- console.log(result.equityCurve) // → feed into the chart via EquityCurveRenderer
834
- ```
835
-
836
- Returns: `fills`, closed `trades`, `equityCurve`, `metrics` (Sharpe, Sortino, Calmar, CAGR, max drawdown, win rate, profit factor, expectancy). See the [live backtest demo](https://bonguynvan.github.io/tradecanvas/docs/analytics/).
837
-
838
- #### Strategy library
839
- Four drop-in reference strategies — each returns a `StrategyFn` ready to feed
840
- `Backtester.run()`:
841
-
842
- ```typescript
843
- import {
844
- Backtester,
845
- smaCrossStrategy,
846
- rsiReversionStrategy,
847
- donchianBreakoutStrategy,
848
- bollingerReversionStrategy,
849
- } from '@tradecanvas/analytics'
850
-
851
- const bt = new Backtester({ initialCash: 10_000 })
852
- bt.run(bars, smaCrossStrategy({ fastPeriod: 10, slowPeriod: 30 }))
853
- bt.run(bars, donchianBreakoutStrategy({ entryPeriod: 20, exitPeriod: 10 }))
854
- ```
855
-
856
- #### Monte Carlo path-dependence
857
- Shuffle realised trade order N times to expose whether a strategy depends on
858
- lucky sequencing. Tight P5/P95 band = robust edge; wide band = path-dependent.
859
-
860
- ```typescript
861
- import { runMonteCarlo } from '@tradecanvas/analytics'
862
-
863
- const result = bt.run(bars, smaCrossStrategy())
864
- const mc = runMonteCarlo(10_000, result.trades, { simulations: 1000, seed: 42 })
865
-
866
- mc.equityBands // [{ step, p5, p25, p50, p75, p95 }, …]
867
- mc.finalEquityPercentiles // { p5, p25, p50, p75, p95 }
868
- mc.probabilityProfitable // 0..1
869
- mc.worstMaxDrawdownPct
870
- ```
871
-
872
- ## Comparison
873
-
874
- | Feature | @tradecanvas/chart | lightweight-charts | chart.js | Highcharts Stock |
875
- |---|---|---|---|---|
876
- | Chart types | 18 + 6 finance | 4 | 8 (non-financial) | 10+ |
877
- | Finance charts | Sparkline, Depth, Equity, Heatmap, Waterfall, Gauge | None | None | Some |
878
- | Built-in indicators | 111 | 0 | 0 | ~30 |
879
- | Drawing tools | 69 | 0 | 0 | Some |
880
- | Trading overlay | Full (pos + orders + drag) | None | None | None |
881
- | Real-time streaming | Built-in (Binance) | Manual | Manual | Built-in |
882
- | Save/load state | Yes | No | No | Yes |
883
- | Replay mode | Yes (`ReplayController`) | No | No | No |
884
- | Backtester | Yes (`@tradecanvas/analytics`) | No | No | No |
885
- | Multi-chart grid | Yes (`ChartGrid`) | No | No | Yes |
886
- | Bundle (gzip) | ~100 KB core | ~45 KB | ~70 KB | ~200 KB |
887
- | Dependencies | 0 | 1 | 0 | 0 |
888
- | Widget (complete UI) | Yes (`ChartWidget`) | No | No | No |
889
- | License | MIT | Apache 2.0 | MIT | Commercial |
890
-
891
- ## API Overview
892
-
893
- ### `new Chart(container, options)`
894
-
895
- ```typescript
896
- const chart = new Chart(element, {
897
- chartType: 'candlestick',
898
- theme: DARK_THEME,
899
- autoScale: true,
900
- rightMargin: 5,
901
- numberLocale: 'en-US', // or 'de-DE', 'vi-VN', etc. — BCP 47 locale
902
- crosshair: { mode: 'magnet' },
903
- features: { drawings: true, indicators: true, trading: true, volume: true },
904
- })
905
-
906
- // Change locale at runtime
907
- chart.setNumberLocale('de-DE') // 65.234,00
908
- ```
141
+ ## Performance
909
142
 
910
- ### Key Methods
143
+ Measured in Chrome on integrated graphics (Intel UHD); the [system audit](https://bonguynvan.github.io/tradecanvas/audit/) has the full table.
911
144
 
912
- | Method | Description |
145
+ | | |
913
146
  |---|---|
914
- | `setData(bars)` | Load historical OHLCV data |
915
- | `appendBar(bar)` | Append a new candle |
916
- | `appendBars(bars)` | Bulk append (reconnect catch-up) |
917
- | `updateLastBar(bar)` | Update the in-progress candle |
918
- | `setCurrentPrice(price, pulseColor?)` | Show a live price line |
919
- | `connect(config)` | Connect to a real-time data source |
920
- | `setTimeframe(tf)` | Switch timeframe on active stream |
921
- | `setChartType(type)` | Switch chart type |
922
- | `setTheme(theme)` | Apply a theme (DARK_THEME, LIGHT_THEME, DARK_TERMINAL) |
923
- | `setNumberLocale(locale)` | Set number format locale (en-US, de-DE, vi-VN) |
924
- | `setStatusText(text)` | Show status in legend area ("LIVE · 8ms") |
925
- | `addIndicator(id, params?)` | Add a technical indicator |
926
- | `removeIndicator(instanceId)` | Remove an indicator |
927
- | `setDrawingTool(tool)` | Activate a drawing tool |
928
- | `setPositions(positions)` | Render trading positions |
929
- | `setOrders(orders)` | Render pending orders |
930
- | `setVolumeProfileVisible(v)` | Toggle the horizontal volume-profile overlay |
931
- | `setVolumeProfileConfig({ buckets, widthRatio, opacity, highlightPoC })` | Tune the volume profile |
932
- | `setAutoScale(v)` / `setLogScale(v)` | Lock or change price-scale mode |
933
- | `setInvertScale(v)` | Turn the price scale upside down |
934
- | `fitContent()` / `scrollToEnd()` | Fit all data / jump to live edge |
935
- | `setVisibleRangePreset(p)` | Show `1D`, `5D`, `1M`, `3M`, `6M`, `YTD`, `1Y`, `5Y` or `All` |
936
- | `goToTime(time)` | Centre the bar at a time |
937
- | `setCrosshairTime(time)` | Mirror another chart's crosshair (vertical line only) |
938
- | `copyDrawings()` / `pasteDrawings()` | Copy the selection, paste into this or another chart |
939
- | `setStayInDrawingMode(v)` | Keep the drawing tool after each drawing |
940
- | `saveState(key?)` | Serialize chart state |
941
- | `loadState(json)` | Restore chart state |
942
- | `screenshot()` | Download chart as image |
943
- | `setRenderer(mode)` | Draw with `'canvas'` (default), `'webgl'` or `'auto'`; resolves to what draws now |
944
- | `getRenderer()` | `'canvas'` or `'webgl'` |
945
- | `on(event, handler)` | Subscribe to events |
946
- | `destroy()` | Clean up all resources |
947
-
948
- ### Data Format
949
-
950
- ```typescript
951
- interface OHLCBar {
952
- time: number // Unix time in ms or seconds (up to 1e12 is read as seconds); ascending
953
- open: number
954
- high: number
955
- low: number
956
- close: number
957
- volume: number
958
- }
959
- ```
147
+ | 1,000,000 bars + 4 indicators, zoomed out, WebGL | 16.7 ms a frame (60 fps) |
148
+ | A live tick on 100,000 bars with four indicators | ~0.001 ms |
149
+ | `Chart` / `ChartWidget`, minified and gzipped | 176 KB / 315 KB |
960
150
 
961
- ## Examples
151
+ ## Packages
962
152
 
963
- | Example | Description |
153
+ | Package | |
964
154
  |---|---|
965
- | [Live demo](https://bonguynvan.github.io/tradecanvas/) | Feature Lab: drawing tools, indicators, trading, ranges, paged history, replay, 30 languages with sub-cent prices, watchlists with live quotes, 200k bars, slow-network switching — each on a live chart. The site and docs are also in Vietnamese, Chinese, Japanese, Korean and Spanish |
966
- | [StackBlitz sandboxes](https://bonguynvan.github.io/tradecanvas/examples/) | One-click, forkable: vanilla `Chart`, `ChartWidget`, React / Vue / Svelte wrappers, finance charts |
967
- | [`@tradecanvas/react`](https://github.com/bonguynvan/tradecanvas/tree/main/packages/react/) · [`/vue`](https://github.com/bonguynvan/tradecanvas/tree/main/packages/vue/) · [`/svelte`](https://github.com/bonguynvan/tradecanvas/tree/main/packages/svelte/) | Framework components — reactive props, typed, zero boilerplate |
155
+ | [`@tradecanvas/chart`](https://www.npmjs.com/package/@tradecanvas/chart) | The chart and `ChartWidget` (`/widget`) |
156
+ | [`@tradecanvas/react`](https://www.npmjs.com/package/@tradecanvas/react) · [`/vue`](https://www.npmjs.com/package/@tradecanvas/vue) · [`/svelte`](https://www.npmjs.com/package/@tradecanvas/svelte) | Framework components |
157
+ | [`@tradecanvas/analytics`](https://www.npmjs.com/package/@tradecanvas/analytics) | Backtester and risk metrics |
158
+ | `@tradecanvas/core` · `@tradecanvas/commons` | The engine and shared types, installed with the chart |
968
159
 
969
- ## AI coding tools
970
-
971
- - [`llms.txt`](https://bonguynvan.github.io/tradecanvas/llms.txt) and
972
- [`llms-full.txt`](https://bonguynvan.github.io/tradecanvas/llms-full.txt) give
973
- assistants the docs in one place.
974
- - An agent skill, [`skills/tradecanvas`](https://github.com/bonguynvan/tradecanvas/blob/main/skills/tradecanvas/SKILL.md), teaches
975
- coding agents to build with TradeCanvas: the entry points, the rules that
976
- avoid most bugs, and worked examples that CI type-checks against the library.
977
- Copy the folder into your project's `.claude/skills/` (or your agent's skills
978
- folder) to use it.
979
-
980
- ## Browser Support
981
-
982
- Chrome 80+, Firefox 80+, Safari 14+, Edge 80+
983
-
984
- ## Framework Integration
160
+ ## Preview builds
985
161
 
986
- Official wrapper components — reactive props, refs, zero boilerplate. Published at `1.x` alongside the core:
162
+ Every pull request and every commit on `main` is published to [pkg.pr.new](https://pkg.pr.new), so a fix or feature can be tried before it is released. Install a preview by pull request number or commit:
987
163
 
988
164
  ```bash
989
- npm install @tradecanvas/react # or @tradecanvas/vue · @tradecanvas/svelte
990
- ```
991
-
992
- ```tsx
993
- import { TradeCanvas } from '@tradecanvas/react'
994
-
995
- <TradeCanvas symbol="BTCUSDT" timeframe="5m" theme="dark" indicators={['rsi', 'macd']} />
996
- <TradeCanvas data={bars} stream={false} indicators={['rsi', { id: 'ema', params: { period: 50 } }]} />
997
- ```
998
-
999
- `indicators` takes ids or `{ id, params, position }` (one whose inputs change is put back with them), `data` shows bars of your own and opens no stream, `stream={false}` never opens one, and `features` follows its changes after mount.
1000
-
1001
- All three share the same prop surface and hand you the underlying `Chart` (for drawings, trading, execution, plugins) via `onReady` / ref / `bind:chart`. See the [frameworks docs](https://bonguynvan.github.io/tradecanvas/docs/frameworks).
1002
-
1003
- ### Headless (own the lifecycle)
1004
-
1005
- The `Chart` class also takes a DOM element directly — framework-agnostic:
1006
-
1007
- **React:**
1008
-
1009
- ```tsx
1010
- import { useEffect, useRef } from 'react'
1011
- import { Chart, BinanceAdapter } from '@tradecanvas/chart'
1012
-
1013
- function TradingChart() {
1014
- const ref = useRef<HTMLDivElement>(null)
1015
-
1016
- useEffect(() => {
1017
- const chart = new Chart(ref.current!, {
1018
- theme: 'dark',
1019
- features: { indicators: true, drawings: true },
1020
- })
1021
- chart.connect({
1022
- adapter: new BinanceAdapter(),
1023
- symbol: 'BTCUSDT',
1024
- timeframe: '5m',
1025
- })
1026
- return () => chart.destroy()
1027
- }, [])
1028
-
1029
- return <div ref={ref} style={{ width: '100%', height: 500 }} />
1030
- }
1031
- ```
1032
-
1033
- **Svelte:**
1034
-
1035
- ```svelte
1036
- <script lang="ts">
1037
- import { onMount, onDestroy } from 'svelte'
1038
- import { Chart, BinanceAdapter, DARK_THEME } from '@tradecanvas/chart'
1039
- import type { TimeFrame } from '@tradecanvas/chart'
1040
-
1041
- interface Props { symbol?: string; timeframe?: TimeFrame }
1042
- let { symbol = 'BTCUSDT', timeframe = '5m' }: Props = $props()
1043
-
1044
- let container: HTMLDivElement
1045
- let chart: Chart | null = null
1046
-
1047
- onMount(() => {
1048
- chart = new Chart(container, {
1049
- chartType: 'candlestick',
1050
- theme: DARK_THEME,
1051
- autoScale: true,
1052
- features: { indicators: true, drawings: true, volume: true },
1053
- })
1054
- chart.connect({ adapter: new BinanceAdapter(), symbol, timeframe })
1055
- })
1056
-
1057
- onDestroy(() => chart?.destroy())
1058
-
1059
- $effect(() => {
1060
- if (!chart) return
1061
- chart.disconnectStream()
1062
- chart.connect({ adapter: new BinanceAdapter(), symbol, timeframe })
1063
- })
1064
- </script>
1065
-
1066
- <div bind:this={container} style="width: 100%; height: 600px" />
1067
- ```
1068
-
1069
- **Vue:**
1070
-
1071
- ```vue
1072
- <template>
1073
- <div ref="chartContainer" style="width: 100%; height: 600px" />
1074
- </template>
1075
-
1076
- <script setup lang="ts">
1077
- import { ref, onMounted, onUnmounted } from 'vue'
1078
- import { Chart, BinanceAdapter, DARK_THEME } from '@tradecanvas/chart'
1079
-
1080
- const chartContainer = ref<HTMLDivElement>()
1081
- let chart: Chart | null = null
1082
-
1083
- onMounted(() => {
1084
- if (!chartContainer.value) return
1085
- chart = new Chart(chartContainer.value, {
1086
- chartType: 'candlestick',
1087
- theme: DARK_THEME,
1088
- autoScale: true,
1089
- features: { indicators: true, drawings: true, volume: true },
1090
- })
1091
- chart.connect({ adapter: new BinanceAdapter(), symbol: 'BTCUSDT', timeframe: '5m' })
1092
- })
1093
-
1094
- onUnmounted(() => chart?.destroy())
1095
- </script>
1096
- ```
1097
-
1098
- ## Performance
1099
-
1100
- A two-canvas Canvas2D pipeline: a hover repaints only the thin top canvas, never the scene. Five things keep large data fast:
1101
-
1102
- - **LTTB downsampling** — line / area charts automatically downsample the visible range to ~2 points per pixel using Largest-Triangle-Three-Buckets when there are far more bars than pixels. The line stays visually identical while drawing dozens of times fewer points; a no-op at normal zoom. The `lttbDownsample` utility is exported for your own use.
1103
- - **Indicators zoomed out** — below a pixel per bar, indicator lines, bands and histograms draw one span per pixel column instead of a stroke through thousands of points: much the same look for a fraction of the raster work (about 54 → 21 ms a frame zoomed out on 200,000 bars with four indicators, on integrated graphics). `node scripts/bench-render.mjs` measures it on your machine.
1104
- - **Visible-range rendering** — every renderer iterates only the bars in view, never the whole series. Hover and pan frame cost stays flat from 500 to 100,000 loaded bars.
1105
- - **Incremental indicators on live ticks** — a tick only changes the forming bar, so built-in indicators that implement `update()` (SMA, EMA, WMA, VWMA, Bollinger, Envelope, RSI, MACD, ATR, OBV, Stochastic) recompute just that bar instead of the whole history. Others fall back to a full recalculation. Custom plugins can opt in via `IndicatorPlugin.update`.
1106
- - **Cheap full loads** — a symbol/timeframe switch recomputes every indicator once. Their per-bar `values` lookup is an `IndicatorValueMap` (array-backed while bars arrive in time order, ~3x cheaper to build than a `Map` keyed by timestamps), and `setData` reuses already well-formed bars instead of copying each one.
1107
-
1108
- BB + EMA + RSI + MACD (`pnpm bench`, single core):
1109
-
1110
- | History | Full recalculation (switch / `setData`) | Incremental `update()` (live tick) |
1111
- |---|---|---|
1112
- | 20,000 bars | ~5 ms | ~0.0005 ms |
1113
- | 100,000 bars | ~27 ms | ~0.001 ms |
1114
-
1115
- Downsampling throughput (`pnpm bench`, single core):
1116
-
1117
- | Visible points → 1600 | Time / frame | Throughput |
1118
- |---|---|---|
1119
- | 10,000 | ~0.025 ms | 39,600 / s |
1120
- | 100,000 | ~0.32 ms | 3,100 / s |
1121
- | 1,000,000 | ~2.6 ms | 380 / s |
1122
-
1123
- A 100k-bar line chart downsamples in ~0.3 ms — well inside a 16.6 ms frame budget — then draws ~62× fewer points (100k → 1600).
1124
-
1125
- ### WebGL renderer (preview)
1126
-
1127
- `renderer: 'webgl'` draws the plot and indicator panes with WebGL 2, on a canvas under the 2D scene: the grid, sessions, candles and volume directly, and the Canvas 2D drawing of indicators, compare lines and most chart types recorded as GPU strokes, fills and rectangles, antialiased at their edges as Canvas 2D is. The depth heatmap and the volume and market profiles are recorded the same way, under the bars; a market profile showing its stats readout or letters stays with Canvas 2D. Text, drawings, orders, axes and the crosshair stay on Canvas 2D, as does anything the GPU wouldn't draw the same (left to Canvas 2D in order, so the stacking doesn't change); custom indicator plugins come along without changes. Candles land on the same device pixels as with Canvas 2D; lines and fills differ only on a few antialiased edge pixels. The WebGL code is a chunk of its own (about 17 KB gzipped), loaded on first use; where WebGL 2 is missing, or its context is lost, the chart carries on with Canvas 2D. If the context comes back, WebGL does too. At most 8 charts on a page draw with WebGL at once (`setMaxWebGLCharts`), as Chrome and Safari keep only about 16 WebGL contexts a page and drop the oldest past that; a chart past the limit draws with Canvas 2D until one is let go. Checked in Chrome, Firefox and WebKit.
1128
-
1129
- ```typescript
1130
- const chart = new Chart(el, { renderer: 'webgl' }) // or 'auto': WebGL on a hardware GPU only
1131
-
1132
- chart.on('rendererChange', (e) => console.log(e.payload))
1133
- // { renderer: 'webgl', reason?: 'contextRestored' }, or { renderer: 'canvas', reason: 'unsupported' | 'contextLost' | 'limit' }
1134
-
1135
- await chart.setRenderer('canvas') // resolves to what draws now
1136
-
1137
- setMaxWebGLCharts(4) // from '@tradecanvas/chart': at most 4 charts on the page draw with WebGL (8 by default)
165
+ npm install https://pkg.pr.new/@tradecanvas/chart@123 # pull request #123
166
+ npm install https://pkg.pr.new/@tradecanvas/chart@a1b2c3d # a commit on main
1138
167
  ```
1139
168
 
1140
- Frame time while panning, on integrated graphics (Intel UHD; 16.7 ms is 60 fps):
169
+ Each pull request gets a comment with the exact commands for every package.
1141
170
 
1142
- | Chart | Pixel ratio | Canvas 2D | WebGL |
1143
- |---|---|---|---|
1144
- | 1600×900, 500 candles + 4 indicators | 2 | 27.4 ms | 19.6 ms |
1145
- | 1600×900, zoomed out on 200,000 bars + 4 indicators | 2 | 34.5 ms | 20.2 ms |
1146
- | 1600×900, zoomed out on 1,000,000 bars + 4 indicators | 2 | 34.5 ms | 16.7 ms |
1147
- | 1600×900, depth heatmap, 240 snapshots × 80 levels | 2 | 25.5 ms | 16.8 ms |
1148
- | Six charts, 500 candles and two indicators each | 2 | 23.5 ms | 17.2 ms |
1149
- | 2560×1400, 2,000 candles + 4 indicators | 1 | 41.6 ms | 17.7 ms |
1150
- | 2560×1400, 2,000 candles + 4 indicators | 1.5 | 70.8 ms | 17.6 ms |
1151
- | 2560×1400, 2,000 candles + 4 indicators | 2 | 114.5 ms | 29.1 ms |
1152
- | 2560×1400, 2,000 candles | 2 | 33.1 ms | 20.9 ms |
171
+ ## Browser support
1153
172
 
1154
- Most WebGL frames above land on 16.7 ms; the averages carry a few longer ones. At a pixel ratio of 2 on a 2560×1400 chart, the browser's compositing of the full-size layers alone takes about 23 ms on this GPU. `node scripts/bench-render.mjs --renderer=webgl` runs these numbers on your own machine.
173
+ Chrome 80+, Firefox 80+, Safari 14+, Edge 80+.
1155
174
 
1156
- ## Architecture
175
+ ## AI coding tools
1157
176
 
1158
- Two stacked canvases — a hover repaints only the thin top one:
1159
-
1160
- ```
1161
- Top canvas (crosshair + axis pills, legend, countdown, measure) z=1
1162
- Scene canvas (grid, candles, indicators, drawings, orders, axes) z=0
1163
- ```
177
+ [`llms.txt`](https://bonguynvan.github.io/tradecanvas/llms.txt) and [`llms-full.txt`](https://bonguynvan.github.io/tradecanvas/llms-full.txt) give assistants the docs in one file, and the agent skill in [`skills/tradecanvas`](https://github.com/bonguynvan/tradecanvas/blob/main/skills/tradecanvas/SKILL.md) teaches coding agents the API with examples that CI type-checks.
1164
178
 
1165
179
  ## Related projects
1166
180
 
1167
- - **[bo-grid](https://github.com/bonguynvan/bo-grid)** — tiny, fast **Svelte 5** data grid for fintech UIs: canvas sparklines, batched realtime cell updates, virtual scrolling, grouping / pivot / tree data, and Excel export, with a core that gzips to ~32 KB. The table half of the same toolkit — pair it with TradeCanvas for a full trading desk. **[Live demo](https://bonguynvan.github.io/bo-grid/)**
181
+ - **[bo-grid](https://github.com/bonguynvan/bo-grid)**: a small, fast Svelte 5 data grid for fintech interfaces, the table half of a trading desk. [Live demo](https://bonguynvan.github.io/bo-grid/)
1168
182
 
1169
183
  ## Contributing
1170
184
 
1171
- Bug reports, ideas and pull requests are welcome. [CONTRIBUTING.md](https://github.com/bonguynvan/tradecanvas/blob/main/CONTRIBUTING.md) covers the setup (`pnpm install && pnpm build && pnpm test`), how the repo is laid out and what a pull request needs. Found a security issue? Please follow [SECURITY.md](https://github.com/bonguynvan/tradecanvas/blob/main/SECURITY.md) instead of opening an issue.
185
+ Bug reports, ideas and pull requests are welcome. [CONTRIBUTING.md](https://github.com/bonguynvan/tradecanvas/blob/main/CONTRIBUTING.md) covers the setup (`pnpm install && pnpm build && pnpm test`) and what a pull request needs. Found a security issue? Please follow [SECURITY.md](https://github.com/bonguynvan/tradecanvas/blob/main/SECURITY.md) instead of opening an issue.
186
+
187
+ ## Sponsor
1172
188
 
1173
- If TradeCanvas saves you time, a star on GitHub helps other developers find it.
189
+ TradeCanvas is free and MIT-licensed, built and maintained in the open. If it helps your product, you can support its development on [GitHub Sponsors](https://github.com/sponsors/bonguynvan).
1174
190
 
1175
191
  ## License
1176
192