@christtrade/depth 0.19.0 → 0.20.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/README.md CHANGED
@@ -1,25 +1,17 @@
1
1
  <h1>ChristTrade Depth</h1>
2
2
 
3
- A complete trading chart for React. Canvas-rendered, plugin-scriptable, and
4
- agnostic about where its data comes from.
3
+ A trading chart for React: canvas rendering, a plugin and scripting runtime,
4
+ a strategy backtester, and a local L3 matching engine. It's the chart behind
5
+ [ChristTrade](https://christtrade.com).
5
6
 
6
- Depth is the chart that powers [ChristTrade](https://christtrade.com). It is not
7
- a plotting primitive you assemble a UI around - it ships the whole surface:
8
- toolbar, symbol switcher, drawing tools, indicators, order ticket, playback,
9
- settings. You give it a data adapter and a container with a height, and you have
10
- a trading terminal.
7
+ Depth ships the whole terminal, not drawing primitives: toolbar, symbol
8
+ switcher, drawing tools, indicators, order ticket, playback and settings. You
9
+ pass it an `IDataAdapter` and a container with a height.
11
10
 
12
- ## What it is, and what it isn't
13
-
14
- `depth.mjs` is about 1.2 MB minified. That's the honest number, and it's the
15
- right one to compare against a TradingView widget rather than against a
16
- lightweight plotting library. If you want 45 KB that draws a line, Depth is the
17
- wrong tool and [Lightweight Charts](https://github.com/tradingview/lightweight-charts)
18
- is a good one. If you want the thing your users already know how to use, keep
19
- reading.
20
-
21
- React 18 or 19 is the only runtime dependency you install. Everything else -
22
- Radix, Lucide, Luxon, the workers, the fonts - is compiled in.
11
+ `depth.mjs` is ~1.2 MB minified. React 18 or 19 is the only peer dependency;
12
+ Radix, Lucide, Luxon, the workers and the fonts are bundled. If you need a 45 KB
13
+ line plotter, use [Lightweight Charts](https://github.com/tradingview/lightweight-charts)
14
+ instead.
23
15
 
24
16
  ## Install
25
17
 
@@ -30,8 +22,8 @@ npm i react react-dom # if you don't already have them
30
22
 
31
23
  ## Quick start
32
24
 
33
- `SimulatedMarketAdapter` generates synthetic market data, so this runs with no
34
- data source wired up:
25
+ `SimulatedMarketAdapter` generates synthetic data, so this runs without a data
26
+ source:
35
27
 
36
28
  ```jsx
37
29
  import { useDepthChart, SimulatedMarketAdapter } from '@christtrade/depth';
@@ -48,110 +40,197 @@ export default function MyChart() {
48
40
  start: Date.now() - 1000 * 60 * 60 * 24,
49
41
  end: Date.now(),
50
42
  },
51
- // additional options...
52
43
  });
53
44
 
54
45
  return <div style={{ width: '100%', height: '100%' }}>{element}</div>;
55
46
  }
56
47
  ```
57
48
 
58
- `element` is the chart. `chart` is the controller handle - timeframes, symbols,
59
- drawings, plugins, serialization. Types ship with the package, so your editor
60
- has the full API.
61
-
62
- ### Four things that trip people up
63
-
64
- - **Give the chart a real height.** It fills its container. A container with
65
- `height: auto` collapses it to nothing.
66
- - **Dark mode is a `dark` class** on any ancestor, or on `<html>`. Without it
67
- you get the light theme.
68
- - **`depth.css` includes a CSS reset** (Tailwind preflight), which is what you
69
- want in a fresh app. If it fights your existing styles, import
70
- `@christtrade/depth/depth.nopreflight.css` instead.
71
- - **Workers are embedded as blob URLs.** Under a strict CSP you need
72
- `worker-src blob:`.
73
-
74
- If you started from `npm create vite`, add `class="dark"` to `<html>` and drop
75
- `import './index.css'` from `main.jsx` - the template stylesheet centres and pads
76
- `#root`, which fights the chart's layout.
77
-
78
- ## What's in the box
79
-
80
- - **12 chart types** - candles, hollow candles, Heikin-Ashi, bars, line, area,
81
- step, baseline, Renko, Kagi, line break, footprint. Plugins can register more.
82
- - **~50 drawing tools** - trendlines, channels, Fibonacci, Gann, patterns,
83
- position tools, measurements, annotations.
84
- - **21 built-in indicators**, each written against the same plugin API third
85
- parties use. There is no privileged internal path.
86
- - **Multi-pane and multi-chart layouts**, with synced crosshairs.
87
- - **Playback** - scrub, step, and replay historical sessions bar by bar.
88
- - **Trading** - order ticket, quick-trade buttons, position and P&L rendering,
49
+ `element` is the chart. `chart` is the controller: timeframes, symbols,
50
+ drawings, plugins, `serialize()` / `restore()`. Types ship with the package.
51
+
52
+ ### Setup gotchas
53
+
54
+ - The chart fills its container. `height: auto` collapses it to 0.
55
+ - Dark mode is a `dark` class on any ancestor or on `<html>`. Without it you
56
+ get the light theme.
57
+ - `depth.css` includes Tailwind preflight (a CSS reset). If it fights your
58
+ styles, import `@christtrade/depth/depth.nopreflight.css` instead.
59
+ - Workers are embedded as blob URLs, so a strict CSP needs `worker-src blob:`.
60
+ - From `npm create vite`: add `class="dark"` to `<html>` and drop
61
+ `import './index.css'` from `main.jsx`. The template centres and pads
62
+ `#root`.
63
+
64
+ ## Features
65
+
66
+ - Chart types: candles, hollow candles, Heikin-Ashi, bars, line, area, step,
67
+ baseline, and footprint on L3 data. Plugins can register more.
68
+ - Drawing tools: trend line, ray, extended line, horizontal and vertical
69
+ lines and rays, cross line, info line, trend angle, parallel channel, Fib
70
+ retracement, rectangle, triangle, text, long/short position and fixed
71
+ volume profile.
72
+ - 21 built-in indicators, written against the same plugin API third parties
73
+ get.
74
+ - Multi-pane and multi-chart layouts with synced crosshairs.
75
+ - Playback: scrub, step and replay sessions bar by bar.
76
+ - Trading: order ticket, quick-trade buttons, positions and P&L on the chart,
89
77
  and a local L3 matching engine with real fee schedules for simulated fills.
90
- - **Sessions** - trading hours, holidays, subsessions, correct gaps.
91
- - **Serialization** - `chart.serialize()` / `chart.restore()` round-trips
92
- layouts, drawings, plugins, and preferences.
78
+ - Sessions: trading hours, holidays and subsessions. Closed time is cut out
79
+ of the x axis.
80
+ - Themes: built-in dark and light themes, or register your own.
93
81
 
94
- ## Data
82
+ ## Architecture
95
83
 
96
- A data source is an `IDataAdapter`. Implement it and pass it in:
84
+ ### Data
97
85
 
98
- ```ts
99
- import type { IDataAdapter } from '@christtrade/depth';
100
- ```
86
+ An `IDataAdapter` implements `resolveSymbol`, `searchSymbols` and
87
+ `fetchBars`, plus optional `subscribeRealtime` and `getCapabilities`. Each
88
+ adapter declares the data level it serves:
89
+
90
+ | Level | Data | Also serves |
91
+ | --- | --- | --- |
92
+ | `l3` | MBO events | `tick`, `ohlcv`, `l2` |
93
+ | `tick` | trades | `ohlcv` |
94
+ | `ohlcv` | bars | |
95
+ | `l2` | book snapshots | |
96
+
97
+ A plugin or feature declares the level it needs, and on a symbol that can't
98
+ serve it, it's refused with a reason instead of breaking. The chunk processors
99
+ (`processOhlcvChunk`, `processTickChunk`, `processL2Chunk`, `processL3Chunk`)
100
+ are exported, so an adapter doesn't reimplement aggregation. Plugins can add
101
+ data sources at runtime with `registerDataSource`, which is how
102
+ bring-your-own-API-key providers work.
103
+
104
+ The `DataEngine` keeps one store per symbol and pages history in as you pan.
105
+ Every chart on that symbol reads the same store.
106
+
107
+ ### Storage
108
+
109
+ OHLCV lives in a `BarStore`: six `Float64Array` columns (`time`, `open`,
110
+ `high`, `low`, `close`, `volume`, plus bid/ask once a bar has them), sorted by
111
+ `time`.
112
+
113
+ - 48 bytes a bar. A `{ time, open, high, low, close, volume }` object is ~176,
114
+ and each one is something for the GC to walk.
115
+ - Capacity doubles on growth, and the spare room is split between both ends,
116
+ so append and prepend are amortized O(bars added). Paging history in never
117
+ copies what's loaded.
118
+ - `lowerBound` / `upperBound` binary search on `time`.
119
+ - When a chart's timeframe is the resolution the data came in at, the chart
120
+ draws from the engine's store directly. Other timeframes get one
121
+ aggregated copy.
122
+
123
+ The column views are replaced when the store grows. Read `bars.close` off the
124
+ store each time, and key caches on `bars.version`, which bumps on every
125
+ mutation.
126
+
127
+ L3 events are packed into 20 bytes each in a `CompactBuffer` (BigInt64
128
+ timestamp, int32 price, uint32 size, uint24 order id, packed action/side), so
129
+ 30M events take ~600 MB instead of ~5 GB as objects.
130
+
131
+ ### Rendering
132
+
133
+ Four stacked canvases: `background`, `base` (candles, grid, indicators),
134
+ `drawings` and `ui` (crosshair, axis labels, price pills). One rAF loop paints
135
+ only the layers marked dirty, so an idle chart costs close to nothing.
136
+
137
+ Time is BigInt nanoseconds. `SessionMapper` maps real time to "market time",
138
+ with closed sessions taken out, and the transformer maps that to pixels.
139
+
140
+ Per frame, drawing is O(visible bars), and lookups by time (status line,
141
+ magnet snap, y-axis autofit) are O(log n) in loaded bars.
142
+
143
+ ### Workers
101
144
 
102
- Depth supports four data levels - OHLCV bars, ticks, L2 book snapshots, and MBO
103
- (L3) events - and each adapter declares which it serves. Higher levels unlock
104
- features that need them (footprint, depth-of-market, per-order replay); lower
105
- levels degrade cleanly rather than breaking. Chunk processors for each level are
106
- exported so adapter authors don't have to reimplement aggregation.
145
+ - `script.worker`: user scripts and strategies, off the main thread.
146
+ - `slice.worker`: decodes L3 ranges and merges history pages.
147
+ - `heatmap.worker`: builds book snapshots and renders the depth heatmap to an
148
+ `OffscreenCanvas`.
149
+ - An indicator worker for heavy indicator math.
107
150
 
108
- Plugins can also register data sources at runtime via `registerDataSource`,
109
- which is how bring-your-own-API-key providers work.
151
+ ## Performance
152
+
153
+ Ryzen 5 7500F, Chrome, 1m bars.
154
+
155
+ - 1M bars loaded, ~300 on screen: panning holds 240fps. Draw, status line,
156
+ autofit and snap cost the same as with 20k loaded (see the perf guard
157
+ below).
158
+ - Heap with 1M bars loaded, in ChristTrade: ~480 MB.
159
+ - Backtest over 2M bars (~4 years of BTC 1m) through the script worker:
160
+ 207-265ms. Stats are running (Welford) and the equity curve is capped, so
161
+ it holds ~1.1 MB at 2M bars.
162
+ - `processL3Chunk`: ~1.5M MBO events/sec on synthetic data.
163
+
164
+ ### Perf guard
165
+
166
+ `npm run perf:guard` runs in CI. It bundles the draw, status line, autofit,
167
+ snap and prepend paths with esbuild, times each at 20k and at 1M loaded bars
168
+ with ~300 on screen, drawing to a no-op canvas, and fails if any is 5x slower
169
+ at 1M, or if a `BarStore` paged in to 1M bars takes more than 72 bytes a bar
170
+ including spare capacity:
171
+
172
+ ```
173
+ ok draw candles 118.0µs -> 85.4µs 0.7x
174
+ ok status line 0.8µs -> 0.9µs 1.1x
175
+ ok autofit 2.0µs -> 1.9µs 1.0x
176
+ ok magnet snap 1.0µs -> 0.9µs 0.9x
177
+ ok page prepend 0.96ms -> 1.04ms 1.1x
178
+ ok bytes/bar 66.7 at 1M, spare room included (max 72)
179
+ ```
180
+
181
+ Left column is 20k bars, right is 1M.
110
182
 
111
183
  ## Plugins
112
184
 
113
- Plugins are declarative JavaScript, written in the built-in script editor on
114
- [christtrade.com](https://christtrade.com) or shipped as modules. They can add
115
- indicators, chart types, drawing tools, panels, toolbar items, data sources,
116
- and render overlays. Heavy computation can be offloaded to WebAssembly.
185
+ Plugins are JavaScript modules, or scripts written in the built-in editor on
186
+ [christtrade.com](https://christtrade.com). They can add indicators, chart
187
+ types, drawing tools, panels, toolbar items, data sources and render overlays,
188
+ and can offload heavy math to WebAssembly.
117
189
 
118
- A plugin declares the capabilities and network origins it needs, and `ctx` hands
119
- over nothing it did not ask for - so you can show a user what a plugin intends
120
- before they start it. That is a declaration mechanism, not a sandbox: plugin code
121
- runs in your page. Read [SECURITY.md](SECURITY.md) before you let other people's
122
- plugins run in your app.
190
+ A plugin declares the capabilities and network origins it needs, and `ctx`
191
+ only exposes those, so you can show a user what a plugin wants before it
192
+ runs. It isn't a sandbox: plugin code runs in your page. Read
193
+ [SECURITY.md](SECURITY.md) before running other people's plugins.
123
194
 
124
- Full plugin documentation lives at
125
- [docs.christtrade.com/depth](https://docs.christtrade.com/depth).
195
+ Strategies run in the script worker. `StrategyEngine` does intrabar fills,
196
+ parameter sweeps and walk-forward analysis.
197
+
198
+ Full docs: [docs.christtrade.com/depth](https://docs.christtrade.com/depth).
199
+
200
+ ## Development
201
+
202
+ ```bash
203
+ npm test # node:test via tsx
204
+ npm run perf:guard
205
+ npx tsc -p tsconfig.json --noEmit
206
+ npm run dist # build dist/, smoke test it
207
+ ```
208
+
209
+ CI runs tests, typecheck and the perf guard on Node 20 and 22.
126
210
 
127
211
  ## Status
128
212
 
129
- Depth is pre-1.0 and versioned accordingly. It runs in production at
130
- ChristTrade, so it is not experimental - but the public API is still moving, and
131
- minor versions may break it. **1.0 is the API freeze**, not the point where the
132
- library becomes usable.
213
+ Pre-1.0. It runs in production at ChristTrade, but the public API still
214
+ moves and minor versions can break it. 1.0 is the API freeze. Breaking changes
215
+ are marked in [CHANGELOG.md](CHANGELOG.md).
133
216
 
134
217
  ## Contributing
135
218
 
136
- **Issues are welcome.** Bug reports, reproductions, and questions about the API
137
- all help, and I read them.
219
+ Issues are welcome: bug reports, reproductions, API questions.
138
220
 
139
- **Pull requests are by invitation.** Depth is a single-author library and I
140
- intend to keep it that way. If you've found something broken, open an issue -
141
- if a PR is the right way to resolve it, I'll ask. Unsolicited PRs will usually
142
- be closed with thanks and reopened as issues, which isn't a comment on the code.
221
+ Pull requests are by invitation. Depth is a single-author library. If you
222
+ found something broken, open an issue, and if a PR is the right fix I'll ask
223
+ for one. Unsolicited PRs usually get closed and reopened as issues, which
224
+ isn't a comment on the code.
143
225
 
144
226
  ## Attribution
145
227
 
146
- Depth draws a small ChristTrade mark in the corner of the chart.
147
-
148
- The Apache License doesn't require you to keep it, and I'm not going to pretend
149
- otherwise - you can remove it and you'll be within your rights. I'm asking you
150
- not to. The mark is the only thing this library asks in return for being free,
151
- and it's how people find it.
228
+ Depth draws a small ChristTrade mark in the corner of the chart. Apache-2.0
229
+ doesn't require you to keep it, and you can remove it. I'm asking you not to:
230
+ it's the only thing the library asks for, and it's how people find it.
152
231
 
153
- The name is a separate matter. Apache-2.0 grants no trademark rights (section
154
- 6), so you can fork Depth but you can't ship it as "ChristTrade Depth." See
232
+ The name is separate. Apache-2.0 grants no trademark rights (section 6), so
233
+ you can fork Depth but can't ship it as "ChristTrade Depth". See
155
234
  [NOTICE](NOTICE).
156
235
 
157
236
  ## License