openalgo-charts 1.9.2 → 2.0.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
@@ -4,17 +4,17 @@
4
4
 
5
5
  **A from-scratch, dependency-free HTML5-canvas charting engine for OpenAlgo.**
6
6
 
7
- Professional interactive charts, 102 built-in indicators plus your own custom ones, drawing tools, order flow, market replay, linked chart grids, and on-chart trading. Six lazy-loaded tiers, zero runtime dependencies, ~59 KB Brotli for the base engine.
7
+ Professional interactive charts, 102 built-in indicators plus your own custom ones, 51 drawing tools, order flow, market replay, linked chart grids, on-chart trading, vector SVG export and an optional WebGL2 backend. Eight lazy-loaded tiers, zero runtime dependencies, 66.42 KB Brotli for the base engine, and a one-call widget tier that adds the toolbar, drawing rail, dialogs and shortcuts.
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/openalgo-charts.svg?color=cb3837&label=npm)](https://www.npmjs.com/package/openalgo-charts)
10
10
  [![license](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](./LICENSE)
11
- [![bundle](https://img.shields.io/badge/brotli-61%20KB%20base%20%C2%B7%20124%20KB%20all%20tiers-brightgreen.svg)](#size-budget)
12
- [![tests](https://img.shields.io/badge/tests-2898%20passing-brightgreen.svg)](#develop)
11
+ [![bundle](https://img.shields.io/badge/brotli-66%20KB%20base%20%C2%B7%20182%20KB%20all%20tiers-brightgreen.svg)](#size-budget)
12
+ [![tests](https://img.shields.io/badge/tests-3999%20passing-brightgreen.svg)](#develop)
13
13
  [![dependencies](https://img.shields.io/badge/runtime%20deps-0-brightgreen.svg)](#principles)
14
14
 
15
- [**Documentation**](https://marketcalls.github.io/openalgo-charts/)  ·  [**Live examples**](https://marketcalls.github.io/openalgo-charts/examples)  ·  [**Getting started**](./docs/getting-started.md)  ·  [**Architecture**](./ARCHITECTURE.md)
15
+ [**Documentation**](https://marketcalls.github.io/openalgo-charts/)  ·  [**Live examples**](https://marketcalls.github.io/openalgo-charts/examples)  ·  [**Getting started**](./docs/getting-started.md)  ·  [**Migrating to 2.0**](./docs/migrating-to-2.md)  ·  [**Architecture**](./ARCHITECTURE.md)
16
16
 
17
- <img src="docs/architecture-diagram.svg" alt="OpenAlgo Charts architecture: seven layers from the public API down to feeds and data, with 102 built-in plus custom indicators, 51 drawing tools, and a six-tier bundle legend" width="920" />
17
+ <img src="docs/architecture-diagram.svg" alt="OpenAlgo Charts architecture: seven layers from the public API down to feeds and data, with 102 built-in plus custom indicators, 51 drawing tools, and an eight-tier bundle legend" width="920" />
18
18
 
19
19
  </div>
20
20
 
@@ -62,7 +62,7 @@ in front of npm rather than being places you upload to. A chart is one HTML file
62
62
  ```html
63
63
  <div id="chart" style="width:100vw;height:100vh"></div>
64
64
  <script type="module">
65
- import { createChart } from 'https://unpkg.com/openalgo-charts@1.9.2/dist/openalgo-charts.mjs';
65
+ import { createChart } from 'https://unpkg.com/openalgo-charts@2.0.1/dist/openalgo-charts.mjs';
66
66
  const chart = createChart(document.getElementById('chart'), { timezone: 'Asia/Kolkata' });
67
67
  chart.addSeries('candlestick').setData(bars);
68
68
  </script>
@@ -70,23 +70,47 @@ in front of npm rather than being places you upload to. A chart is one HTML file
70
70
 
71
71
  Each tier is its own file, so `openalgo-charts.indicators.mjs` next to it registers
72
72
  all 102 built-ins. Pin the version in anything you leave running. There is no
73
- stylesheet to load: the engine ships no DOM. See
73
+ stylesheet to load: the engine ships no DOM, and the one tier that does
74
+ (`openalgo-charts/widget`) injects its own. See
74
75
  [Use from a CDN](https://marketcalls.github.io/openalgo-charts/docs/cdn).
75
76
 
77
+ ## The whole terminal in one call
78
+
79
+ ```ts
80
+ import { createWidget } from 'openalgo-charts/widget';
81
+ import 'openalgo-charts/indicators';
82
+ import { OpenAlgoDataFeed } from 'openalgo-charts';
83
+
84
+ const widget = createWidget('#terminal', {
85
+ feed: new OpenAlgoDataFeed({ baseUrl: 'http://127.0.0.1:5000', apiKey: 'YOUR_KEY' }),
86
+ symbol: 'RELIANCE', exchange: 'NSE', interval: '5m',
87
+ theme: 'dark',
88
+ persist: true,
89
+ onOrder: (order) => broker.place(order), // the right-click menu offers order entry only when this is set
90
+ });
91
+
92
+ widget.chart; // the Chart underneath, every base API available
93
+ widget.draw; // the DrawingController the rail drives
94
+ ```
95
+
96
+ `openalgo-charts/widget` is the eighth tier and the only one that builds DOM: a top bar (symbol search, interval pills, chart type, Indicators, capture, settings, theme), the drawing rail with pins and flyouts, a status line, toasts, a right-click menu, a keymap with a `?` panel, and optional layout persistence. Every dialog is generated from a schema the engine already ships (`chartSettingsSchema`, the indicator descriptor, `drawingSettingsSchema`), so no control exists without something behind it, and the chrome takes its colours from the active `ChartTheme` through `--oac-` tokens, so `setTheme` recolours canvas and chrome together. It drives the engine only through the public API, and the build proves it: the ESLint tier ACL and `npm run shake` keep it out of every other bundle, and importing the module touches no DOM, so it can sit in code that also runs on a server. The guide is [`docs/widget.md`](./docs/widget.md).
97
+
76
98
  ## Tiers
77
99
 
78
100
  Import only what you use. Each tier is a separate bundle that registers into the base engine's registries, so the cost of a feature you don't load is zero.
79
101
 
80
102
  | Import | Contents | Brotli |
81
103
  |---|---|---|
82
- | `openalgo-charts` | Engine, 13 chart types, panes &amp; scales, primitives, registries, chart state, chart linking, bar cache, interval registry, trading overlay, OpenAlgo feeds | 60.9 KB |
83
- | `openalgo-charts/indicators` | 102 built-in indicators, the `registerIndicator` contract for your own, and the Tier-2 (external-data) contract | 27.3 KB |
84
- | `openalgo-charts/draw` | 43 drawing tools + a headless drawing controller and clipboard | 15.4 KB |
85
- | `openalgo-charts/transform` | Heikin Ashi, Renko, Range bars, Line Break, Point &amp; Figure, Kagi | 2.7 KB |
86
- | `openalgo-charts/profile` | Volume Profile, Market Profile (TPO), Footprint, order flow | 10.7 KB |
87
- | `openalgo-charts/trade` | Order / position / bracket tools + DOM ladder | 7.6 KB |
104
+ | `openalgo-charts` | Engine, 13 chart types, panes &amp; scales, primitives, registries, chart state, chart linking, bar cache, interval registry, trading overlay, SVG export, render backend port, OpenAlgo feeds | 66.42 KB |
105
+ | `openalgo-charts/indicators` | 102 built-in indicators, the `registerIndicator` contract for your own, and the Tier-2 (external-data) contract | 27.27 KB |
106
+ | `openalgo-charts/draw` | 51 drawing tools + a headless drawing controller, clipboard, settings schema, level palette, freehand geometry and SVG icons | 25.82 KB |
107
+ | `openalgo-charts/transform` | Heikin Ashi, Renko, Range bars, Line Break, Point &amp; Figure, Kagi | 2.66 KB |
108
+ | `openalgo-charts/profile` | Volume Profile, Market Profile (TPO), Footprint, order flow | 10.66 KB |
109
+ | `openalgo-charts/trade` | Order / position / bracket tools + DOM ladder | 7.61 KB |
110
+ | `openalgo-charts/webgl` | WebGL2 series backend: batched, analytically anti-aliased GPU rendering of the standard chart types behind `renderer: 'auto'`, with a session-long fallback to the 2D path | 6.38 KB |
111
+ | `openalgo-charts/widget` | The chart with its chrome in one call: `createWidget` adds a top bar, the drawing rail, a status line, the settings and indicator dialogs, drawing properties, a right-click menu, a keymap with a `?` panel and optional layout persistence. The only tier that ships DOM | 35.56 KB |
88
112
 
89
- Everything together is **124.47 KB Brotli**. Figures are the measured `size-limit` output. The trade tier is listed as its delta over the base, so loading base + trade costs 68.49 KB.
113
+ Everything together is **182.37 KB Brotli**; a widget terminal (base + draw + indicators + widget, what one `createWidget` call loads) is 155.06 KB. Figures are the measured `size-limit` output. The trade tier is listed as its delta over the base, so loading base + trade costs 74.03 KB.
90
114
 
91
115
  ## What's built
92
116
 
@@ -124,6 +148,28 @@ Headless by design: no toolbar, no dialogs. Placement with live preview, selecti
124
148
 
125
149
  `draw.copy()`, `draw.cut()` and `draw.paste()` move drawings through the OS clipboard, including between two charts on the page. The payload is JSON under one namespaced key, so foreign text pastes nothing instead of throwing at your Ctrl+V handler, and every field is validated before it reaches the model. A refused clipboard permission does not lose the copy: every write also lands in a shared in-memory clipboard, and a cut deletes only after the write succeeds. A paste is one undo step of fresh objects, nudged two bars and 16 px so it is visibly a second shape. The key bindings stay yours; the engine installs no listeners.
126
150
 
151
+ ### Drawing model and feel
152
+
153
+ ```ts
154
+ const draw = new DrawingController(chart, { magnet: 'weak' }); // 'off' | 'weak' | 'strong'
155
+
156
+ draw.select([a, b], false); // a selection, not a single id
157
+ draw.nudge(draw.selection(), 0, -1); // one undo entry for the whole group
158
+ draw.sendBehindSeries(a); // zIndex below zero paints under the candles
159
+
160
+ draw.add({ tool: 'text', points: [p], paneIndex: 0,
161
+ text: { value: 'Breakout', bold: true, align: 'center' } }); // text is its own block
162
+ draw.add({ tool: 'fib-retracement', points: [lo, hi], paneIndex: 0,
163
+ style: { levels: [{ ratio: 0.5 }, { ratio: 0.618, color: '#f5a623' }, { ratio: 1, enabled: false }] } });
164
+
165
+ const doc = draw.toJSON(); // { version: 2, drawings }; fromJSON also takes a 1.9.x array
166
+ const fields = drawingSettingsSchema('trend-line'); // what a properties panel may show, and nothing else
167
+ ```
168
+
169
+ A drawing carries a paint order (`zIndex`: below zero paints under the series, at or above zero over it, with `bringToFront`, `sendToBack`, `sendBehindSeries` and `bringAboveSeries`), a text block (`drawing.text`, so a label colour is never confused with a stroke colour) and per-level fib rungs (`FibLevel`: ratio, colour, label, enabled), and the controller holds a selection rather than a single id: shift, ctrl or meta click adds, a body drag moves the whole group as one undo step, and `updateMany`, `removeMany`, `duplicate` and `nudge` act on the list. `drawingSettingsSchema(toolId)` describes a properties panel per tool, declaring only fields that tool's renderer reads, and `readDrawingSettings` / `applyDrawingSettings` are its round trip. `toJSON()` returns a versioned document; `fromJSON` and `migrateDrawings` upgrade any 1.9.x payload, so a layout saved by an older host opens with its text and levels intact. See [Migrating to 2.0](./docs/migrating-to-2.md).
170
+
171
+ Under the hand: the drawing under the pointer shows its handles faintly before it is grabbed (`hovered()`, `drawing:hover`), at the cost of the overlay tier only; Shift locks a line to 45 degree steps while placing or dragging a handle; the magnet paints a ring on the bar centre where the next click will land, and `'weak'` pulls only when an O/H/L/C is within a few pixels; grab targets grow for a touch pointer; Escape, Enter and Backspace cancel, finish or pop an anchor while placing, through `keyToDrawingAction`, which also maps undo, redo, copy, cut, paste, duplicate, delete and arrow nudge as a pure function you wire yourself. Line tools take a `showStats` readout of change, percent, bars and angle. The brush and highlighter ink every coalesced pointer sample, thin on release and paint as a spline, with pen pressure driving the width when `style.pressure` is on. Every tool icon and a chrome set ship as path data with builders for an inline `<svg>`, a sprite with `<use>` and a CSS cursor (`iconSvg`, `iconSprite`, `iconUse`, `toolCursor`), so a rail, a flyout and the armed cursor derive from one registry.
172
+
127
173
  ### Panes, scales &amp; legends
128
174
  Draggable pane dividers, move / maximize / remove, and pane legends showing one reading per plot in that plot's own colour, with inline show-hide / settings / move / delete controls revealed on hover. The status line is switchable field by field (logo, title, market status, OHLC, bar change, volume, last day change, last value) over a host-supplied data source.
129
175
 
@@ -218,6 +264,27 @@ An interval code resolves through a registry whose entry is a **bucketing rule,
218
264
  ### State
219
265
  `chart.getState()` / `chart.restoreState()` capture the viewport, grid, panes, price scales, indicator instances, drawings, and the whole settings block (canvas, status line, trading colours, event filters) as one JSON payload: saved layouts and templates with no extra storage plumbing.
220
266
 
267
+ ### Vector export
268
+
269
+ ```ts
270
+ const svg = chart.exportSVG(); // a standalone SVG string at the live size
271
+ const print = chart.exportSVG({ width: 1600, height: 900, background: false }); // transparent, for an embedded figure
272
+ ```
273
+
274
+ The ordinary paint of every pane, run once into a serialising 2D context at pixel ratio 1, so axis labels and tags stay text, lines stay lines, and there is no second renderer to drift from the canvas one. Nothing transient is in it: no crosshair, hover or drag. A different size lays the chart out for the export and puts the live layout back without a blank frame. `SvgContext` is exported for a host that wants to run its own primitive into one. `takeScreenshot()` still returns a canvas when a picture is what you want.
275
+
276
+ ### Render backends
277
+
278
+ ```ts
279
+ import 'openalgo-charts/webgl';
280
+
281
+ const chart = createChart(el, { renderer: 'auto' }); // 'canvas2d' (default) | 'webgl2' | 'auto'
282
+ chart.rendererKind; // what it actually paints with
283
+ chart.on('renderer:fallback', ({ from, to, reason }) => log(reason));
284
+ ```
285
+
286
+ The series pass on each pane goes through a render backend port; the shipped Canvas2D backend is pixel-identical to 1.9.2, and the `openalgo-charts/webgl` tier registers a WebGL2 backend that batches every standard chart type into one shared offscreen surface per page with analytic anti-aliasing and composites it into the pane's own canvas, so screenshots, the SVG export and the DOM are unchanged and a dashboard of panes never opens more than one GL context. `'webgl2'` throws until the tier is imported and falls back to the 2D path with one warning on a device without WebGL2; `'auto'` is the silent form. A lost context moves the chart to `canvas2d` for the session and emits `renderer:fallback`. Text, dashed lines, gradients, drawings and custom types stay on the 2D context, which already does them well.
287
+
221
288
  ### Data
222
289
  OpenAlgo REST history + WebSocket ticks with auto-reconnect and resubscribe, live candle aggregation, tick/volume bars, a unified `chart.on(...)` event bus, markers and signals, earnings/dividend/expiry event markers, an IANA chart timezone, and custom price/time formatters.
223
290
 
@@ -227,13 +294,16 @@ Enforced in CI by [`size-limit`](./.size-limit.json). Nothing is excluded, becau
227
294
 
228
295
  | Bundle | Limit | Actual |
229
296
  |---|---|---|
230
- | Base engine | 62 KB | 60.88 KB |
231
- | Base + trade | 70 KB | 68.49 KB |
297
+ | Base engine | 67 KB | 66.42 KB |
298
+ | Base + trade | 75 KB | 74.03 KB |
232
299
  | Indicators tier | 30 KB | 27.27 KB |
233
- | Draw tier | 16 KB | 15.39 KB |
300
+ | Draw tier | 26 KB | 25.82 KB |
234
301
  | Transform tier | 5 KB | 2.66 KB |
235
302
  | Profile tier | 11 KB | 10.66 KB |
236
- | **Everything** | **126 KB** | **124.47 KB** |
303
+ | WebGL2 tier | 7 KB | 6.38 KB |
304
+ | Widget tier | 36 KB | 35.56 KB |
305
+ | Widget terminal (base + draw + indicators + widget) | 156 KB | 155.06 KB |
306
+ | **Everything** | **183 KB** | **182.37 KB** |
237
307
 
238
308
  ## Documentation
239
309
 
@@ -248,6 +318,8 @@ npm run build # build the library (dist/) the live
248
318
  cd website && npm install && npm run dev # http://localhost:3000/openalgo-charts
249
319
  ```
250
320
 
321
+ For charts that remain open through a market session, see [Performance & Operations](https://marketcalls.github.io/openalgo-charts/docs/performance-and-operations/) for bounded history, canvas memory, feed cleanup, cache limits, and soak checks.
322
+
251
323
  ## Agent skills
252
324
 
253
325
  Teach your AI coding assistant this library:
@@ -256,7 +328,7 @@ Teach your AI coding assistant this library:
256
328
  npx skills add https://github.com/marketcalls/openalgo-charts
257
329
  ```
258
330
 
259
- Installs six skills from [`.github/skills/`](./.github/skills) - a reference hub with 21 deep-dive files covering the whole API surface and its foot-guns, plus task skills for scaffolding a chart, adding indicators, building a terminal, writing a plugin, and debugging. Works with Claude Code, Cursor, Codex, Copilot, Gemini CLI and the rest of the `skills` CLI's supported agents.
331
+ Installs six skills from [`.github/skills/`](./.github/skills) - a reference hub with 22 deep-dive files covering the whole API surface and its foot-guns, plus task skills for scaffolding a chart, adding indicators, building a terminal, writing a plugin, and debugging. Works with Claude Code, Cursor, Codex, Copilot, Gemini CLI and the rest of the `skills` CLI's supported agents.
260
332
 
261
333
  ## Examples
262
334
 
@@ -265,7 +337,8 @@ Runnable demos in [`examples/`](./examples), including a full **yfinance termina
265
337
  ```bash
266
338
  npm run build
267
339
  cd examples/yfinance && pip install -r requirements.txt && python server.py
268
- # http://127.0.0.1:8000/examples/yfinance/index.html
340
+ # serves http://127.0.0.1:8000/examples/yfinance/index.html
341
+ python server.py --fixture # no yfinance, no network: deterministic synthetic bars
269
342
  ```
270
343
 
271
344
  ## Develop
@@ -273,11 +346,11 @@ cd examples/yfinance && pip install -r requirements.txt && python server.py
273
346
  ```bash
274
347
  npm install # install dev toolchain
275
348
  npm run typecheck # strict TypeScript check
276
- npm test # unit tests (vitest) - 2898 across 143 files
349
+ npm test # unit tests (vitest) - 3999 across 170 files
277
350
  npm run build # Rollup -> dist/ (minified ESM per tier + types)
278
351
  npm run size # size-limit (Brotli) against the budget
279
352
  npm run e2e # Playwright Chromium smoke tests
280
- npm run verify # typecheck + test + build + size
353
+ npm run verify # lint + typecheck + test + build + demo tests + dts + size + shake
281
354
  ```
282
355
 
283
356
  ## Principles
@@ -290,7 +363,7 @@ npm run verify # typecheck + test + build + size
290
363
 
291
364
  ## Status &amp; limitations
292
365
 
293
- Version **1.9.2**. All engine build phases are implemented with 2898 unit tests across 143 files.
366
+ Version **2.0.1**. All engine build phases are implemented with 3999 unit tests across 170 files. Upgrading a 1.9.x host: [Migrating to 2.0](./docs/migrating-to-2.md).
294
367
 
295
368
  Known gaps, stated plainly:
296
369
 
@@ -298,7 +371,7 @@ Known gaps, stated plainly:
298
371
  - **Only `Footprint` is theme-aware among the profile primitives.** `VolumeProfile`, `MarketProfile` and `HorizontalProfile` never read `rc.theme`; their defaults are dark-tuned, so a light theme needs explicit colours. `HorizontalProfile` also hardcodes its POC / value-area line colours and has no `setOptions`.
299
372
  - The OpenAlgo **WS/trade adapter wire schemas** ship with injectable transports and offline tests, but the exact field names should be verified against your running OpenAlgo build.
300
373
  - **A pane has exactly one hidden overlay scale**, so every symbol comparison on a pane shares one baseline. That is right for a single comparison, the common case, but a second one on the same pane is quoted against the first instrument's price; put further instruments on their own pane with `paneIndex` until the overlay scales are keyed.
301
- - An optional **DOM chrome package** (toolbar, dialogs, command palette, objects panel) is the next planned piece; today that UI lives in the examples.
374
+ - **The WebGL2 backend draws the standard chart types.** Kagi, point-and-figure and custom chart types, drawings, text and every primitive stay on the 2D context; `renderer: 'auto'` is a speed-up for the series pass, not a second renderer for everything.
302
375
 
303
376
  See [`ARCHITECTURE.md`](./ARCHITECTURE.md) §13a for the full deferred list.
304
377