@tradecanvas/chart 1.4.0 → 1.7.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.
- package/LICENSE +21 -21
- package/README.md +910 -858
- package/dist/Chart.d.ts +606 -23
- package/dist/Chart.d.ts.map +1 -1
- package/dist/DataManager.d.ts +5 -0
- package/dist/DataManager.d.ts.map +1 -1
- package/dist/HistoryPager.d.ts +58 -0
- package/dist/HistoryPager.d.ts.map +1 -0
- package/dist/LayoutSession-CTZB90NX.cjs +2 -0
- package/dist/LayoutSession-CTZB90NX.cjs.map +1 -0
- package/dist/LayoutSession-CWn3sdbs.js +3967 -0
- package/dist/LayoutSession-CWn3sdbs.js.map +1 -0
- package/dist/ThemeManager.d.ts +5 -1
- package/dist/ThemeManager.d.ts.map +1 -1
- package/dist/chartA11y.d.ts +59 -0
- package/dist/chartA11y.d.ts.map +1 -0
- package/dist/charts/ChartTypeStrategy.d.ts +5 -4
- package/dist/charts/ChartTypeStrategy.d.ts.map +1 -1
- package/dist/grid/ChartGrid.d.ts +6 -1
- package/dist/grid/ChartGrid.d.ts.map +1 -1
- package/dist/i18n-CiQ8EDVs.js +1469 -0
- package/dist/i18n-CiQ8EDVs.js.map +1 -0
- package/dist/i18n-WsLA9SmD.cjs +2 -0
- package/dist/i18n-WsLA9SmD.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +22 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -31
- package/dist/index.js.map +1 -1
- package/dist/indicatorLabel.d.ts +7 -0
- package/dist/indicatorLabel.d.ts.map +1 -0
- package/dist/layout/LayoutManager.d.ts +50 -0
- package/dist/layout/LayoutManager.d.ts.map +1 -1
- package/dist/layout/priceAxisWidth.d.ts +2 -0
- package/dist/layout/priceAxisWidth.d.ts.map +1 -1
- package/dist/regularHours.d.ts +10 -0
- package/dist/regularHours.d.ts.map +1 -0
- package/dist/state/LayoutSession.d.ts +101 -0
- package/dist/state/LayoutSession.d.ts.map +1 -0
- package/dist/state/layoutStorage.d.ts +46 -0
- package/dist/state/layoutStorage.d.ts.map +1 -0
- package/dist/trading/wireExecution.d.ts +7 -3
- package/dist/trading/wireExecution.d.ts.map +1 -1
- package/dist/widget/ChartWidget.d.ts +219 -10
- package/dist/widget/ChartWidget.d.ts.map +1 -1
- package/dist/widget/ChartWidgetGrid.d.ts +166 -0
- package/dist/widget/ChartWidgetGrid.d.ts.map +1 -0
- package/dist/widget/DrawingTemplateStore.d.ts +29 -8
- package/dist/widget/DrawingTemplateStore.d.ts.map +1 -1
- package/dist/widget/WatchlistStore.d.ts +65 -0
- package/dist/widget/WatchlistStore.d.ts.map +1 -0
- package/dist/widget/WidgetAccountPanel.d.ts +72 -0
- package/dist/widget/WidgetAccountPanel.d.ts.map +1 -0
- package/dist/widget/WidgetAlertsPanel.d.ts +50 -3
- package/dist/widget/WidgetAlertsPanel.d.ts.map +1 -1
- package/dist/widget/WidgetBracketBar.d.ts +3 -1
- package/dist/widget/WidgetBracketBar.d.ts.map +1 -1
- package/dist/widget/WidgetChartNav.d.ts +36 -0
- package/dist/widget/WidgetChartNav.d.ts.map +1 -0
- package/dist/widget/WidgetCommandPalette.d.ts +3 -1
- package/dist/widget/WidgetCommandPalette.d.ts.map +1 -1
- package/dist/widget/WidgetContextMenu.d.ts +40 -0
- package/dist/widget/WidgetContextMenu.d.ts.map +1 -0
- package/dist/widget/WidgetDataWindow.d.ts +3 -1
- package/dist/widget/WidgetDataWindow.d.ts.map +1 -1
- package/dist/widget/WidgetDepthLadder.d.ts +3 -1
- package/dist/widget/WidgetDepthLadder.d.ts.map +1 -1
- package/dist/widget/WidgetDrawingSettings.d.ts +108 -0
- package/dist/widget/WidgetDrawingSettings.d.ts.map +1 -0
- package/dist/widget/WidgetDrawingSidebar.d.ts +14 -1
- package/dist/widget/WidgetDrawingSidebar.d.ts.map +1 -1
- package/dist/widget/WidgetDrawingStyle.d.ts +3 -1
- package/dist/widget/WidgetDrawingStyle.d.ts.map +1 -1
- package/dist/widget/WidgetDropdown.d.ts.map +1 -1
- package/dist/widget/WidgetGoToDate.d.ts +5 -4
- package/dist/widget/WidgetGoToDate.d.ts.map +1 -1
- package/dist/widget/WidgetHistoryPill.d.ts +23 -0
- package/dist/widget/WidgetHistoryPill.d.ts.map +1 -0
- package/dist/widget/WidgetHotkeySheet.d.ts.map +1 -1
- package/dist/widget/WidgetIndicatorLegend.d.ts +31 -1
- package/dist/widget/WidgetIndicatorLegend.d.ts.map +1 -1
- package/dist/widget/WidgetIndicatorSettings.d.ts +62 -7
- package/dist/widget/WidgetIndicatorSettings.d.ts.map +1 -1
- package/dist/widget/WidgetIntervalInput.d.ts +34 -0
- package/dist/widget/WidgetIntervalInput.d.ts.map +1 -0
- package/dist/widget/WidgetLayoutsDialog.d.ts +39 -0
- package/dist/widget/WidgetLayoutsDialog.d.ts.map +1 -0
- package/dist/widget/WidgetLayoutsUI.d.ts +37 -0
- package/dist/widget/WidgetLayoutsUI.d.ts.map +1 -0
- package/dist/widget/WidgetNamePrompt.d.ts +31 -0
- package/dist/widget/WidgetNamePrompt.d.ts.map +1 -0
- package/dist/widget/WidgetObjectTree.d.ts +21 -3
- package/dist/widget/WidgetObjectTree.d.ts.map +1 -1
- package/dist/widget/WidgetOrderTicket.d.ts +49 -0
- package/dist/widget/WidgetOrderTicket.d.ts.map +1 -0
- package/dist/widget/WidgetReplayBar.d.ts +15 -0
- package/dist/widget/WidgetReplayBar.d.ts.map +1 -1
- package/dist/widget/WidgetSettings.d.ts +13 -1
- package/dist/widget/WidgetSettings.d.ts.map +1 -1
- package/dist/widget/WidgetStatusBar.d.ts +3 -0
- package/dist/widget/WidgetStatusBar.d.ts.map +1 -1
- package/dist/widget/WidgetStyles.d.ts +3 -1
- package/dist/widget/WidgetStyles.d.ts.map +1 -1
- package/dist/widget/WidgetSymbolInfo.d.ts +64 -0
- package/dist/widget/WidgetSymbolInfo.d.ts.map +1 -0
- package/dist/widget/WidgetSymbolSearch.d.ts +26 -5
- package/dist/widget/WidgetSymbolSearch.d.ts.map +1 -1
- package/dist/widget/WidgetToolbar.d.ts +30 -1
- package/dist/widget/WidgetToolbar.d.ts.map +1 -1
- package/dist/widget/WidgetWatchlist.d.ts +53 -13
- package/dist/widget/WidgetWatchlist.d.ts.map +1 -1
- package/dist/widget/chartMenu.d.ts +40 -0
- package/dist/widget/chartMenu.d.ts.map +1 -0
- package/dist/widget/drawingMenu.d.ts +21 -0
- package/dist/widget/drawingMenu.d.ts.map +1 -0
- package/dist/widget/focusTrap.d.ts +8 -0
- package/dist/widget/focusTrap.d.ts.map +1 -0
- package/dist/widget/i18n.d.ts +20 -89
- package/dist/widget/i18n.d.ts.map +1 -1
- package/dist/widget/iconSet.d.ts.map +1 -1
- package/dist/widget/index.cjs +4294 -3028
- package/dist/widget/index.cjs.map +1 -1
- package/dist/widget/index.d.ts +15 -1
- package/dist/widget/index.d.ts.map +1 -1
- package/dist/widget/index.js +6326 -1185
- package/dist/widget/index.js.map +1 -1
- package/dist/widget/indicatorTemplates.d.ts +24 -0
- package/dist/widget/indicatorTemplates.d.ts.map +1 -0
- package/dist/widget/keyTarget.d.ts +11 -0
- package/dist/widget/keyTarget.d.ts.map +1 -0
- package/dist/widget/legendValues.d.ts +11 -6
- package/dist/widget/legendValues.d.ts.map +1 -1
- package/dist/widget/locales/ar.d.ts +4 -0
- package/dist/widget/locales/ar.d.ts.map +1 -0
- package/dist/widget/locales/de.d.ts +4 -0
- package/dist/widget/locales/de.d.ts.map +1 -0
- package/dist/widget/locales/en.d.ts +722 -0
- package/dist/widget/locales/en.d.ts.map +1 -0
- package/dist/widget/locales/es.d.ts +4 -0
- package/dist/widget/locales/es.d.ts.map +1 -0
- package/dist/widget/locales/fr.d.ts +4 -0
- package/dist/widget/locales/fr.d.ts.map +1 -0
- package/dist/widget/locales/he.d.ts +4 -0
- package/dist/widget/locales/he.d.ts.map +1 -0
- package/dist/widget/locales/id.d.ts +4 -0
- package/dist/widget/locales/id.d.ts.map +1 -0
- package/dist/widget/locales/index.cjs +2 -0
- package/dist/widget/locales/index.cjs.map +1 -0
- package/dist/widget/locales/index.d.ts +33 -0
- package/dist/widget/locales/index.d.ts.map +1 -0
- package/dist/widget/locales/index.js +10089 -0
- package/dist/widget/locales/index.js.map +1 -0
- package/dist/widget/locales/ja.d.ts +4 -0
- package/dist/widget/locales/ja.d.ts.map +1 -0
- package/dist/widget/locales/ko.d.ts +4 -0
- package/dist/widget/locales/ko.d.ts.map +1 -0
- package/dist/widget/locales/pt.d.ts +4 -0
- package/dist/widget/locales/pt.d.ts.map +1 -0
- package/dist/widget/locales/ru.d.ts +4 -0
- package/dist/widget/locales/ru.d.ts.map +1 -0
- package/dist/widget/locales/th.d.ts +4 -0
- package/dist/widget/locales/th.d.ts.map +1 -0
- package/dist/widget/locales/tr.d.ts +4 -0
- package/dist/widget/locales/tr.d.ts.map +1 -0
- package/dist/widget/locales/vi.d.ts +4 -0
- package/dist/widget/locales/vi.d.ts.map +1 -0
- package/dist/widget/locales/zh.d.ts +4 -0
- package/dist/widget/locales/zh.d.ts.map +1 -0
- package/dist/widget/locales/zhHant.d.ts +4 -0
- package/dist/widget/locales/zhHant.d.ts.map +1 -0
- package/dist/widget/orderTicket.d.ts +27 -0
- package/dist/widget/orderTicket.d.ts.map +1 -0
- package/dist/widget/settingsControls.d.ts +24 -0
- package/dist/widget/settingsControls.d.ts.map +1 -0
- package/dist/widget/types.d.ts +182 -6
- package/dist/widget/types.d.ts.map +1 -1
- package/dist/widget/widgetConfig.d.ts.map +1 -1
- package/dist/widget/widgetLayout.d.ts +22 -0
- package/dist/widget/widgetLayout.d.ts.map +1 -0
- package/dist/widget/widgetLocales.d.ts +17 -0
- package/dist/widget/widgetLocales.d.ts.map +1 -0
- package/dist/widget/widgetTimeframes.d.ts +8 -0
- package/dist/widget/widgetTimeframes.d.ts.map +1 -1
- package/dist/widget/widgetTimezones.d.ts +32 -0
- package/dist/widget/widgetTimezones.d.ts.map +1 -0
- package/dist/widget/widgetUI.d.ts +126 -0
- package/dist/widget/widgetUI.d.ts.map +1 -0
- package/package.json +8 -3
- package/dist/DragDropImporter-C5kgfbET.cjs +0 -2
- package/dist/DragDropImporter-C5kgfbET.cjs.map +0 -1
- package/dist/DragDropImporter-Dd9Mud5J.js +0 -2099
- package/dist/DragDropImporter-Dd9Mud5J.js.map +0 -1
- package/dist/charts/IndicatorPriceRange.d.ts +0 -20
- package/dist/charts/IndicatorPriceRange.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,858 +1,910 @@
|
|
|
1
|
-
# @tradecanvas/chart
|
|
2
|
-
|
|
3
|
-
High-performance canvas trading chart with built-in indicators, drawing tools, and real-time streaming. Zero external dependencies.
|
|
4
|
-
|
|
5
|
-
**[Live Demo](https://bonguynvan.github.io/tradecanvas/)** | **[GitHub](https://github.com/bonguynvan/tradecanvas)** | **[npm](https://www.npmjs.com/package/@tradecanvas/chart)**
|
|
6
|
-
|
|
7
|
-
## Why TradeCanvas?
|
|
8
|
-
|
|
9
|
-
Most chart libraries make you choose: pretty charts with no trading features, or trading features with an ugly API. TradeCanvas gives you both.
|
|
10
|
-
|
|
11
|
-
- **70 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. No separate calculation library needed.
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **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.
|
|
15
|
-
- **Trading overlay** — Render open positions with entry line, P&L zone, and SL/TP markers. Orders as dashed lines. Drag SL/TP to modify. Cleanly opt-out via `features.trading: false` for non-trading projects.
|
|
16
|
-
- **Real-time streaming** — Built-in Binance adapter. Plug in your own data source with the adapter interface.
|
|
17
|
-
- **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.**
|
|
18
|
-
- **Replay mode** —
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
21
|
-
- **
|
|
22
|
-
- **
|
|
23
|
-
- **
|
|
24
|
-
- **
|
|
25
|
-
- **
|
|
26
|
-
- **
|
|
27
|
-
- **
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
|
88
|
-
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
|
149
|
-
|
|
150
|
-
|
|
|
151
|
-
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
|
183
|
-
|
|
184
|
-
| `--tcw-
|
|
185
|
-
| `--tcw-
|
|
186
|
-
| `--tcw-
|
|
187
|
-
| `--tcw-
|
|
188
|
-
| `--tcw-
|
|
189
|
-
| `--tcw-
|
|
190
|
-
| `--tcw-
|
|
191
|
-
| `--tcw-
|
|
192
|
-
| `--tcw-
|
|
193
|
-
| `--tcw-
|
|
194
|
-
| `--tcw-
|
|
195
|
-
| `--tcw-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
|
204
|
-
|
|
205
|
-
|
|
|
206
|
-
|
|
|
207
|
-
|
|
|
208
|
-
|
|
|
209
|
-
|
|
|
210
|
-
|
|
|
211
|
-
|
|
|
212
|
-
|
|
|
213
|
-
|
|
|
214
|
-
|
|
|
215
|
-
|
|
|
216
|
-
|
|
|
217
|
-
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
###
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
new
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
chart.
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
chart.
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
)
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
```
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
chart.
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
chart.
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
chart.
|
|
517
|
-
chart.
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
```
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
|
693
|
-
|
|
694
|
-
|
|
|
695
|
-
|
|
|
696
|
-
|
|
|
697
|
-
|
|
|
698
|
-
|
|
|
699
|
-
|
|
|
700
|
-
|
|
|
701
|
-
|
|
|
702
|
-
| `
|
|
703
|
-
| `
|
|
704
|
-
|
|
|
705
|
-
|
|
|
706
|
-
|
|
|
707
|
-
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
|
739
|
-
|
|
740
|
-
|
|
|
741
|
-
|
|
|
742
|
-
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
1
|
+
# @tradecanvas/chart
|
|
2
|
+
|
|
3
|
+
High-performance canvas trading chart with built-in indicators, drawing tools, and real-time streaming. Zero external dependencies.
|
|
4
|
+
|
|
5
|
+
**[Live Demo](https://bonguynvan.github.io/tradecanvas/)** | **[GitHub](https://github.com/bonguynvan/tradecanvas)** | **[npm](https://www.npmjs.com/package/@tradecanvas/chart)**
|
|
6
|
+
|
|
7
|
+
## Why TradeCanvas?
|
|
8
|
+
|
|
9
|
+
Most chart libraries make you choose: pretty charts with no trading features, or trading features with an ugly API. TradeCanvas gives you both.
|
|
10
|
+
|
|
11
|
+
- **70 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. No separate calculation library needed.
|
|
12
|
+
- **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.
|
|
13
|
+
- **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.
|
|
14
|
+
- **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.
|
|
15
|
+
- **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.
|
|
16
|
+
- **Real-time streaming** — Built-in Binance adapter. Plug in your own data source with the adapter interface.
|
|
17
|
+
- **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.**
|
|
18
|
+
- **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.
|
|
19
|
+
- **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.
|
|
20
|
+
- **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.
|
|
21
|
+
- **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.
|
|
22
|
+
- **Volume Profile** *(new in 0.9)* — optional horizontal histogram of traded volume bucketed by price over the visible range, with point-of-control highlighting.
|
|
23
|
+
- **Watchlist sidebar** *(new in 0.9)* — opt-in vertical panel listing symbols with last price, % change, mini sparkline. Click a row to switch chart.
|
|
24
|
+
- **CSV / JSON drag-and-drop** *(new in 0.9)* — 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.
|
|
25
|
+
- **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.
|
|
26
|
+
- **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.
|
|
27
|
+
- **Signal markers & trade zones** — render bot/algorithm output (directional arrows, entry→exit rectangles) as a first-class chart layer.
|
|
28
|
+
- **Hotkey sheet** *(new in 0.9)* — press `?` in the widget to open a categorized keyboard-shortcut reference.
|
|
29
|
+
- **Extensible widget** — add your own toolbar buttons and right-click menu entries (`addToolbarButton`, `chartMenuItems`).
|
|
30
|
+
- **Save/load chart state** — Persist drawings, indicators, theme, and chart type to JSON. Restore with one call.
|
|
31
|
+
- **Zero dependencies** — The entire library is self-contained. No `d3`, no `chart.js`, no `fancy-canvas`.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install @tradecanvas/chart
|
|
37
|
+
# or
|
|
38
|
+
pnpm add @tradecanvas/chart
|
|
39
|
+
# or
|
|
40
|
+
yarn add @tradecanvas/chart
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
The fastest path is `ChartWidget` — drop-in component with a full trading UI (toolbar, drawing sidebar, settings dialog, status bar). Zero framework dependency.
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { ChartWidget } from '@tradecanvas/chart/widget'
|
|
49
|
+
import { BinanceAdapter } from '@tradecanvas/chart'
|
|
50
|
+
|
|
51
|
+
const widget = new ChartWidget(document.getElementById('chart')!, {
|
|
52
|
+
symbol: 'BTCUSDT',
|
|
53
|
+
timeframe: '5m',
|
|
54
|
+
theme: 'dark',
|
|
55
|
+
adapter: new BinanceAdapter(),
|
|
56
|
+
trading: true,
|
|
57
|
+
})
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
That's it. Live data, all 95 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.
|
|
61
|
+
|
|
62
|
+
## Headless Chart
|
|
63
|
+
|
|
64
|
+
For projects that want to own the surrounding UI (custom toolbar, framework-specific controls), use the lower-level `Chart` class directly:
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { Chart, BinanceAdapter } from '@tradecanvas/chart'
|
|
68
|
+
|
|
69
|
+
const chart = new Chart(document.getElementById('chart')!, {
|
|
70
|
+
theme: 'dark',
|
|
71
|
+
autoScale: true,
|
|
72
|
+
features: {
|
|
73
|
+
drawings: true,
|
|
74
|
+
indicators: true,
|
|
75
|
+
trading: true, // set false to disable orders/positions entirely
|
|
76
|
+
tradingContextMenu: true, // opt-in right-click order menu (off by default)
|
|
77
|
+
volume: true,
|
|
78
|
+
},
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
const adapter = new BinanceAdapter()
|
|
82
|
+
chart.connect({ adapter, symbol: 'BTCUSDT', timeframe: '5m', historyLimit: 300 })
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Widget Options
|
|
86
|
+
|
|
87
|
+
| Option | Type | Default | Description |
|
|
88
|
+
|---|---|---|---|
|
|
89
|
+
| `symbol` | `string` | `'BTCUSDT'` | Initial trading symbol |
|
|
90
|
+
| `timeframe` | `TimeFrame` | `'5m'` | Initial timeframe |
|
|
91
|
+
| `theme` | `'dark' \| 'light' \| Theme` | `'dark'` | Chart theme |
|
|
92
|
+
| `adapter` | `DataAdapter` | — | Data source adapter |
|
|
93
|
+
| `toolbar` | `boolean` | `true` | Show top toolbar |
|
|
94
|
+
| `drawingTools` | `boolean` | `true` | Show left drawing sidebar |
|
|
95
|
+
| `settings` | `boolean` | `true` | Show settings button |
|
|
96
|
+
| `trading` | `boolean` | `true` | Enable trading overlay |
|
|
97
|
+
| `statusBar` | `boolean` | `true` | Show bottom status bar |
|
|
98
|
+
| `rangeBar` | `boolean` | `true` | Range presets (1D … All) and go to date (Alt+G) on the status bar |
|
|
99
|
+
| `indicatorLegend` | `boolean` | `true` | Indicators listed on the chart (under the OHLCV legend and atop their panes) with show / settings / remove |
|
|
100
|
+
| `fullscreen` | `boolean` | `true` | Fullscreen button in the toolbar |
|
|
101
|
+
| `symbols` | `string[]` | BTC/ETH/SOL/BNB | Searchable symbol catalog |
|
|
102
|
+
| `timeframes` | `TimeFrame[]` | 1m to 1M | Timeframes on offer; pin favourites from the ▾ menu |
|
|
103
|
+
| `chartTypes` | `ChartType[]` | 18 types | Available chart types |
|
|
104
|
+
| `watchlist` | `boolean` | `false` | Right-side watchlist sidebar |
|
|
105
|
+
| `dragDropImport` | `boolean` | `true` | Drop CSV / JSON files onto the chart to load data |
|
|
106
|
+
| `persistLayouts` | `boolean \| { keyPrefix, debounceMs }` | `false` | Save per-symbol indicators / drawings / chart type to localStorage |
|
|
107
|
+
| `onSymbolChange` | `(symbol) => void` | — | Symbol change callback |
|
|
108
|
+
| `onTimeframeChange` | `(tf) => void` | — | Timeframe change callback |
|
|
109
|
+
| `onReady` | `(chart) => void` | — | Fired when chart is ready |
|
|
110
|
+
| `locale` | `string` | `'en'` | UI chrome language — built-in `'en'` / `'vi'`, see **Widget i18n** below |
|
|
111
|
+
| `messages` | `Partial<Record<MessageKey, string>>` | — | Override or add individual UI strings on top of `locale` |
|
|
112
|
+
|
|
113
|
+
### Icons
|
|
114
|
+
|
|
115
|
+
The widget's icon set is exported for your own UI: `createIcon(name)`,
|
|
116
|
+
`createToolIcon(drawingTool)`, `createChartTypeIcon(chartType)` return inline
|
|
117
|
+
SVG strings drawn in `currentColor` (24 px grid, 1.75 px strokes).
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
import { createToolIcon } from '@tradecanvas/chart/widget'
|
|
121
|
+
button.innerHTML = createToolIcon('fibRetracement', 16)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Widget i18n
|
|
125
|
+
|
|
126
|
+
`locale` and `messages` translate `ChartWidget`'s own chrome — toolbar, watchlist, indicator-picker section headers (Popular/All, overlay/panel tags), status bar, settings panel (titles/tabs/section headers), and hotkey sheet (title/group headers). Set at construction; not currently hot-swappable at runtime.
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
new ChartWidget(el, {
|
|
130
|
+
locale: 'vi', // built-in Vietnamese table
|
|
131
|
+
messages: { 'watchlist.title': 'Theo dõi' }, // override/add individual keys — always wins
|
|
132
|
+
chartOptions: { numberLocale: 'vi-VN' }, // separate: number/date formatting (see below)
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`locale`/`messages` only cover chrome **text**; they're independent of `chartOptions.numberLocale`, which controls number/date **formatting** (price axis, legend, watchlist prices, current-price tag, session-break dates) via `Intl`/`toLocaleString`.
|
|
137
|
+
|
|
138
|
+
Not yet covered by `locale` (still English; PRs welcome, or override via `messages`/your own CSS):
|
|
139
|
+
- Indicator and drawing-tool **names** (SMA, Bollinger Bands, Trend Line, …) — these come from `widgetConfig.ts`'s data tables, not the message catalog.
|
|
140
|
+
- Individual settings rows beyond the tab/section level (e.g. "Up Body", "Grid Lines").
|
|
141
|
+
- Individual hotkey-sheet shortcut labels and key-cap text (group titles are translated).
|
|
142
|
+
- Alerts panel, symbol search, command palette, data window, depth ladder, bracket bar, replay bar, drawing-style panel, object tree.
|
|
143
|
+
|
|
144
|
+
See `packages/library/src/widget/i18n.ts` for the full key list (`MessageKey`) and the English/Vietnamese tables.
|
|
145
|
+
|
|
146
|
+
### Widget vs Headless
|
|
147
|
+
|
|
148
|
+
| | `Chart` (headless) | `ChartWidget` |
|
|
149
|
+
|---|---|---|
|
|
150
|
+
| Import | `@tradecanvas/chart` | `@tradecanvas/chart/widget` |
|
|
151
|
+
| UI included | None — build your own | Complete toolbar, sidebar, settings |
|
|
152
|
+
| Bundle impact | ~50 KB gzip | ~65 KB gzip (includes UI) |
|
|
153
|
+
| Framework | Any (React, Vue, Svelte, vanilla) | Vanilla JS DOM (works everywhere) |
|
|
154
|
+
| Customization | Full control | Toggle sections on/off |
|
|
155
|
+
| Advanced access | Direct API | `widget.getChart()` for direct API |
|
|
156
|
+
|
|
157
|
+
### Widget Look
|
|
158
|
+
|
|
159
|
+
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:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
const widget = new ChartWidget(host, { ui: 'terminal' });
|
|
163
|
+
widget.setUI({ preset: 'studio', radius: { md: 10 }, density: 'compact', toolbar: 'floating', active: 'solid' });
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
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).
|
|
167
|
+
|
|
168
|
+
### Widget Theming
|
|
169
|
+
|
|
170
|
+
`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.
|
|
171
|
+
|
|
172
|
+
```css
|
|
173
|
+
/* Dark is the default (no attribute needed); light sets data-tcw-theme="light" */
|
|
174
|
+
.my-app .tcw-root:not([data-tcw-theme="light"]) {
|
|
175
|
+
--tcw-bg: #0a0a0f;
|
|
176
|
+
--tcw-accent: #7c5cff;
|
|
177
|
+
--tcw-radius: 0px;
|
|
178
|
+
--tcw-radius-lg: 0px;
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
| Variable | Default (dark) | Purpose |
|
|
183
|
+
|---|---|---|
|
|
184
|
+
| `--tcw-bg` | `#080b10` | Root background |
|
|
185
|
+
| `--tcw-bg-surface` | `#0c1016` | Panel / toolbar surface |
|
|
186
|
+
| `--tcw-bg-elevated` | `#141922` | Popovers, dropdowns, modals |
|
|
187
|
+
| `--tcw-bg-overlay` | `rgba(20,25,34,.5)` | Backdrop behind overlays |
|
|
188
|
+
| `--tcw-border` | `#1f2630` | Default border |
|
|
189
|
+
| `--tcw-border-strong` | `#2a323e` | Emphasized border (focus rings, dividers) |
|
|
190
|
+
| `--tcw-text` | `#e7e9ee` | Primary text |
|
|
191
|
+
| `--tcw-text-dim` | `#aab1bd` | Secondary text |
|
|
192
|
+
| `--tcw-text-muted` | `#758091` | Tertiary / placeholder text |
|
|
193
|
+
| `--tcw-accent` | `#f2a93b` | Primary accent (active tab, focus, links) |
|
|
194
|
+
| `--tcw-accent-ink` | `#1a1204` | Text and icons on an accent fill |
|
|
195
|
+
| `--tcw-accent-hover` | `#f5b95c` | Accent hover state |
|
|
196
|
+
| `--tcw-accent-soft` | `rgba(242,169,59,.14)` | Accent tint (selected row background) |
|
|
197
|
+
| `--tcw-accent-glow` | `rgba(242,169,59,.22)` | Accent glow (focus halo) |
|
|
198
|
+
| `--tcw-accent-line` | `rgba(242,169,59,.55)` | Accent border/underline |
|
|
199
|
+
| `--tcw-red` / `--tcw-red-soft` | `#e8505b` / tint | Down/sell/negative |
|
|
200
|
+
| `--tcw-green` / `--tcw-green-soft` | `#1fa874` / tint | Up/buy/positive |
|
|
201
|
+
| `--tcw-amber` | `#ff9f43` | Warning |
|
|
202
|
+
| `--tcw-hover-bg` | `rgba(255,255,255,.05)` | Row/button hover background |
|
|
203
|
+
| `--tcw-active-bg` | `rgba(255,255,255,.08)` | Row/button pressed background |
|
|
204
|
+
| `--tcw-divider` | `rgba(255,255,255,.06)` | Hairline dividers |
|
|
205
|
+
| `--tcw-ease` / `--tcw-ease-out` | cubic-bezier | Transition easing |
|
|
206
|
+
| `--tcw-dur-fast` / `-normal` / `-slow` | `120ms` / `180ms` / `260ms` | Transition durations |
|
|
207
|
+
| `--tcw-radius-xs` / `-sm` / `--tcw-radius` / `-lg` / `-xl` | `3px` / `5px` / `7px` / `11px` / `16px` | The corner scale — set to `0` for a square look |
|
|
208
|
+
| `--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 |
|
|
209
|
+
| `--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 |
|
|
210
|
+
| `--tcw-font` / `--tcw-font-size` / `--tcw-weight` / `--tcw-weight-strong` | `'Manrope', 'Inter', …` / `13px` / `500` / `600` | Type |
|
|
211
|
+
| `--tcw-label-case` / `--tcw-label-tracking` | `none` / `0em` | Small labels (section titles) |
|
|
212
|
+
| `--tcw-border-w` / `--tcw-sep-w` | `1px` / `0px` | Border width; rules between toolbar groups |
|
|
213
|
+
| `--tcw-menu-shadow` / `--tcw-dialog-shadow` / `--tcw-tooltip-shadow` | the elevation shadows | Shadows of menus, dialogs, tooltips |
|
|
214
|
+
| `--tcw-blur` / `--tcw-surface-opacity` | `0px` / `100%` | Frosted menus |
|
|
215
|
+
| `--tcw-shadow-sm` / `-md` / `-lg` / `-xl` | box-shadow values | Elevation |
|
|
216
|
+
| `--tcw-ring` | `0 0 0 2px rgba(242,169,59,.45)` | Focus ring |
|
|
217
|
+
| `--tcw-font-mono` | `'JetBrains Mono', …` | Monospace font stack (price ladder, code) |
|
|
218
|
+
|
|
219
|
+
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.
|
|
220
|
+
|
|
221
|
+
## Features
|
|
222
|
+
|
|
223
|
+
### Chart Types
|
|
224
|
+
|
|
225
|
+
| Type | Description |
|
|
226
|
+
|---|---|
|
|
227
|
+
| Candlestick | Standard OHLC candles |
|
|
228
|
+
| Hollow Candle | Open/close determines fill |
|
|
229
|
+
| Bar (OHLC) | Classic open-high-low-close bars |
|
|
230
|
+
| Line | Close price line |
|
|
231
|
+
| Area | Filled area below close |
|
|
232
|
+
| Baseline | Two-tone area split at a reference price |
|
|
233
|
+
| Heikin-Ashi | Smoothed candles for trend identification |
|
|
234
|
+
| Renko | Fixed-size bricks that ignore time |
|
|
235
|
+
| Kagi | Reversal-based line chart |
|
|
236
|
+
| Point & Figure | X/O columns for supply/demand analysis |
|
|
237
|
+
| Line Break | Three-line break charts |
|
|
238
|
+
| Range Bars | Fixed price-range bars — each bar's high − low equals a configured range |
|
|
239
|
+
| Volume Candles | Candlesticks with width proportional to volume |
|
|
240
|
+
| Equivolume | Full-range boxes with width proportional to volume share (Richard Arms style) |
|
|
241
|
+
| HLC Area | High-low-close area band with close line |
|
|
242
|
+
| Step Line | Staircase/step pattern from close prices |
|
|
243
|
+
| Line with Markers | Close line with circular markers at each data point |
|
|
244
|
+
|
|
245
|
+
### Multi-Chart Grid
|
|
246
|
+
|
|
247
|
+
Display multiple synchronized charts side-by-side with linked crosshairs and time axis:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
import { ChartGrid, BinanceAdapter } from '@tradecanvas/chart'
|
|
251
|
+
|
|
252
|
+
const grid = new ChartGrid(document.getElementById('grid')!, {
|
|
253
|
+
layout: '2x2',
|
|
254
|
+
syncCrosshair: true,
|
|
255
|
+
syncTimeAxis: true,
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
// An adapter keeps one stream: give each chart its own
|
|
259
|
+
grid.connectAll(() => new BinanceAdapter(), ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT'], '5m')
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
With the full widget on each chart, a bar to pick the arrangement and sync, and the whole grid saved as a named layout:
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
import { ChartWidgetGrid } from '@tradecanvas/chart/widget'
|
|
266
|
+
|
|
267
|
+
const workspace = new ChartWidgetGrid(document.getElementById('grid')!, {
|
|
268
|
+
layout: '1x2',
|
|
269
|
+
adapter: () => new BinanceAdapter(),
|
|
270
|
+
cells: [{ symbol: 'BTCUSDT' }, { symbol: 'ETHUSDT', timeframe: '1h' }],
|
|
271
|
+
sync: { crosshair: true, interval: false, symbol: false, time: false, drawings: false },
|
|
272
|
+
})
|
|
273
|
+
workspace.setSync({ time: true })
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Supported layouts: `'1x1'`, `'1x2'`, `'2x1'`, `'2x2'`, `'1x3'`, `'3x1'`, `'2x3'`, `'3x2'`.
|
|
277
|
+
|
|
278
|
+
### Command Palette
|
|
279
|
+
|
|
280
|
+
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).
|
|
281
|
+
|
|
282
|
+
### Finance Charts
|
|
283
|
+
|
|
284
|
+
| Chart | Description |
|
|
285
|
+
|---|---|
|
|
286
|
+
| SparklineChart | Tiny inline line/area chart from a number array — for dashboards and KPI cards |
|
|
287
|
+
| DepthChart | Bid/ask order book visualization with cumulative volume areas |
|
|
288
|
+
| EquityCurveChart | Portfolio equity line with drawdown shading and benchmark comparison |
|
|
289
|
+
| HeatmapChart | Colored cell grid with treemap layout — for sector/market performance |
|
|
290
|
+
| WaterfallChart | Running cumulative bars — P&L attribution, revenue bridge, cash flow |
|
|
291
|
+
| GaugeChart | Speedometer-style gauge — KPIs, risk scores, Fear & Greed index |
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
import {
|
|
295
|
+
SparklineChart, DepthChart, EquityCurveChart, HeatmapChart,
|
|
296
|
+
WaterfallChart, GaugeChart,
|
|
297
|
+
} from '@tradecanvas/chart'
|
|
298
|
+
|
|
299
|
+
// Sparkline in a 120x48 container
|
|
300
|
+
new SparklineChart(el, { data: [100, 102, 98, 105, 103], mode: 'area', color: '#1fa874' })
|
|
301
|
+
|
|
302
|
+
// Equity curve with drawdown
|
|
303
|
+
new EquityCurveChart(el, { data: equityPoints, drawdown: true, benchmark: spyData })
|
|
304
|
+
|
|
305
|
+
// Order book depth
|
|
306
|
+
new DepthChart(el, { data: { bids, asks }, crosshair: true })
|
|
307
|
+
|
|
308
|
+
// Market heatmap (treemap weighted by market cap)
|
|
309
|
+
new HeatmapChart(el, { data: cells, weighted: true })
|
|
310
|
+
|
|
311
|
+
// P&L waterfall
|
|
312
|
+
new WaterfallChart(el, {
|
|
313
|
+
data: [
|
|
314
|
+
{ label: 'Start', value: 10000, type: 'total' },
|
|
315
|
+
{ label: 'Gain', value: 1850 },
|
|
316
|
+
{ label: 'Loss', value: -620 },
|
|
317
|
+
{ label: 'End', value: 11230, type: 'total' },
|
|
318
|
+
],
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
// Fear & Greed gauge: zones light up to the value, the label shows the current zone
|
|
322
|
+
const gauge = new GaugeChart(el, {
|
|
323
|
+
value: 72,
|
|
324
|
+
label: 'Fear & Greed',
|
|
325
|
+
zones: [
|
|
326
|
+
{ from: 0, to: 25, color: '#e8505b', label: 'Extreme fear' },
|
|
327
|
+
{ from: 25, to: 45, color: '#f2a93b', label: 'Fear' },
|
|
328
|
+
{ from: 45, to: 55, color: '#8a93a3', label: 'Neutral' },
|
|
329
|
+
{ from: 55, to: 75, color: '#62c895', label: 'Greed' },
|
|
330
|
+
{ from: 75, to: 100, color: '#1fa874', label: 'Extreme greed' },
|
|
331
|
+
],
|
|
332
|
+
// pointer: 'needle', // classic needle instead of the ring marker
|
|
333
|
+
})
|
|
334
|
+
gauge.setValue(85) // animates smoothly
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Indicators (built-in)
|
|
338
|
+
|
|
339
|
+
95 indicators — moving averages (SMA, EMA, WMA, Hull, DEMA, TEMA, ALMA, KAMA,
|
|
340
|
+
LSMA, McGinley, SMMA, MA Cross, MTF MA), bands and channels (Bollinger,
|
|
341
|
+
Keltner, Donchian, Envelope, Linear Regression), trend and stops (Ichimoku,
|
|
342
|
+
Supertrend, Parabolic SAR, Chandelier, Chande Kroll Stop, Alligator, ZigZag,
|
|
343
|
+
Fractals, Pivot Points), VWAPs and Volume Profile on the price pane; RSI, MACD,
|
|
344
|
+
Stochastic, ATR, ADX, CCI, OBV, MFI, Bollinger %B and BandWidth, Historical
|
|
345
|
+
Volatility, Ulcer Index and 40 more oscillators, volume and volatility
|
|
346
|
+
indicators in panes. The [indicator catalog](https://bonguynvan.github.io/tradecanvas/docs/indicators)
|
|
347
|
+
lists every id with its inputs, lines and levels.
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
import { indicatorSource } from '@tradecanvas/chart'
|
|
351
|
+
|
|
352
|
+
const rsi = chart.addIndicator('rsi', { period: 14 })!
|
|
353
|
+
chart.addIndicator('ema', { period: 21, source: 'hlc3' }) // another price
|
|
354
|
+
chart.addIndicator('sma', { period: 9, source: indicatorSource(rsi, 'value') }) // RSI's own average, in RSI's pane
|
|
355
|
+
chart.setIndicatorLevels(rsi, [20, 50, 80])
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
- **Sources**: close, open, high, low, hl2, hlc3, ohlc4, hlcc4, or another indicator's line.
|
|
359
|
+
- **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`).
|
|
360
|
+
- **Undo and templates**: Ctrl/Cmd+Z undoes indicator changes, in the same history as drawings; ChartWidget saves the indicators as named templates (`getIndicatorSetup` / `applyIndicatorSetup`).
|
|
361
|
+
- **Levels**: editable per instance (RSI 30/70, CCI ±100 …), kept in saved layouts.
|
|
362
|
+
- **Value tags**: each line's latest value on its axis, in its colour.
|
|
363
|
+
- **Custom indicators** declare their lines (`plots`), scale, levels and inputs; the chart draws and labels them.
|
|
364
|
+
|
|
365
|
+
Invalid parameters (NaN, Infinity, non-numeric strings, missing keys) fall back
|
|
366
|
+
to the defaults instead of reaching the calculations.
|
|
367
|
+
|
|
368
|
+
### Drawing Tools
|
|
369
|
+
|
|
370
|
+
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
|
|
371
|
+
|
|
372
|
+
All drawing tools support:
|
|
373
|
+
- Click-to-place with magnet snapping to OHLC values
|
|
374
|
+
- Undo / redo (Ctrl+Z / Ctrl+Y)
|
|
375
|
+
- Serialization for save/load
|
|
376
|
+
- Custom styles (color, width, dash pattern)
|
|
377
|
+
|
|
378
|
+
### Trading Overlay
|
|
379
|
+
|
|
380
|
+
Render open positions and pending orders directly on the chart, like MT4/MT5.
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
import type { TradingPosition, TradingOrder } from '@tradecanvas/chart'
|
|
384
|
+
|
|
385
|
+
chart.setPositions([{
|
|
386
|
+
id: 'pos-1',
|
|
387
|
+
side: 'buy',
|
|
388
|
+
entryPrice: 3500,
|
|
389
|
+
quantity: 1.5,
|
|
390
|
+
closedQuantity: 0.5, // partial close — visualized as a left-edge dim band
|
|
391
|
+
stopLoss: 3400,
|
|
392
|
+
takeProfit: 3700,
|
|
393
|
+
}])
|
|
394
|
+
|
|
395
|
+
chart.setOrders([{
|
|
396
|
+
id: 'order-1',
|
|
397
|
+
side: 'sell',
|
|
398
|
+
type: 'limit',
|
|
399
|
+
price: 3800,
|
|
400
|
+
quantity: 0.5,
|
|
401
|
+
label: 'TP',
|
|
402
|
+
draggable: true,
|
|
403
|
+
}])
|
|
404
|
+
|
|
405
|
+
// Customize the position zone color via P&L thresholds
|
|
406
|
+
chart.setTradingConfig({
|
|
407
|
+
pnlThresholds: [
|
|
408
|
+
{ pnl: -Infinity, color: '#b91c1c' },
|
|
409
|
+
{ pnl: 0, color: '#94a3b8' },
|
|
410
|
+
{ pnl: 50, color: '#16a34a' },
|
|
411
|
+
{ pnl: 200, color: '#15803d' },
|
|
412
|
+
],
|
|
413
|
+
// Custom label template — tokens: {side} {qty} {openQty} {closedQty} {entry} {price} {pnl} {pnlPct} {pnlSign}
|
|
414
|
+
positionLabel: '{side} {openQty}/{qty} @ {entry} | {pnlSign}{pnl} ({pnlPct})',
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
// Listen for user drag-to-modify
|
|
418
|
+
chart.on('positionModify', (e) => console.log('SL/TP moved:', e.payload))
|
|
419
|
+
chart.on('orderModify', (e) => console.log('Order moved:', e.payload))
|
|
420
|
+
|
|
421
|
+
// The × and ⇅ buttons on the lines raise these; so can your own UI
|
|
422
|
+
chart.cancelOrderIntent('order-1')
|
|
423
|
+
chart.reversePositionIntent('pos-1')
|
|
424
|
+
chart.on('executionFill', (e) => console.log(e.payload.reason, e.payload.pnl))
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Signal Markers
|
|
428
|
+
|
|
429
|
+
Visualize buy/sell signals from bots, indicators, or manual analysis.
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
chart.addSignalMarker({
|
|
433
|
+
time: 1715692800000,
|
|
434
|
+
price: 62500,
|
|
435
|
+
direction: 'long',
|
|
436
|
+
confidence: 0.85,
|
|
437
|
+
source: 'ema-crossover',
|
|
438
|
+
label: 'EMA Cross',
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
// Color-code by source
|
|
442
|
+
chart.setSignalMarkerStyle({
|
|
443
|
+
sourceColors: {
|
|
444
|
+
'ema-crossover': '#4c8dff',
|
|
445
|
+
'rsi-divergence': '#f2a93b',
|
|
446
|
+
'whale-flow': '#9C27B0',
|
|
447
|
+
},
|
|
448
|
+
})
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Trade Zones
|
|
452
|
+
|
|
453
|
+
Render entry→exit rectangles with P&L coloring for executed trades.
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
const zoneId = chart.addTradeZone({
|
|
457
|
+
entryTime: 1715692800000,
|
|
458
|
+
entryPrice: 62500,
|
|
459
|
+
exitTime: 1715700000000,
|
|
460
|
+
exitPrice: 63200,
|
|
461
|
+
direction: 'long',
|
|
462
|
+
pnl: 140,
|
|
463
|
+
pnlPercent: 1.12,
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
// Update a live trade when it closes
|
|
467
|
+
chart.updateTradeZone(zoneId, {
|
|
468
|
+
exitTime: Date.now(),
|
|
469
|
+
exitPrice: 63500,
|
|
470
|
+
pnl: 200,
|
|
471
|
+
})
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Real-Time Streaming
|
|
475
|
+
|
|
476
|
+
```typescript
|
|
477
|
+
// Built-in Binance adapter (free, no API key)
|
|
478
|
+
chart.connect({
|
|
479
|
+
adapter: new BinanceAdapter(),
|
|
480
|
+
symbol: 'ETHUSDT',
|
|
481
|
+
timeframe: '1m',
|
|
482
|
+
historyLimit: 500,
|
|
483
|
+
})
|
|
484
|
+
|
|
485
|
+
// Or manual data feed
|
|
486
|
+
chart.setData(historicalBars)
|
|
487
|
+
chart.appendBar(newBar)
|
|
488
|
+
chart.updateLastBar(updatedBar)
|
|
489
|
+
chart.setCurrentPrice(3500.42)
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Indicators outside the chart
|
|
493
|
+
|
|
494
|
+
`IndicatorWorkerHost` computes an indicator from bars with the same messages a
|
|
495
|
+
Web Worker would use. The worker script is not part of the published packages
|
|
496
|
+
yet; pass `null` and register the plugins to compute in place (SSR, tests,
|
|
497
|
+
scripts):
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
import { IndicatorWorkerHost, RSIIndicator } from '@tradecanvas/core'
|
|
501
|
+
|
|
502
|
+
const host = new IndicatorWorkerHost(null)
|
|
503
|
+
host.registerFallbackPlugin(new RSIIndicator())
|
|
504
|
+
const output = await host.calculate('rsi', { id: 'rsi', instanceId: 'rsi-1', params: { period: 14 } }, bars)
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### Save / Load
|
|
508
|
+
|
|
509
|
+
```typescript
|
|
510
|
+
const json = chart.saveState()
|
|
511
|
+
localStorage.setItem('my-chart', json!)
|
|
512
|
+
|
|
513
|
+
chart.loadState(localStorage.getItem('my-chart')!)
|
|
514
|
+
|
|
515
|
+
// Download / upload files
|
|
516
|
+
chart.downloadState('my-chart.json')
|
|
517
|
+
await chart.loadStateFromFile()
|
|
518
|
+
|
|
519
|
+
// Or keep a layout saved as it changes (debounced)
|
|
520
|
+
chart.setAutoSave('my-chart', 1500)
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
A saved layout holds the chart type, theme, drawings, indicators (inputs, pane,
|
|
524
|
+
colours, visibility) and alerts, including alerts on indicator lines.
|
|
525
|
+
|
|
526
|
+
### Themes
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
import { DARK_THEME, LIGHT_THEME, DARK_TERMINAL } from '@tradecanvas/chart'
|
|
530
|
+
|
|
531
|
+
// Built-in presets: DARK_THEME, LIGHT_THEME, DARK_TERMINAL
|
|
532
|
+
chart.setTheme(DARK_TERMINAL) // fintech terminal: #0E0E0E bg, #00FF87/#FF3B4D candles, monospace
|
|
533
|
+
|
|
534
|
+
// Or customize any preset
|
|
535
|
+
chart.setTheme({
|
|
536
|
+
...DARK_THEME,
|
|
537
|
+
candleUp: '#1fa874',
|
|
538
|
+
candleDown: '#e8505b',
|
|
539
|
+
background: '#0a0a0f',
|
|
540
|
+
})
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### Events
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
chart.on('crosshairMove', (e) => { /* { point, bar, barIndex, indicatorValues } — also over indicator panes */ })
|
|
547
|
+
chart.on('crosshairLeave', () => { /* the pointer left the plot */ })
|
|
548
|
+
chart.on('drawingToolChange', (e) => { /* { tool } — null once a drawing is finished or cancelled */ })
|
|
549
|
+
chart.on('indicatorUpdate', (e) => { /* { from } — indicator values recomputed from this bar on */ })
|
|
550
|
+
chart.on('paneResize', (e) => { /* { instanceId, size } — an indicator pane was resized */ })
|
|
551
|
+
chart.on('indicatorChange', (e) => { /* { instanceId, change } — shown/hidden, restyled, levels, inputs or pane changed */ })
|
|
552
|
+
chart.on('barClick', (e) => { /* { bar, barIndex, point } */ })
|
|
553
|
+
chart.on('visibleRangeChange', (e) => { /* { from, to } — bar indices, not timestamps */ })
|
|
554
|
+
chart.on('priceRangeChange', (e) => { /* { min, max } — visible price bounds */ })
|
|
555
|
+
chart.on('zoomChange', (e) => { /* { barWidth } — pixels per bar */ })
|
|
556
|
+
chart.on('drawingCreate', (e) => { /* ... */ })
|
|
557
|
+
chart.on('orderModify', (e) => { /* ... */ })
|
|
558
|
+
chart.on('positionModify', (e) => { /* ... */ })
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
`visibleRangeChange`, `priceRangeChange`, and `zoomChange` fire on every pan,
|
|
562
|
+
zoom, resize, and data update — but only when that piece of viewport state
|
|
563
|
+
actually changed. Resolve a `visibleRangeChange` index to time with
|
|
564
|
+
`chart.getData()[e.payload.from].time`.
|
|
565
|
+
|
|
566
|
+
### Replay Mode
|
|
567
|
+
|
|
568
|
+
`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).
|
|
569
|
+
|
|
570
|
+
```typescript
|
|
571
|
+
import { ReplayController } from '@tradecanvas/chart'
|
|
572
|
+
|
|
573
|
+
const replay = new ReplayController({
|
|
574
|
+
data: historicalBars,
|
|
575
|
+
speed: 10, // bars per second
|
|
576
|
+
startIndex: 0,
|
|
577
|
+
})
|
|
578
|
+
|
|
579
|
+
// Seed the chart with the prefix before replay starts
|
|
580
|
+
chart.setData(replay.getPrefix())
|
|
581
|
+
|
|
582
|
+
// Each emitted bar drives the chart forward
|
|
583
|
+
replay.on('bar', ({ bar }) => chart.appendBar(bar))
|
|
584
|
+
replay.on('finished', () => console.log('done'))
|
|
585
|
+
|
|
586
|
+
replay.start()
|
|
587
|
+
// replay.pause(); replay.resume(); replay.step(5); replay.seek(200); replay.setSpeed(20)
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### Chart Interaction
|
|
591
|
+
|
|
592
|
+
Every gesture you'd expect from a desktop trading chart is built in:
|
|
593
|
+
|
|
594
|
+
| Gesture | Result |
|
|
595
|
+
|---|---|
|
|
596
|
+
| Drag chart body left/right | Pan through time |
|
|
597
|
+
| Drag chart body up/down | Pan the price scale (freezes auto-scale; double-click price axis to restore) |
|
|
598
|
+
| Drag price axis up/down | Compress / expand vertical scale (freezes auto-scale) |
|
|
599
|
+
| Drag time axis left/right | Zoom time axis |
|
|
600
|
+
| Double-click price axis | Re-enable auto-scale |
|
|
601
|
+
| Double-click time axis | Fit all data to viewport |
|
|
602
|
+
| Wheel | Zoom around cursor |
|
|
603
|
+
| `Shift` + drag | Measure ruler (bars × time × price Δ × %) |
|
|
604
|
+
| `Alt` + click | Pin OHLC tooltip; live crosshair shows Δ to pinned bar |
|
|
605
|
+
| Hover | Price + time pill labels follow on both axes |
|
|
606
|
+
| `Esc` | Unpin tooltip / cancel drawing |
|
|
607
|
+
| `?` | Show keyboard-shortcut sheet *(widget)* |
|
|
608
|
+
| `Ctrl/⌘ + K` | Command palette *(widget)* |
|
|
609
|
+
| `Ctrl/⌘ + P` | Symbol search *(widget)* |
|
|
610
|
+
| `Ctrl/⌘ + Z` / `Shift + Z` | Undo / redo drawings |
|
|
611
|
+
|
|
612
|
+
### Data import — drag-and-drop or programmatic
|
|
613
|
+
|
|
614
|
+
```typescript
|
|
615
|
+
import { parseOHLCV } from '@tradecanvas/chart'
|
|
616
|
+
|
|
617
|
+
const { data, rowCount, skipped } = parseOHLCV(csvText)
|
|
618
|
+
chart.setData(data)
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
Drop a CSV or JSON file onto the widget and it loads instantly. Auto-detects
|
|
622
|
+
delimiter (`,` / `;` / tab / `|`), header vs. headerless, ISO 8601 timestamps,
|
|
623
|
+
and array-of-arrays vs. array-of-objects JSON.
|
|
624
|
+
|
|
625
|
+
### Backtesting (`@tradecanvas/analytics`)
|
|
626
|
+
|
|
627
|
+
Bar-by-bar strategy backtester with virtual fills, commission/slippage models, and a full risk-metrics report.
|
|
628
|
+
|
|
629
|
+
```typescript
|
|
630
|
+
import { Backtester, PercentCommission, PercentSlippage } from '@tradecanvas/analytics'
|
|
631
|
+
|
|
632
|
+
const bt = new Backtester({
|
|
633
|
+
initialCash: 10_000,
|
|
634
|
+
commission: new PercentCommission(0.0005),
|
|
635
|
+
slippage: new PercentSlippage(0.0003),
|
|
636
|
+
})
|
|
637
|
+
|
|
638
|
+
const result = bt.run(historicalBars, (ctx) => {
|
|
639
|
+
// Strategy fn runs at close of each bar; orders fill on the NEXT bar.
|
|
640
|
+
if (!ctx.position && smaFast > smaSlow) {
|
|
641
|
+
ctx.placeOrder({ side: 'long', type: 'market', quantity: 1 })
|
|
642
|
+
} else if (ctx.position && smaFast < smaSlow) {
|
|
643
|
+
ctx.close()
|
|
644
|
+
}
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
console.log(result.metrics.sharpe) // 1.42
|
|
648
|
+
console.log(result.metrics.maxDrawdownPct) // 0.087
|
|
649
|
+
console.log(result.equityCurve) // → feed into the chart via EquityCurveRenderer
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
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/).
|
|
653
|
+
|
|
654
|
+
#### Strategy library (new in 0.9)
|
|
655
|
+
|
|
656
|
+
Four drop-in reference strategies — each returns a `StrategyFn` ready to feed
|
|
657
|
+
`Backtester.run()`:
|
|
658
|
+
|
|
659
|
+
```typescript
|
|
660
|
+
import {
|
|
661
|
+
Backtester,
|
|
662
|
+
smaCrossStrategy,
|
|
663
|
+
rsiReversionStrategy,
|
|
664
|
+
donchianBreakoutStrategy,
|
|
665
|
+
bollingerReversionStrategy,
|
|
666
|
+
} from '@tradecanvas/analytics'
|
|
667
|
+
|
|
668
|
+
const bt = new Backtester({ initialCash: 10_000 })
|
|
669
|
+
bt.run(bars, smaCrossStrategy({ fastPeriod: 10, slowPeriod: 30 }))
|
|
670
|
+
bt.run(bars, donchianBreakoutStrategy({ entryPeriod: 20, exitPeriod: 10 }))
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
#### Monte Carlo path-dependence (new in 0.9)
|
|
674
|
+
|
|
675
|
+
Shuffle realised trade order N times to expose whether a strategy depends on
|
|
676
|
+
lucky sequencing. Tight P5/P95 band = robust edge; wide band = path-dependent.
|
|
677
|
+
|
|
678
|
+
```typescript
|
|
679
|
+
import { runMonteCarlo } from '@tradecanvas/analytics'
|
|
680
|
+
|
|
681
|
+
const result = bt.run(bars, smaCrossStrategy())
|
|
682
|
+
const mc = runMonteCarlo(10_000, result.trades, { simulations: 1000, seed: 42 })
|
|
683
|
+
|
|
684
|
+
mc.equityBands // [{ step, p5, p25, p50, p75, p95 }, …]
|
|
685
|
+
mc.finalEquityPercentiles // { p5, p25, p50, p75, p95 }
|
|
686
|
+
mc.probabilityProfitable // 0..1
|
|
687
|
+
mc.worstMaxDrawdownPct
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
## Comparison
|
|
691
|
+
|
|
692
|
+
| Feature | @tradecanvas/chart | lightweight-charts | chart.js | Highcharts Stock |
|
|
693
|
+
|---|---|---|---|---|
|
|
694
|
+
| Chart types | 18 + 6 finance | 4 | 8 (non-financial) | 10+ |
|
|
695
|
+
| Finance charts | Sparkline, Depth, Equity, Heatmap, Waterfall, Gauge | None | None | Some |
|
|
696
|
+
| Built-in indicators | 33 | 0 | 0 | ~30 |
|
|
697
|
+
| Drawing tools | 24 | 0 | 0 | Some |
|
|
698
|
+
| Trading overlay | Full (pos + orders + drag) | None | None | None |
|
|
699
|
+
| Real-time streaming | Built-in (Binance) | Manual | Manual | Built-in |
|
|
700
|
+
| Save/load state | Yes | No | No | Yes |
|
|
701
|
+
| Replay mode | Yes (`ReplayController`) | No | No | No |
|
|
702
|
+
| Backtester | Yes (`@tradecanvas/analytics`) | No | No | No |
|
|
703
|
+
| Multi-chart grid | Yes (`ChartGrid`) | No | No | Yes |
|
|
704
|
+
| Bundle (gzip) | ~56 KB core | ~45 KB | ~70 KB | ~200 KB |
|
|
705
|
+
| Dependencies | 0 | 1 | 0 | 0 |
|
|
706
|
+
| Widget (complete UI) | Yes (`ChartWidget`) | No | No | No |
|
|
707
|
+
| License | MIT | Apache 2.0 | MIT | Commercial |
|
|
708
|
+
|
|
709
|
+
## API Overview
|
|
710
|
+
|
|
711
|
+
### `new Chart(container, options)`
|
|
712
|
+
|
|
713
|
+
```typescript
|
|
714
|
+
const chart = new Chart(element, {
|
|
715
|
+
chartType: 'candlestick',
|
|
716
|
+
theme: DARK_THEME,
|
|
717
|
+
autoScale: true,
|
|
718
|
+
rightMargin: 5,
|
|
719
|
+
numberLocale: 'en-US', // or 'de-DE', 'vi-VN', etc. — BCP 47 locale
|
|
720
|
+
crosshair: { mode: 'magnet' },
|
|
721
|
+
features: { drawings: true, indicators: true, trading: true, volume: true },
|
|
722
|
+
})
|
|
723
|
+
|
|
724
|
+
// Change locale at runtime
|
|
725
|
+
chart.setNumberLocale('de-DE') // 65.234,00
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
### Key Methods
|
|
729
|
+
|
|
730
|
+
| Method | Description |
|
|
731
|
+
|---|---|
|
|
732
|
+
| `setData(bars)` | Load historical OHLCV data |
|
|
733
|
+
| `appendBar(bar)` | Append a new candle |
|
|
734
|
+
| `appendBars(bars)` | Bulk append (reconnect catch-up) |
|
|
735
|
+
| `updateLastBar(bar)` | Update the in-progress candle |
|
|
736
|
+
| `setCurrentPrice(price, pulseColor?)` | Show a live price line |
|
|
737
|
+
| `connect(config)` | Connect to a real-time data source |
|
|
738
|
+
| `setTimeframe(tf)` | Switch timeframe on active stream |
|
|
739
|
+
| `setChartType(type)` | Switch chart type |
|
|
740
|
+
| `setTheme(theme)` | Apply a theme (DARK_THEME, LIGHT_THEME, DARK_TERMINAL) |
|
|
741
|
+
| `setNumberLocale(locale)` | Set number format locale (en-US, de-DE, vi-VN) |
|
|
742
|
+
| `setStatusText(text)` | Show status in legend area ("LIVE · 8ms") |
|
|
743
|
+
| `addIndicator(id, params?)` | Add a technical indicator |
|
|
744
|
+
| `removeIndicator(instanceId)` | Remove an indicator |
|
|
745
|
+
| `setDrawingTool(tool)` | Activate a drawing tool |
|
|
746
|
+
| `setPositions(positions)` | Render trading positions |
|
|
747
|
+
| `setOrders(orders)` | Render pending orders |
|
|
748
|
+
| `setVolumeProfileVisible(v)` | Toggle the horizontal volume-profile overlay |
|
|
749
|
+
| `setVolumeProfileConfig({ buckets, widthRatio, opacity, highlightPoC })` | Tune the volume profile |
|
|
750
|
+
| `setAutoScale(v)` / `setLogScale(v)` | Lock or change price-scale mode |
|
|
751
|
+
| `setInvertScale(v)` | Turn the price scale upside down |
|
|
752
|
+
| `fitContent()` / `scrollToEnd()` | Fit all data / jump to live edge |
|
|
753
|
+
| `setVisibleRangePreset(p)` | Show `1D`, `5D`, `1M`, `3M`, `6M`, `YTD`, `1Y`, `5Y` or `All` |
|
|
754
|
+
| `goToTime(time)` | Centre the bar at a time |
|
|
755
|
+
| `setCrosshairTime(time)` | Mirror another chart's crosshair (vertical line only) |
|
|
756
|
+
| `copyDrawings()` / `pasteDrawings()` | Copy the selection, paste into this or another chart |
|
|
757
|
+
| `setStayInDrawingMode(v)` | Keep the drawing tool after each drawing |
|
|
758
|
+
| `saveState(key?)` | Serialize chart state |
|
|
759
|
+
| `loadState(json)` | Restore chart state |
|
|
760
|
+
| `screenshot()` | Download chart as image |
|
|
761
|
+
| `on(event, handler)` | Subscribe to events |
|
|
762
|
+
| `destroy()` | Clean up all resources |
|
|
763
|
+
|
|
764
|
+
### Data Format
|
|
765
|
+
|
|
766
|
+
```typescript
|
|
767
|
+
interface OHLCBar {
|
|
768
|
+
time: number // Unix time in ms or seconds (up to 1e12 is read as seconds); ascending
|
|
769
|
+
open: number
|
|
770
|
+
high: number
|
|
771
|
+
low: number
|
|
772
|
+
close: number
|
|
773
|
+
volume: number
|
|
774
|
+
}
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
## Examples
|
|
778
|
+
|
|
779
|
+
| Example | Description |
|
|
780
|
+
|---|---|
|
|
781
|
+
| [Live demo](https://bonguynvan.github.io/tradecanvas/) | Feature Lab: drawing tools, indicators, trading, replay, sub-cent prices + Vietnamese UI, 200k bars, slow-network switching — each on a live chart |
|
|
782
|
+
| [StackBlitz sandboxes](https://bonguynvan.github.io/tradecanvas/examples/) | One-click, forkable: vanilla `Chart`, `ChartWidget`, React / Vue / Svelte wrappers, finance charts |
|
|
783
|
+
| [`@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 — reactive props, typed, zero boilerplate |
|
|
784
|
+
|
|
785
|
+
## AI coding tools
|
|
786
|
+
|
|
787
|
+
- [`llms.txt`](https://bonguynvan.github.io/tradecanvas/llms.txt) and
|
|
788
|
+
[`llms-full.txt`](https://bonguynvan.github.io/tradecanvas/llms-full.txt) give
|
|
789
|
+
assistants the docs in one place.
|
|
790
|
+
- An agent skill, [`skills/tradecanvas`](https://github.com/bonguynvan/tradecanvas/blob/main/skills/tradecanvas/SKILL.md), teaches
|
|
791
|
+
coding agents to build with TradeCanvas: the entry points, the rules that
|
|
792
|
+
avoid most bugs, and worked examples that CI type-checks against the library.
|
|
793
|
+
Copy the folder into your project's `.claude/skills/` (or your agent's skills
|
|
794
|
+
folder) to use it.
|
|
795
|
+
|
|
796
|
+
## Browser Support
|
|
797
|
+
|
|
798
|
+
Chrome 80+, Firefox 80+, Safari 14+, Edge 80+
|
|
799
|
+
|
|
800
|
+
## Framework Integration
|
|
801
|
+
|
|
802
|
+
TradeCanvas is framework-agnostic. The `Chart` class takes a DOM element and manages its own canvas layers.
|
|
803
|
+
|
|
804
|
+
**React:**
|
|
805
|
+
|
|
806
|
+
```tsx
|
|
807
|
+
import { useEffect, useRef } from 'react'
|
|
808
|
+
import { Chart, BinanceAdapter } from '@tradecanvas/chart'
|
|
809
|
+
|
|
810
|
+
function TradingChart() {
|
|
811
|
+
const ref = useRef<HTMLDivElement>(null)
|
|
812
|
+
|
|
813
|
+
useEffect(() => {
|
|
814
|
+
const chart = new Chart(ref.current!, {
|
|
815
|
+
theme: 'dark',
|
|
816
|
+
features: { indicators: true, drawings: true },
|
|
817
|
+
})
|
|
818
|
+
chart.connect({
|
|
819
|
+
adapter: new BinanceAdapter(),
|
|
820
|
+
symbol: 'BTCUSDT',
|
|
821
|
+
timeframe: '5m',
|
|
822
|
+
})
|
|
823
|
+
return () => chart.destroy()
|
|
824
|
+
}, [])
|
|
825
|
+
|
|
826
|
+
return <div ref={ref} style={{ width: '100%', height: 500 }} />
|
|
827
|
+
}
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
**Svelte:**
|
|
831
|
+
|
|
832
|
+
```svelte
|
|
833
|
+
<script lang="ts">
|
|
834
|
+
import { onMount, onDestroy } from 'svelte'
|
|
835
|
+
import { Chart, BinanceAdapter, DARK_THEME } from '@tradecanvas/chart'
|
|
836
|
+
import type { TimeFrame } from '@tradecanvas/chart'
|
|
837
|
+
|
|
838
|
+
interface Props { symbol?: string; timeframe?: TimeFrame }
|
|
839
|
+
let { symbol = 'BTCUSDT', timeframe = '5m' }: Props = $props()
|
|
840
|
+
|
|
841
|
+
let container: HTMLDivElement
|
|
842
|
+
let chart: Chart | null = null
|
|
843
|
+
|
|
844
|
+
onMount(() => {
|
|
845
|
+
chart = new Chart(container, {
|
|
846
|
+
chartType: 'candlestick',
|
|
847
|
+
theme: DARK_THEME,
|
|
848
|
+
autoScale: true,
|
|
849
|
+
features: { indicators: true, drawings: true, volume: true },
|
|
850
|
+
})
|
|
851
|
+
chart.connect({ adapter: new BinanceAdapter(), symbol, timeframe })
|
|
852
|
+
})
|
|
853
|
+
|
|
854
|
+
onDestroy(() => chart?.destroy())
|
|
855
|
+
|
|
856
|
+
$effect(() => {
|
|
857
|
+
if (!chart) return
|
|
858
|
+
chart.disconnectStream()
|
|
859
|
+
chart.connect({ adapter: new BinanceAdapter(), symbol, timeframe })
|
|
860
|
+
})
|
|
861
|
+
</script>
|
|
862
|
+
|
|
863
|
+
<div bind:this={container} style="width: 100%; height: 600px" />
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
**Vue:**
|
|
867
|
+
|
|
868
|
+
```vue
|
|
869
|
+
<template>
|
|
870
|
+
<div ref="chartContainer" style="width: 100%; height: 600px" />
|
|
871
|
+
</template>
|
|
872
|
+
|
|
873
|
+
<script setup lang="ts">
|
|
874
|
+
import { ref, onMounted, onUnmounted } from 'vue'
|
|
875
|
+
import { Chart, BinanceAdapter, DARK_THEME } from '@tradecanvas/chart'
|
|
876
|
+
|
|
877
|
+
const chartContainer = ref<HTMLDivElement>()
|
|
878
|
+
let chart: Chart | null = null
|
|
879
|
+
|
|
880
|
+
onMounted(() => {
|
|
881
|
+
if (!chartContainer.value) return
|
|
882
|
+
chart = new Chart(chartContainer.value, {
|
|
883
|
+
chartType: 'candlestick',
|
|
884
|
+
theme: DARK_THEME,
|
|
885
|
+
autoScale: true,
|
|
886
|
+
features: { indicators: true, drawings: true, volume: true },
|
|
887
|
+
})
|
|
888
|
+
chart.connect({ adapter: new BinanceAdapter(), symbol: 'BTCUSDT', timeframe: '5m' })
|
|
889
|
+
})
|
|
890
|
+
|
|
891
|
+
onUnmounted(() => chart?.destroy())
|
|
892
|
+
</script>
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
## Architecture
|
|
896
|
+
|
|
897
|
+
Two stacked canvases — a hover repaints only the thin top one:
|
|
898
|
+
|
|
899
|
+
```
|
|
900
|
+
Top canvas (crosshair + axis pills, legend, countdown, measure) z=1
|
|
901
|
+
Scene canvas (grid, candles, indicators, drawings, orders, axes) z=0
|
|
902
|
+
```
|
|
903
|
+
|
|
904
|
+
## Related projects
|
|
905
|
+
|
|
906
|
+
- **[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/)**
|
|
907
|
+
|
|
908
|
+
## License
|
|
909
|
+
|
|
910
|
+
[MIT](./LICENSE)
|