wickchart 0.3.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # HabView
1
+ # WickChart
2
2
 
3
- **`<hab-chart>` — a modern, simpler, more useful charting web component.**
3
+ **`<wick-chart>` — a modern, simpler, more useful charting web component.**
4
4
 
5
5
  A TradingView-style financial chart as a single framework-agnostic Web Component.
6
6
  One file, zero dependencies, one HTML tag. Canvas-rendered, fast, themeable, and
@@ -14,8 +14,8 @@ npm install wickchart
14
14
 
15
15
  ```js
16
16
  // any bundler / framework — TypeScript types included
17
- import 'wickchart'; // registers <hab-chart>
18
- import HabChart from 'wickchart'; // for HabChart.registerIndicator(...)
17
+ import 'wickchart'; // registers <wick-chart>
18
+ import WickChart from 'wickchart'; // for WickChart.registerIndicator(...)
19
19
  import { encodeStateQuery } from 'wickchart/core'; // pure helpers
20
20
  ```
21
21
 
@@ -24,10 +24,10 @@ Or straight from a CDN — no install, no build:
24
24
  ```html
25
25
  <script type="module" src="https://unpkg.com/wickchart"></script>
26
26
 
27
- <hab-chart label="BTC · 1h" type="candles" indicators="sma:20 volume"></hab-chart>
27
+ <wick-chart label="BTC · 1h" type="candles" indicators="sma:20 volume"></wick-chart>
28
28
 
29
29
  <script type="module">
30
- const chart = document.querySelector('hab-chart');
30
+ const chart = document.querySelector('wick-chart');
31
31
  chart.setData(bars); // [{ time, open, high, low, close, volume }]
32
32
  chart.update(bar); // stream live updates
33
33
  </script>
@@ -37,7 +37,7 @@ Works in plain HTML, React, Vue, Svelte, Angular — anywhere a `<div>` works.
37
37
  TypeScript declarations ship inside the package (generated at pack time from
38
38
  the JSDoc-annotated source — the repo itself stays 100% dependency-free JS).
39
39
 
40
- ## Declarative live charts with `<hab-feed>`
40
+ ## Declarative live charts with `<wick-feed>`
41
41
 
42
42
  One more script tag and your chart is fully live — data, backfill, streaming —
43
43
  with **zero JavaScript written**:
@@ -45,13 +45,13 @@ with **zero JavaScript written**:
45
45
  ```html
46
46
  <script type="module" src="https://unpkg.com/wickchart/feed"></script>
47
47
 
48
- <hab-feed for="chart" binance="BTCUSDT" tf="1h"></hab-feed>
49
- <hab-chart id="chart" indicators="sma:20 volume" profile></hab-chart>
48
+ <wick-feed for="chart" binance="BTCUSDT" tf="1h"></wick-feed>
49
+ <wick-chart id="chart" indicators="sma:20 volume" profile></wick-chart>
50
50
  ```
51
51
 
52
52
  | Attribute | Meaning |
53
53
  | ----------- | ----------------------------------------------------------------------- |
54
- | `for` | target `<hab-chart>` id (auto-pairs with the first chart when omitted) |
54
+ | `for` | target `<wick-chart>` id (auto-pairs with the first chart when omitted) |
55
55
  | `binance` | Binance symbol (`BTCUSDT`) — REST load + WebSocket live + backfill |
56
56
  | `demo` | deterministic offline synthetic feed (`demo="ETH"` picks a base price) |
57
57
  | `url` | generic REST endpoint returning a JSON array of bars (+ `poll="10"` sec) |
@@ -61,7 +61,7 @@ with **zero JavaScript written**:
61
61
 
62
62
  The element reflects its state in the `status` attribute (`loading`, `live`,
63
63
  `polling`, `fallback`, `loaded`, `waiting`, `idle`) and emits
64
- `hab-feed:status` / `hab-feed:fallback` events. When Binance is unreachable
64
+ `wick-feed:status` / `wick-feed:fallback` events. When Binance is unreachable
65
65
  (geo-blocked, offline), it degrades gracefully: WebSocket → REST polling → a
66
66
  synthetic stream bridged from the last real price, so the chart never goes
67
67
  blank. It also wires `chart.onloadmore` for infinite backfill automatically.
@@ -71,7 +71,7 @@ blank. It also wires `chart.onloadmore` for infinite backfill automatically.
71
71
  ## Why another chart library?
72
72
 
73
73
  TradingView's charting library is powerful but heavy and enterprise-licensed;
74
- most wrappers add build steps and framework lock-in. HabView takes the opposite
74
+ most wrappers add build steps and framework lock-in. WickChart takes the opposite
75
75
  bet:
76
76
 
77
77
  - **Zero dependencies, single file** (~40 KB unminified, no build step required)
@@ -82,7 +82,13 @@ bet:
82
82
  outside the component (Shadow DOM friendly)
83
83
  - **Accessible** — focusable, arrow-key crosshair, ARIA summary of the data
84
84
 
85
- ## Run the demo
85
+ ## Try it online
86
+
87
+ The demo site is deployed to GitHub Pages:
88
+ **https://benyblack.github.io/wickchart/** — a landing page with a live hero
89
+ chart, the full interactive demo, and the zero-JavaScript declarative page.
90
+
91
+ ## Run the demo locally
86
92
 
87
93
  ```bash
88
94
  npm run dev # serves on http://localhost:5173
@@ -125,6 +131,7 @@ chart.setData([
125
131
  | `stats` | off | Live statistics chip for the visible range |
126
132
  | `profile` | off | Volume profile overlay (POC + 70% value area) |
127
133
  | `annotations` | off | Smart annotations (volume spikes, gaps, pivots, RSI divergences) |
134
+ | `volshading` | off | Volatility-regime background shading (see below) |
128
135
 
129
136
  \* `indicators=""` disables everything, including volume. Token syntax:
130
137
  `name[:param[/param…]][@color]` — e.g. `sma:20@#ff0000`, `macd:12/26/9`.
@@ -146,7 +153,7 @@ Register your own — anything from a one-liner moving average to a multi-line
146
153
  pane:
147
154
 
148
155
  ```js
149
- HabChart.registerIndicator('vwap', {
156
+ WickChart.registerIndicator('vwap', {
150
157
  kind: 'overlay', // or 'pane'
151
158
  params: { period: 20 }, // defaults; set via indicators="vwap:30"
152
159
  compute(bars, params) { // bars: normalized {time,open,high,low,close,volume}
@@ -164,13 +171,119 @@ HabChart.registerIndicator('vwap', {
164
171
  chart.indicators = 'vwap:20';
165
172
  ```
166
173
 
167
- `import HabChart from 'wickchart'` gives you the class for
168
- `HabChart.registerIndicator(...)` (the element is registered as a side effect
174
+ `import WickChart from 'wickchart'` gives you the class for
175
+ `WickChart.registerIndicator(...)` (the element is registered as a side effect
169
176
  of importing the package).
170
177
 
178
+ ### WickScript — custom indicators as expressions
179
+
180
+ No build step, no JS: write an indicator inline in the attribute. `expr:{…}`
181
+ draws on the price chart; `pexpr:{…}` gets its own pane. Add an optional
182
+ `@color`, mix freely with named indicators, and it all round-trips through
183
+ shareable URLs.
184
+
185
+ ```html
186
+ <wick-chart indicators="sma:20 expr:{(close - sma(close,20)) / sma(close,20) * 100}@ff6a00"></wick-chart>
187
+
188
+ <!-- oscillator in its own pane -->
189
+ <wick-chart indicators="pexpr:{rsi(close,14)} pexpr:{change(close) / close * 100}"></wick-chart>
190
+ ```
191
+
192
+ | Series variables | |
193
+ |---|---|
194
+ | `open` `high` `low` `close` `volume` | raw bar fields |
195
+ | `hl2` `hlc3` `ohlc4` | classic derived prices |
196
+
197
+ | Functions | |
198
+ |---|---|
199
+ | `sma(x,n)` `ema(x,n)` `wma(x,n)` `stddev(x,n)` | moving stats (window `n` must be a whole number ≥ 1) |
200
+ | `rsi(x,n)` | RSI of any series |
201
+ | `hh(x,n)` `ll(x,n)` | rolling highest / lowest |
202
+ | `prev(x[,k])` `change(x)` | shifted series / bar-to-bar delta |
203
+ | `abs(x)` `sqrt(x)` `log(x)` `min(a,b)` `max(a,b)` | element-wise math |
204
+ | `crossup(a,b)` `crossdown(a,b)` | 1 on a strict cross, else 0 |
205
+
206
+ Operators are `+ - * / %` with usual precedence, unary `-`, and parentheses.
207
+ Values before a window fills are `NaN` (not drawn), division by zero yields
208
+ `NaN`, and identifiers are case-insensitive.
209
+
210
+ The expression is compiled by a hand-written tokenizer + recursive-descent
211
+ parser in `wickchart/core` — **no `eval`, no `new Function`** — with caps on
212
+ length (512), tokens (128) and nesting (24). Invalid scripts are reported via
213
+ the parse result's `unknown` list and simply not drawn; they can never execute
214
+ anything.
215
+
216
+ Programmatically, compile once and reuse, or register it under a name for the
217
+ attribute syntax:
218
+
219
+ ```js
220
+ import { scriptIndicator } from 'wickchart/core';
221
+
222
+ WickChart.registerIndicator('spread', scriptIndicator('close - ema(close,21)'));
223
+ chart.indicators = 'spread'; // now usable like any built-in
224
+ ```
225
+
226
+ The demo has a live input for it (type an expression, optionally tick *pane*,
227
+ press **+ Expr** — invalid expressions show the compiler's error inline).
228
+
229
+ ### Volatility-regime shading
230
+
231
+ `<wick-chart volshading>` tints the price pane background by realized
232
+ volatility — the rolling stddev of log returns (20 bars by default),
233
+ classified against its own full-history percentiles: **calm** (≤ 30th
234
+ percentile, subtle blue), **normal** (untinted), **hot** (≥ 70th percentile,
235
+ subtle red). Market state at a glance: quiet ranges and violent expansions
236
+ read instantly, and the legend shows the hovered bar's regime and
237
+ percentile (`VOL 30/70 · hot · 94%ile`).
238
+
239
+ ```html
240
+ <wick-chart volshading></wick-chart> <!-- defaults 30/70, 20 bars -->
241
+ <wick-chart volshading="20/85"></wick-chart> <!-- custom cutoffs -->
242
+ <wick-chart volshading="20/85/50"></wick-chart> <!-- + 50-bar vol window -->
243
+ ```
244
+
245
+ Cutoffs are clamped so the low percentile always stays at least 2 points
246
+ below the high one; the toggle and custom cutoffs round-trip through
247
+ shareable URLs (`vsh=1` / `vsh=20/85`). A degenerate history (flat series)
248
+ classifies everything as normal. The pieces are exported from
249
+ `wickchart/core` (`calcRealizedVol`, `volRegimeBands`, `percentileOfSorted`)
250
+ if you want to build on them.
251
+
252
+ ### AI-ready data window — `getDataWindow()`
253
+
254
+ One call turns whatever is on screen into a compact, LLM-pasteable summary.
255
+ Everything is computed locally from the visible bars — trend (least-squares
256
+ drift + fit), realized-vol percentile, SMA/RSI snapshot, up/down bar mix,
257
+ volume profile notes, and the same pattern detection that powers smart
258
+ annotations (gaps, spikes, pivots, divergences). Nothing leaves the page
259
+ until you copy it somewhere.
260
+
261
+ ```js
262
+ const s = chart.getDataWindow();
263
+ s.text; // markdown — ready to paste into any AI chat
264
+ s.trend; // { label: 'strong uptrend', slopePctPerBar: 0.77, r2: 0.94 }
265
+ s.volPctile; // 84 → hot regime relative to the window itself
266
+ s.patterns; // [{ time, note }] — most recent first
267
+ ```
268
+
269
+ `text` renders like:
270
+
271
+ ```
272
+ CHART SUMMARY — BTC · 1h · 214 bars · 2026-08-21 → 2026-09-07
273
+ - Close 97.03 (−1.20% over window). High 104.20 on 2026-08-28, low 91.40 on 2026-09-01. Max drawdown 8.1%.
274
+ - Trend: downtrend (drift −0.061%/bar, fit r² 0.58). Price below SMA20 (99.10). RSI(14) 41.3.
275
+ - Volatility: annualized 48%; latest realized vol at the 84th percentile of the window (hot regime).
276
+ - Bars: 96 up / 117 down. Volume avg 1.2K/bar, peak 8.9K on 2026-09-01.
277
+ - Notable: Gapped down −1.42% (2026-09-01); Volume 4.1× average (2026-09-03).
278
+ ```
279
+
280
+ The demo's **Explain** button shows this in a panel with a one-click copy.
281
+ The pure function behind it (`windowSummary(bars, i0, i1, opts)`) is exported
282
+ from `wickchart/core` for server-side use.
283
+
171
284
  ### Sonification — the chart by ear
172
285
 
173
- `<hab-chart sonify>` maps price to pitch (180–880 Hz across the visible
286
+ `<wick-chart sonify>` maps price to pitch (180–880 Hz across the visible
174
287
  scale, log-aware): moving the crosshair with the mouse or **arrow keys** plays
175
288
  a short tone per bar, so trend and shape are audible — a rare accessibility
176
289
  win for screen-reader users. `chart.playRange()` sweeps the whole visible
@@ -184,7 +297,7 @@ Tag charts with the same channel and they share pointers — across browser
184
297
  tabs, or between multiple charts on one page:
185
298
 
186
299
  ```html
187
- <hab-chart co-view="btc-room"></hab-chart>
300
+ <wick-chart co-view="btc-room"></wick-chart>
188
301
  ```
189
302
 
190
303
  Hovering in one tab draws a ghost crosshair (accent, dotted, with the time
@@ -195,12 +308,12 @@ stops moving. Same-origin only (BroadcastChannel); the connection follows the
195
308
 
196
309
  ### Smart annotations
197
310
 
198
- `<hab-chart annotations>` marks notable events on the visible range — volume
311
+ `<wick-chart annotations>` marks notable events on the visible range — volume
199
312
  spikes (>3× average), price gaps, 41-bar pivot highs/lows, and RSI
200
313
  divergences — with lettered badges (V/G/H/L/D). Hover a badged bar and the
201
314
  legend shows a one-line insight ("Volume 4.2× average", "Bearish RSI
202
315
  divergence"). The current set is emitted on every recompute via the
203
- `hab:annotations` event, so hosts can build their own UI from it. Badges are
316
+ `wick:annotations` event, so hosts can build their own UI from it. Badges are
204
317
  hidden at extreme zoom-out, where bars collapse into columns.
205
318
 
206
319
  ### Example: VWAP via the registry
@@ -209,9 +322,9 @@ VWAP ships in the demo but *not* as a builtin — it's the reference for writing
209
322
  your own (session-anchored, resets each trading day):
210
323
 
211
324
  ```js
212
- import HabChart from 'wickchart';
325
+ import WickChart from 'wickchart';
213
326
 
214
- HabChart.registerIndicator('vwap', {
327
+ WickChart.registerIndicator('vwap', {
215
328
  kind: 'overlay',
216
329
  params: {},
217
330
  compute(bars) {
@@ -243,6 +356,7 @@ chart.indicators = 'vwap';
243
356
  | `getVisibleRange()` | → `{ from, to }` (ms timestamps) |
244
357
  | `setVisibleRange({from, to})` | Jump to a time window |
245
358
  | `exportPNG()` | → PNG data URL of the current canvas |
359
+ | `getDataWindow()` | → AI-ready summary of the visible window (see below) |
246
360
  | `getState()` | → serializable snapshot (type, indicators, view, positions, alerts) |
247
361
  | `setState(state)` | Apply a snapshot; a pending view applies after the next `setData()` |
248
362
 
@@ -273,7 +387,7 @@ chart.addPosition({ id: 'x1', side: 'short', entry: 66000, qty: 1 });
273
387
  chart.removePosition('x1');
274
388
 
275
389
  chart.addAlert({ price: 65000, direction: 'above' }); // 'above' | 'below' | 'cross'
276
- chart.addEventListener('hab:alert', (e) => {
390
+ chart.addEventListener('wick:alert', (e) => {
277
391
  console.log('crossed!', e.detail.id, e.detail.price);
278
392
  });
279
393
  ```
@@ -283,12 +397,12 @@ The P&L chip recalculates on every streamed bar. Alerts are edge-triggered
283
397
 
284
398
  ### Stats & measure
285
399
 
286
- `<hab-chart stats>` shows live statistics of the visible range — return %,
400
+ `<wick-chart stats>` shows live statistics of the visible range — return %,
287
401
  max drawdown, annualized volatility, up/down bar counts, average volume —
288
402
  recalculated as you pan and zoom.
289
403
 
290
404
  Hold **Shift and drag** across the chart to measure a move: an overlay shows
291
- Δprice, Δ%, bar count and elapsed time, and a `hab:measure` event fires on
405
+ Δprice, Δ%, bar count and elapsed time, and a `wick:measure` event fires on
292
406
  release (`detail.from` / `detail.to` carry index, time and price). Click or
293
407
  press `Esc` to clear.
294
408
 
@@ -315,9 +429,9 @@ Reflected properties (`chart.type = 'line'`) work for `theme`, `type`, `label`,
315
429
 
316
430
  | Event | Detail |
317
431
  | --------------- | ---------------------------------------------------------- |
318
- | `hab:crosshair` | `{ index, bar, x, y, price }` on hover / arrows, `null` on leave |
319
- | `hab:range` | `{ from, to }` after zoom / pan / jump |
320
- | `hab:select` | `{ index, bar, price }` on click/tap (e.g. open an order form at that price) |
432
+ | `wick:crosshair` | `{ index, bar, x, y, price }` on hover / arrows, `null` on leave |
433
+ | `wick:range` | `{ from, to }` after zoom / pan / jump |
434
+ | `wick:select` | `{ index, bar, price }` on click/tap (e.g. open an order form at that price) |
321
435
 
322
436
  ## Theming
323
437
 
@@ -325,18 +439,18 @@ All colors are CSS custom properties settable on the element (they pierce the
325
439
  Shadow DOM):
326
440
 
327
441
  ```css
328
- hab-chart {
329
- --hab-bg: #0d1117; /* transparent works too */
330
- --hab-up: #16c784;
331
- --hab-down: #ea3943;
332
- --hab-accent: #4c8dff; /* line & area color */
333
- --hab-text: #8b949e; /* axis text */
334
- --hab-text-strong: #e6edf3; /* legend values */
335
- --hab-grid: rgba(230,237,243,.05);
336
- --hab-border: rgba(230,237,243,.09);
337
- --hab-crosshair: rgba(230,237,243,.42);
338
- --hab-rsi: #a78bfa;
339
- --hab-overlay-0: #f0b429; /* SMA color, …-1, -2, … for more overlays */
442
+ wick-chart {
443
+ --wick-bg: #0d1117; /* transparent works too */
444
+ --wick-up: #16c784;
445
+ --wick-down: #ea3943;
446
+ --wick-accent: #4c8dff; /* line & area color */
447
+ --wick-text: #8b949e; /* axis text */
448
+ --wick-text-strong: #e6edf3; /* legend values */
449
+ --wick-grid: rgba(230,237,243,.05);
450
+ --wick-border: rgba(230,237,243,.09);
451
+ --wick-crosshair: rgba(230,237,243,.42);
452
+ --wick-rsi: #a78bfa;
453
+ --wick-overlay-0: #f0b429; /* SMA color, …-1, -2, … for more overlays */
340
454
  }
341
455
  ```
342
456
 
@@ -399,6 +513,27 @@ column, and an offscreen layer so hover only repaints the crosshair.
399
513
  - Incremental (O(1)) indicator updates for high-frequency streaming
400
514
  - Min/max downsampling and/or an offscreen hover layer if profiling ever demands
401
515
 
516
+ ## Migrating from 0.x (HabView)
517
+
518
+ 1.0 renames the public surface to the WickChart brand. The 0.x names keep
519
+ working as **deprecated aliases** (removed in 2.0), so upgrading is safe to
520
+ do lazily:
521
+
522
+ | 0.x (deprecated alias) | 1.0 canonical |
523
+ |---|---|
524
+ | `<hab-chart>` / `<hab-feed>` | `<wick-chart>` / `<wick-feed>` |
525
+ | `hab:range`, `hab:select`, `hab:alert`, `hab:crosshair`, `hab:measure`, `hab:annotations` | `wick:*` of the same name (both fire during 1.x) |
526
+ | `hab-feed:status` / `hab-feed:fallback` | `wick-feed:status` / `wick-feed:fallback` (both fire during 1.x) |
527
+ | `--hab-bg`, `--hab-up`, … | `--wick-*` of the same name (`--wick-*` wins; `--hab-*` is the fallback) |
528
+ | `HabChart` / `HabFeed` classes | `WickChart` / `WickFeed` (also as named exports) |
529
+ | HabScript (the `expr:{…}` language) | WickScript — syntax unchanged |
530
+ | `import … from 'wickchart/src/hab-chart.js'` | use the package entry points (`wickchart`, `wickchart/core`, `wickchart/feed`) — module files are renamed |
531
+
532
+ Two behavioral notes: custom indicators registered via
533
+ `WickChart.registerIndicator()` are shared with the legacy `<hab-chart>`
534
+ alias (one registry), and cross-tab co-view channels are now prefixed
535
+ `wick-co-view:` (a 0.x tab and a 1.x tab won't pair — refresh both).
536
+
402
537
  ## License
403
538
 
404
539
  MIT
package/package.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "name": "wickchart",
3
- "version": "0.3.0",
4
- "description": "<hab-chart> — a modern, dependency-free financial charting web component. Candles, line & area charts, crosshair, zoom/pan, indicators, live streaming, theming.",
3
+ "version": "1.0.0",
4
+ "description": "<wick-chart> — a modern, dependency-free financial charting web component. Candles, line & area charts, crosshair, zoom/pan, indicators (incl. a safe expression mini-language), live streaming via <wick-feed>, theming.",
5
5
  "type": "module",
6
- "main": "src/hab-chart.js",
7
- "module": "src/hab-chart.js",
8
- "types": "types/hab-chart.d.ts",
6
+ "main": "src/wick-chart.js",
7
+ "module": "src/wick-chart.js",
8
+ "types": "types/wick-chart.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
- "types": "./types/hab-chart.d.ts",
12
- "default": "./src/hab-chart.js"
11
+ "types": "./types/wick-chart.d.ts",
12
+ "default": "./src/wick-chart.js"
13
13
  },
14
14
  "./core": {
15
15
  "types": "./types/core.d.ts",
16
16
  "default": "./src/core.js"
17
17
  },
18
18
  "./feed": {
19
- "types": "./types/hab-feed.d.ts",
20
- "default": "./src/hab-feed.js"
19
+ "types": "./types/wick-feed.d.ts",
20
+ "default": "./src/wick-feed.js"
21
21
  }
22
22
  },
23
23
  "files": [
@@ -25,14 +25,14 @@
25
25
  "types"
26
26
  ],
27
27
  "sideEffects": [
28
- "src/hab-chart.js"
28
+ "src/wick-chart.js"
29
29
  ],
30
30
  "scripts": {
31
31
  "dev": "npx --yes serve . -l 5173",
32
32
  "test": "node --test \"tests/*.test.mjs\"",
33
- "build:types": "tsc -p tsconfig.json",
33
+ "build:types": "node -e \"require('fs').rmSync('types', { recursive: true, force: true });\" && tsc -p tsconfig.json",
34
34
  "prepack": "npm run build:types",
35
- "ci": "npm run build:types && npm test && node --check src/hab-chart.js && node --check src/core.js && node --check demo/app.js"
35
+ "ci": "npm run build:types && npm test && node --check src/wick-chart.js && node --check src/wick-feed.js && node --check src/core.js && node --check demo/app.js"
36
36
  },
37
37
  "keywords": [
38
38
  "chart",