wickchart 0.4.0 → 1.2.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 +274 -48
- package/package.json +29 -12
- package/src/core.js +452 -12
- package/src/feeds.js +1 -1
- package/src/react-core.js +191 -0
- package/src/react.js +21 -0
- package/src/{hab-chart.js → wick-chart.js} +525 -111
- package/src/{hab-feed.js → wick-feed.js} +34 -19
- package/types/core.d.ts +209 -3
- package/types/react-core.d.ts +64 -0
- package/types/react.d.ts +3 -0
- package/types/{hab-chart.d.ts → wick-chart.d.ts} +215 -13
- package/types/{hab-feed.d.ts → wick-feed.d.ts} +8 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#
|
|
1
|
+
# WickChart
|
|
2
2
|
|
|
3
|
-
**`<
|
|
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,9 +14,10 @@ npm install wickchart
|
|
|
14
14
|
|
|
15
15
|
```js
|
|
16
16
|
// any bundler / framework — TypeScript types included
|
|
17
|
-
import 'wickchart'; // registers <
|
|
18
|
-
import
|
|
17
|
+
import 'wickchart'; // registers <wick-chart>
|
|
18
|
+
import WickChart from 'wickchart'; // for WickChart.registerIndicator(...)
|
|
19
19
|
import { encodeStateQuery } from 'wickchart/core'; // pure helpers
|
|
20
|
+
import { WickChart } from 'wickchart/react'; // React bindings (optional)
|
|
20
21
|
```
|
|
21
22
|
|
|
22
23
|
Or straight from a CDN — no install, no build:
|
|
@@ -24,10 +25,10 @@ Or straight from a CDN — no install, no build:
|
|
|
24
25
|
```html
|
|
25
26
|
<script type="module" src="https://unpkg.com/wickchart"></script>
|
|
26
27
|
|
|
27
|
-
<
|
|
28
|
+
<wick-chart label="BTC · 1h" type="candles" indicators="sma:20 volume"></wick-chart>
|
|
28
29
|
|
|
29
30
|
<script type="module">
|
|
30
|
-
const chart = document.querySelector('
|
|
31
|
+
const chart = document.querySelector('wick-chart');
|
|
31
32
|
chart.setData(bars); // [{ time, open, high, low, close, volume }]
|
|
32
33
|
chart.update(bar); // stream live updates
|
|
33
34
|
</script>
|
|
@@ -37,7 +38,7 @@ Works in plain HTML, React, Vue, Svelte, Angular — anywhere a `<div>` works.
|
|
|
37
38
|
TypeScript declarations ship inside the package (generated at pack time from
|
|
38
39
|
the JSDoc-annotated source — the repo itself stays 100% dependency-free JS).
|
|
39
40
|
|
|
40
|
-
## Declarative live charts with `<
|
|
41
|
+
## Declarative live charts with `<wick-feed>`
|
|
41
42
|
|
|
42
43
|
One more script tag and your chart is fully live — data, backfill, streaming —
|
|
43
44
|
with **zero JavaScript written**:
|
|
@@ -45,13 +46,13 @@ with **zero JavaScript written**:
|
|
|
45
46
|
```html
|
|
46
47
|
<script type="module" src="https://unpkg.com/wickchart/feed"></script>
|
|
47
48
|
|
|
48
|
-
<
|
|
49
|
-
<
|
|
49
|
+
<wick-feed for="chart" binance="BTCUSDT" tf="1h"></wick-feed>
|
|
50
|
+
<wick-chart id="chart" indicators="sma:20 volume" profile></wick-chart>
|
|
50
51
|
```
|
|
51
52
|
|
|
52
53
|
| Attribute | Meaning |
|
|
53
54
|
| ----------- | ----------------------------------------------------------------------- |
|
|
54
|
-
| `for` | target `<
|
|
55
|
+
| `for` | target `<wick-chart>` id (auto-pairs with the first chart when omitted) |
|
|
55
56
|
| `binance` | Binance symbol (`BTCUSDT`) — REST load + WebSocket live + backfill |
|
|
56
57
|
| `demo` | deterministic offline synthetic feed (`demo="ETH"` picks a base price) |
|
|
57
58
|
| `url` | generic REST endpoint returning a JSON array of bars (+ `poll="10"` sec) |
|
|
@@ -61,7 +62,7 @@ with **zero JavaScript written**:
|
|
|
61
62
|
|
|
62
63
|
The element reflects its state in the `status` attribute (`loading`, `live`,
|
|
63
64
|
`polling`, `fallback`, `loaded`, `waiting`, `idle`) and emits
|
|
64
|
-
`
|
|
65
|
+
`wick-feed:status` / `wick-feed:fallback` events. When Binance is unreachable
|
|
65
66
|
(geo-blocked, offline), it degrades gracefully: WebSocket → REST polling → a
|
|
66
67
|
synthetic stream bridged from the last real price, so the chart never goes
|
|
67
68
|
blank. It also wires `chart.onloadmore` for infinite backfill automatically.
|
|
@@ -71,7 +72,7 @@ blank. It also wires `chart.onloadmore` for infinite backfill automatically.
|
|
|
71
72
|
## Why another chart library?
|
|
72
73
|
|
|
73
74
|
TradingView's charting library is powerful but heavy and enterprise-licensed;
|
|
74
|
-
most wrappers add build steps and framework lock-in.
|
|
75
|
+
most wrappers add build steps and framework lock-in. WickChart takes the opposite
|
|
75
76
|
bet:
|
|
76
77
|
|
|
77
78
|
- **Zero dependencies, single file** (~40 KB unminified, no build step required)
|
|
@@ -82,7 +83,20 @@ bet:
|
|
|
82
83
|
outside the component (Shadow DOM friendly)
|
|
83
84
|
- **Accessible** — focusable, arrow-key crosshair, ARIA summary of the data
|
|
84
85
|
|
|
85
|
-
##
|
|
86
|
+
## Try it online
|
|
87
|
+
|
|
88
|
+
The demo site is deployed to GitHub Pages:
|
|
89
|
+
**https://benyblack.github.io/wickchart/** — a landing page with a live hero
|
|
90
|
+
chart, the full interactive demo, the zero-JavaScript declarative page, and a
|
|
91
|
+
[React demo](./demo/react.html) driven entirely by React state.
|
|
92
|
+
|
|
93
|
+
**Full documentation lives at
|
|
94
|
+
[benyblack.github.io/wickchart/docs.html](./docs.html)** — every attribute,
|
|
95
|
+
method, event, the WickScript reference, overlays (with a live JSON
|
|
96
|
+
playground), feeds, theming and framework bindings, each with runnable
|
|
97
|
+
examples. This README covers the same ground in plain markdown.
|
|
98
|
+
|
|
99
|
+
## Run the demo locally
|
|
86
100
|
|
|
87
101
|
```bash
|
|
88
102
|
npm run dev # serves on http://localhost:5173
|
|
@@ -99,6 +113,101 @@ with graceful fallback to synthetic data if it isn't.
|
|
|
99
113
|
|
|
100
114
|
---
|
|
101
115
|
|
|
116
|
+
## Frameworks
|
|
117
|
+
|
|
118
|
+
`<wick-chart>` is framework-agnostic — attributes, one `data` property,
|
|
119
|
+
standard DOM events. The one opinionated wrapper ships as `wickchart/react`,
|
|
120
|
+
which turns that contract into idiomatic React with proper event
|
|
121
|
+
subscription/cleanup. `react` is an **optional** peer dependency: nothing
|
|
122
|
+
changes if you never import `wickchart/react`.
|
|
123
|
+
|
|
124
|
+
### React
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
npm install wickchart react
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```jsx
|
|
131
|
+
import { WickChart, useWickChart } from 'wickchart/react';
|
|
132
|
+
|
|
133
|
+
// drop-in component — props map 1:1 onto the element
|
|
134
|
+
export function PriceChart({ bars, onRange }) {
|
|
135
|
+
return (
|
|
136
|
+
<WickChart
|
|
137
|
+
type="candles"
|
|
138
|
+
indicators="sma:20 ema:50 volume"
|
|
139
|
+
volshading
|
|
140
|
+
label="BTC · 1h"
|
|
141
|
+
data={bars} // bars are assigned as a property
|
|
142
|
+
onRange={onRange} // subscribes to wick:range
|
|
143
|
+
onAlert={(e) => toast(`crossed ${e.detail.price}`)}
|
|
144
|
+
style={{ height: 420 }}
|
|
145
|
+
/>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// or the hook, when you need the imperative API
|
|
150
|
+
function PracticeChart({ bars }) {
|
|
151
|
+
const { ref, chart } = useWickChart({ data: bars, indicators: 'sma:20' });
|
|
152
|
+
// chart.getDataWindow(), chart.addAlert(...), chart.getState() … after mount
|
|
153
|
+
return <wick-chart ref={ref} style={{ height: 420 }} />;
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Rules of thumb:
|
|
158
|
+
|
|
159
|
+
- **Pass a fresh array** to `data` when the bars change — the binding compares
|
|
160
|
+
by reference, and reassignment is what triggers a redraw (don't mutate).
|
|
161
|
+
The same rule applies to `overlays` (see
|
|
162
|
+
[Server-side overlays](#server-side-overlays-zones--levels)).
|
|
163
|
+
- **String/number/boolean props become attributes** (`type`, `indicators`,
|
|
164
|
+
`volshading`, …); `className`/`style`/`id` reach React as usual.
|
|
165
|
+
- **`onXxx` subscribes to `wick:xxx`** with cleanup on unmount; an
|
|
166
|
+
`events={{ range: fn }}` object works too.
|
|
167
|
+
- Works the same on React 16.8 → 19 — no custom-element event caveats.
|
|
168
|
+
|
|
169
|
+
No build step? The [React demo](./demo/react.html) runs straight off a CDN
|
|
170
|
+
import map — `react` and `react-dom` from esm.sh, the bindings from the
|
|
171
|
+
package source.
|
|
172
|
+
|
|
173
|
+
### Vue 3
|
|
174
|
+
|
|
175
|
+
```vue
|
|
176
|
+
<script setup>
|
|
177
|
+
import { ref, onMounted } from 'vue';
|
|
178
|
+
import 'wickchart';
|
|
179
|
+
const chart = ref(null);
|
|
180
|
+
const bars = ref([]);
|
|
181
|
+
onMounted(async () => {
|
|
182
|
+
bars.value = await loadBars();
|
|
183
|
+
chart.value.data = bars.value;
|
|
184
|
+
chart.value.addEventListener('wick:range', (e) => console.log(e.detail));
|
|
185
|
+
});
|
|
186
|
+
</script>
|
|
187
|
+
|
|
188
|
+
<template>
|
|
189
|
+
<wick-chart ref="chart" type="candles" indicators="sma:20"
|
|
190
|
+
style="height: 420px"></wick-chart>
|
|
191
|
+
</template>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Svelte
|
|
195
|
+
|
|
196
|
+
```svelte
|
|
197
|
+
<script>
|
|
198
|
+
import 'wickchart';
|
|
199
|
+
let el;
|
|
200
|
+
let bars = [];
|
|
201
|
+
$: if (el && bars.length) el.data = bars;
|
|
202
|
+
</script>
|
|
203
|
+
|
|
204
|
+
<wick-chart bind:this={el} type="candles" indicators="sma:20"
|
|
205
|
+
on:wick:alert={(e) => console.log(e.detail)}
|
|
206
|
+
style="height: 420px"></wick-chart>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
102
211
|
## Data format
|
|
103
212
|
|
|
104
213
|
Bars are plain objects; `time` accepts **milliseconds or seconds** (auto-detected).
|
|
@@ -126,6 +235,7 @@ chart.setData([
|
|
|
126
235
|
| `profile` | off | Volume profile overlay (POC + 70% value area) |
|
|
127
236
|
| `annotations` | off | Smart annotations (volume spikes, gaps, pivots, RSI divergences) |
|
|
128
237
|
| `volshading` | off | Volatility-regime background shading (see below) |
|
|
238
|
+
| `overlays` | – | JSON array of server-side zones & levels (see below) |
|
|
129
239
|
|
|
130
240
|
\* `indicators=""` disables everything, including volume. Token syntax:
|
|
131
241
|
`name[:param[/param…]][@color]` — e.g. `sma:20@#ff0000`, `macd:12/26/9`.
|
|
@@ -147,7 +257,7 @@ Register your own — anything from a one-liner moving average to a multi-line
|
|
|
147
257
|
pane:
|
|
148
258
|
|
|
149
259
|
```js
|
|
150
|
-
|
|
260
|
+
WickChart.registerIndicator('vwap', {
|
|
151
261
|
kind: 'overlay', // or 'pane'
|
|
152
262
|
params: { period: 20 }, // defaults; set via indicators="vwap:30"
|
|
153
263
|
compute(bars, params) { // bars: normalized {time,open,high,low,close,volume}
|
|
@@ -165,11 +275,11 @@ HabChart.registerIndicator('vwap', {
|
|
|
165
275
|
chart.indicators = 'vwap:20';
|
|
166
276
|
```
|
|
167
277
|
|
|
168
|
-
`import
|
|
169
|
-
`
|
|
278
|
+
`import WickChart from 'wickchart'` gives you the class for
|
|
279
|
+
`WickChart.registerIndicator(...)` (the element is registered as a side effect
|
|
170
280
|
of importing the package).
|
|
171
281
|
|
|
172
|
-
###
|
|
282
|
+
### WickScript — custom indicators as expressions
|
|
173
283
|
|
|
174
284
|
No build step, no JS: write an indicator inline in the attribute. `expr:{…}`
|
|
175
285
|
draws on the price chart; `pexpr:{…}` gets its own pane. Add an optional
|
|
@@ -177,10 +287,10 @@ draws on the price chart; `pexpr:{…}` gets its own pane. Add an optional
|
|
|
177
287
|
shareable URLs.
|
|
178
288
|
|
|
179
289
|
```html
|
|
180
|
-
<
|
|
290
|
+
<wick-chart indicators="sma:20 expr:{(close - sma(close,20)) / sma(close,20) * 100}@ff6a00"></wick-chart>
|
|
181
291
|
|
|
182
292
|
<!-- oscillator in its own pane -->
|
|
183
|
-
<
|
|
293
|
+
<wick-chart indicators="pexpr:{rsi(close,14)} pexpr:{change(close) / close * 100}"></wick-chart>
|
|
184
294
|
```
|
|
185
295
|
|
|
186
296
|
| Series variables | |
|
|
@@ -213,7 +323,7 @@ attribute syntax:
|
|
|
213
323
|
```js
|
|
214
324
|
import { scriptIndicator } from 'wickchart/core';
|
|
215
325
|
|
|
216
|
-
|
|
326
|
+
WickChart.registerIndicator('spread', scriptIndicator('close - ema(close,21)'));
|
|
217
327
|
chart.indicators = 'spread'; // now usable like any built-in
|
|
218
328
|
```
|
|
219
329
|
|
|
@@ -222,7 +332,7 @@ press **+ Expr** — invalid expressions show the compiler's error inline).
|
|
|
222
332
|
|
|
223
333
|
### Volatility-regime shading
|
|
224
334
|
|
|
225
|
-
`<
|
|
335
|
+
`<wick-chart volshading>` tints the price pane background by realized
|
|
226
336
|
volatility — the rolling stddev of log returns (20 bars by default),
|
|
227
337
|
classified against its own full-history percentiles: **calm** (≤ 30th
|
|
228
338
|
percentile, subtle blue), **normal** (untinted), **hot** (≥ 70th percentile,
|
|
@@ -231,9 +341,9 @@ read instantly, and the legend shows the hovered bar's regime and
|
|
|
231
341
|
percentile (`VOL 30/70 · hot · 94%ile`).
|
|
232
342
|
|
|
233
343
|
```html
|
|
234
|
-
<
|
|
235
|
-
<
|
|
236
|
-
<
|
|
344
|
+
<wick-chart volshading></wick-chart> <!-- defaults 30/70, 20 bars -->
|
|
345
|
+
<wick-chart volshading="20/85"></wick-chart> <!-- custom cutoffs -->
|
|
346
|
+
<wick-chart volshading="20/85/50"></wick-chart> <!-- + 50-bar vol window -->
|
|
237
347
|
```
|
|
238
348
|
|
|
239
349
|
Cutoffs are clamped so the low percentile always stays at least 2 points
|
|
@@ -243,6 +353,69 @@ classifies everything as normal. The pieces are exported from
|
|
|
243
353
|
`wickchart/core` (`calcRealizedVol`, `volRegimeBands`, `percentileOfSorted`)
|
|
244
354
|
if you want to build on them.
|
|
245
355
|
|
|
356
|
+
### Server-side overlays (zones & levels)
|
|
357
|
+
|
|
358
|
+
Draw analysis from your own API straight onto the chart: supply/demand
|
|
359
|
+
**zones** (time × price rectangles) and horizontal **levels**, rendered
|
|
360
|
+
behind the candles. Zones without a `to` extend into future space past the
|
|
361
|
+
last bar, like TradingView drawings.
|
|
362
|
+
|
|
363
|
+
```js
|
|
364
|
+
const res = await fetch('https://api.example.com/analysis?symbol=BTC');
|
|
365
|
+
chart.setOverlays(await res.json());
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
```js
|
|
369
|
+
[
|
|
370
|
+
// zone: from/to are timestamps (ms or s); null → chart edge
|
|
371
|
+
{ type: 'zone', from: 1753920000000, priceFrom: 33000, priceTo: 35600,
|
|
372
|
+
color: '#ef5350', alpha: 0.25, label: 'demand' },
|
|
373
|
+
{ type: 'zone', from: 1753920000000, // no `to` → extends
|
|
374
|
+
priceFrom: 37700, priceTo: 40900, color: '#26a69a' }, // to the right edge
|
|
375
|
+
// level: horizontal price line, full width by default
|
|
376
|
+
{ type: 'level', price: 28700, color: '#3f51b5', label: 'S1' },
|
|
377
|
+
{ type: 'level', price: 22800, color: '#3f51b5', dash: true },
|
|
378
|
+
]
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
- `addOverlay(o)` upserts one (by `id`), `removeOverlay(id)`,
|
|
382
|
+
`clearOverlays()`, and `chart.overlays` reads them back.
|
|
383
|
+
- Colors accept hex / `rgb()` / CSS names plus the palette keys
|
|
384
|
+
`up` | `down` | `accent`; `alpha` clamps to 0.02–0.8 (default 0.22).
|
|
385
|
+
- Timestamps snap to bars (before the first bar clamps left, after the last
|
|
386
|
+
clamps right); invalid entries are dropped, never thrown — it's API data.
|
|
387
|
+
- Fully declarative, too — the same JSON as an attribute:
|
|
388
|
+
|
|
389
|
+
```html
|
|
390
|
+
<wick-chart overlays='[{"type":"level","price":28700,"color":"#3f51b5","label":"S1"}]'></wick-chart>
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
The React binding takes `overlays` as a prop (fresh array → re-apply), and
|
|
394
|
+
`normalizeOverlays` / `barIndexForTime` / `resolveOverlayColor` are exported
|
|
395
|
+
from `wickchart/core`.
|
|
396
|
+
|
|
397
|
+
### Scenario mode — ghost paths & volatility cones
|
|
398
|
+
|
|
399
|
+
Project what-if into future space: a ghost path of hypothetical prices plus
|
|
400
|
+
a volatility cone (±1σ/±2σ bands widening with √h from realized vol).
|
|
401
|
+
|
|
402
|
+
```js
|
|
403
|
+
chart.setScenario({
|
|
404
|
+
path: [64000, 65500, 66800, 68000], // prices for future bars 1..N
|
|
405
|
+
cone: true, // σ-bands from realized vol (default)
|
|
406
|
+
label: 'bull case',
|
|
407
|
+
color: 'up', // up | down | accent or safe colors
|
|
408
|
+
});
|
|
409
|
+
chart.setScenario({ horizon: 48 }); // cone-only projection
|
|
410
|
+
chart.clearScenario();
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
Setting a scenario reserves future space on the right so the cone stays
|
|
414
|
+
visible; the horizon defaults to the path length (1–500) and `levels` are σ
|
|
415
|
+
multipliers (default `[1, 2]`). Like overlays, scenarios are analysis data —
|
|
416
|
+
excluded from shareable state, and the same shape a server-side model could
|
|
417
|
+
push. `calcVolCone` / `normalizeScenario` are exported from `wickchart/core`.
|
|
418
|
+
|
|
246
419
|
### AI-ready data window — `getDataWindow()`
|
|
247
420
|
|
|
248
421
|
One call turns whatever is on screen into a compact, LLM-pasteable summary.
|
|
@@ -260,6 +433,31 @@ s.volPctile; // 84 → hot regime relative to the window itself
|
|
|
260
433
|
s.patterns; // [{ time, note }] — most recent first
|
|
261
434
|
```
|
|
262
435
|
|
|
436
|
+
### AI agent interface — the chart as a tool surface
|
|
437
|
+
|
|
438
|
+
The chart can publish its own **tool manifest** and accept validated
|
|
439
|
+
tool-calls, so any LLM can operate it with zero glue code — the chart never
|
|
440
|
+
touches the network; you supply the model call.
|
|
441
|
+
|
|
442
|
+
```js
|
|
443
|
+
chart.aiTools(); // manifest: get_data_window, set_indicators, set_overlays, add_alert, …
|
|
444
|
+
chart.aiPrompt(); // system prompt demanding JSON [{tool, args}] ops
|
|
445
|
+
chart.aiContext(); // grounding: current state + visible-window summary
|
|
446
|
+
chart.applyAI(ops); // validated dispatcher — per-op {ok, result} / {ok:false, error}
|
|
447
|
+
|
|
448
|
+
const { results } = await chart.ask(
|
|
449
|
+
'add RSI, mark the demand zone, and alert me on volume spikes',
|
|
450
|
+
{ run: async (payload) => (await callMyLLM(payload)).ops }
|
|
451
|
+
);
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Every op is whitelisted and its args validated (indicator names checked
|
|
455
|
+
against the registry, overlays through the sanitizer, enums enforced) — LLM
|
|
456
|
+
output is treated as untrusted input, and a bad op returns an error the
|
|
457
|
+
model can self-correct from instead of throwing. The
|
|
458
|
+
[docs page](./docs.html) has a live playground driving `applyAI()` with an
|
|
459
|
+
offline demo agent (no network, no keys).
|
|
460
|
+
|
|
263
461
|
`text` renders like:
|
|
264
462
|
|
|
265
463
|
```
|
|
@@ -277,7 +475,7 @@ from `wickchart/core` for server-side use.
|
|
|
277
475
|
|
|
278
476
|
### Sonification — the chart by ear
|
|
279
477
|
|
|
280
|
-
`<
|
|
478
|
+
`<wick-chart sonify>` maps price to pitch (180–880 Hz across the visible
|
|
281
479
|
scale, log-aware): moving the crosshair with the mouse or **arrow keys** plays
|
|
282
480
|
a short tone per bar, so trend and shape are audible — a rare accessibility
|
|
283
481
|
win for screen-reader users. `chart.playRange()` sweeps the whole visible
|
|
@@ -291,7 +489,7 @@ Tag charts with the same channel and they share pointers — across browser
|
|
|
291
489
|
tabs, or between multiple charts on one page:
|
|
292
490
|
|
|
293
491
|
```html
|
|
294
|
-
<
|
|
492
|
+
<wick-chart co-view="btc-room"></wick-chart>
|
|
295
493
|
```
|
|
296
494
|
|
|
297
495
|
Hovering in one tab draws a ghost crosshair (accent, dotted, with the time
|
|
@@ -302,12 +500,12 @@ stops moving. Same-origin only (BroadcastChannel); the connection follows the
|
|
|
302
500
|
|
|
303
501
|
### Smart annotations
|
|
304
502
|
|
|
305
|
-
`<
|
|
503
|
+
`<wick-chart annotations>` marks notable events on the visible range — volume
|
|
306
504
|
spikes (>3× average), price gaps, 41-bar pivot highs/lows, and RSI
|
|
307
505
|
divergences — with lettered badges (V/G/H/L/D). Hover a badged bar and the
|
|
308
506
|
legend shows a one-line insight ("Volume 4.2× average", "Bearish RSI
|
|
309
507
|
divergence"). The current set is emitted on every recompute via the
|
|
310
|
-
`
|
|
508
|
+
`wick:annotations` event, so hosts can build their own UI from it. Badges are
|
|
311
509
|
hidden at extreme zoom-out, where bars collapse into columns.
|
|
312
510
|
|
|
313
511
|
### Example: VWAP via the registry
|
|
@@ -316,9 +514,9 @@ VWAP ships in the demo but *not* as a builtin — it's the reference for writing
|
|
|
316
514
|
your own (session-anchored, resets each trading day):
|
|
317
515
|
|
|
318
516
|
```js
|
|
319
|
-
import
|
|
517
|
+
import WickChart from 'wickchart';
|
|
320
518
|
|
|
321
|
-
|
|
519
|
+
WickChart.registerIndicator('vwap', {
|
|
322
520
|
kind: 'overlay',
|
|
323
521
|
params: {},
|
|
324
522
|
compute(bars) {
|
|
@@ -381,22 +579,29 @@ chart.addPosition({ id: 'x1', side: 'short', entry: 66000, qty: 1 });
|
|
|
381
579
|
chart.removePosition('x1');
|
|
382
580
|
|
|
383
581
|
chart.addAlert({ price: 65000, direction: 'above' }); // 'above' | 'below' | 'cross'
|
|
384
|
-
chart.addEventListener('
|
|
582
|
+
chart.addEventListener('wick:alert', (e) => {
|
|
385
583
|
console.log('crossed!', e.detail.id, e.detail.price);
|
|
386
584
|
});
|
|
585
|
+
|
|
586
|
+
// scripted alerts — any WickScript predicate, fired on its false→true edge
|
|
587
|
+
chart.addAlert({ when: 'crossup(rsi(close,14), 30)' });
|
|
588
|
+
chart.addAlert({ when: 'volume > sma(volume,20) * 3', once: false }); // re-arms
|
|
387
589
|
```
|
|
388
590
|
|
|
389
591
|
The P&L chip recalculates on every streamed bar. Alerts are edge-triggered
|
|
390
592
|
(fire once per crossing) and one-shot by default (`once: false` to re-arm).
|
|
593
|
+
Scripted alerts are evaluated locally on every streamed bar — the event
|
|
594
|
+
carries the triggering close as `price` plus the `when` source; an invalid
|
|
595
|
+
predicate is rejected (`addAlert` returns `null`), never thrown.
|
|
391
596
|
|
|
392
597
|
### Stats & measure
|
|
393
598
|
|
|
394
|
-
`<
|
|
599
|
+
`<wick-chart stats>` shows live statistics of the visible range — return %,
|
|
395
600
|
max drawdown, annualized volatility, up/down bar counts, average volume —
|
|
396
601
|
recalculated as you pan and zoom.
|
|
397
602
|
|
|
398
603
|
Hold **Shift and drag** across the chart to measure a move: an overlay shows
|
|
399
|
-
Δprice, Δ%, bar count and elapsed time, and a `
|
|
604
|
+
Δprice, Δ%, bar count and elapsed time, and a `wick:measure` event fires on
|
|
400
605
|
release (`detail.from` / `detail.to` carry index, time and price). Click or
|
|
401
606
|
press `Esc` to clear.
|
|
402
607
|
|
|
@@ -423,9 +628,9 @@ Reflected properties (`chart.type = 'line'`) work for `theme`, `type`, `label`,
|
|
|
423
628
|
|
|
424
629
|
| Event | Detail |
|
|
425
630
|
| --------------- | ---------------------------------------------------------- |
|
|
426
|
-
| `
|
|
427
|
-
| `
|
|
428
|
-
| `
|
|
631
|
+
| `wick:crosshair` | `{ index, bar, x, y, price }` on hover / arrows, `null` on leave |
|
|
632
|
+
| `wick:range` | `{ from, to }` after zoom / pan / jump |
|
|
633
|
+
| `wick:select` | `{ index, bar, price }` on click/tap (e.g. open an order form at that price) |
|
|
429
634
|
|
|
430
635
|
## Theming
|
|
431
636
|
|
|
@@ -433,18 +638,18 @@ All colors are CSS custom properties settable on the element (they pierce the
|
|
|
433
638
|
Shadow DOM):
|
|
434
639
|
|
|
435
640
|
```css
|
|
436
|
-
|
|
437
|
-
--
|
|
438
|
-
--
|
|
439
|
-
--
|
|
440
|
-
--
|
|
441
|
-
--
|
|
442
|
-
--
|
|
443
|
-
--
|
|
444
|
-
--
|
|
445
|
-
--
|
|
446
|
-
--
|
|
447
|
-
--
|
|
641
|
+
wick-chart {
|
|
642
|
+
--wick-bg: #0d1117; /* transparent works too */
|
|
643
|
+
--wick-up: #16c784;
|
|
644
|
+
--wick-down: #ea3943;
|
|
645
|
+
--wick-accent: #4c8dff; /* line & area color */
|
|
646
|
+
--wick-text: #8b949e; /* axis text */
|
|
647
|
+
--wick-text-strong: #e6edf3; /* legend values */
|
|
648
|
+
--wick-grid: rgba(230,237,243,.05);
|
|
649
|
+
--wick-border: rgba(230,237,243,.09);
|
|
650
|
+
--wick-crosshair: rgba(230,237,243,.42);
|
|
651
|
+
--wick-rsi: #a78bfa;
|
|
652
|
+
--wick-overlay-0: #f0b429; /* SMA color, …-1, -2, … for more overlays */
|
|
448
653
|
}
|
|
449
654
|
```
|
|
450
655
|
|
|
@@ -507,6 +712,27 @@ column, and an offscreen layer so hover only repaints the crosshair.
|
|
|
507
712
|
- Incremental (O(1)) indicator updates for high-frequency streaming
|
|
508
713
|
- Min/max downsampling and/or an offscreen hover layer if profiling ever demands
|
|
509
714
|
|
|
715
|
+
## Migrating from 0.x (HabView)
|
|
716
|
+
|
|
717
|
+
1.0 renames the public surface to the WickChart brand. The 0.x names keep
|
|
718
|
+
working as **deprecated aliases** (removed in 2.0), so upgrading is safe to
|
|
719
|
+
do lazily:
|
|
720
|
+
|
|
721
|
+
| 0.x (deprecated alias) | 1.0 canonical |
|
|
722
|
+
|---|---|
|
|
723
|
+
| `<hab-chart>` / `<hab-feed>` | `<wick-chart>` / `<wick-feed>` |
|
|
724
|
+
| `hab:range`, `hab:select`, `hab:alert`, `hab:crosshair`, `hab:measure`, `hab:annotations` | `wick:*` of the same name (both fire during 1.x) |
|
|
725
|
+
| `hab-feed:status` / `hab-feed:fallback` | `wick-feed:status` / `wick-feed:fallback` (both fire during 1.x) |
|
|
726
|
+
| `--hab-bg`, `--hab-up`, … | `--wick-*` of the same name (`--wick-*` wins; `--hab-*` is the fallback) |
|
|
727
|
+
| `HabChart` / `HabFeed` classes | `WickChart` / `WickFeed` (also as named exports) |
|
|
728
|
+
| HabScript (the `expr:{…}` language) | WickScript — syntax unchanged |
|
|
729
|
+
| `import … from 'wickchart/src/hab-chart.js'` | use the package entry points (`wickchart`, `wickchart/core`, `wickchart/feed`) — module files are renamed |
|
|
730
|
+
|
|
731
|
+
Two behavioral notes: custom indicators registered via
|
|
732
|
+
`WickChart.registerIndicator()` are shared with the legacy `<hab-chart>`
|
|
733
|
+
alias (one registry), and cross-tab co-view channels are now prefixed
|
|
734
|
+
`wick-co-view:` (a 0.x tab and a 1.x tab won't pair — refresh both).
|
|
735
|
+
|
|
510
736
|
## License
|
|
511
737
|
|
|
512
738
|
MIT
|
package/package.json
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wickchart",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "<
|
|
3
|
+
"version": "1.2.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/
|
|
7
|
-
"module": "src/
|
|
8
|
-
"types": "types/
|
|
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/
|
|
12
|
-
"default": "./src/
|
|
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/
|
|
20
|
-
"default": "./src/
|
|
19
|
+
"types": "./types/wick-feed.d.ts",
|
|
20
|
+
"default": "./src/wick-feed.js"
|
|
21
|
+
},
|
|
22
|
+
"./react": {
|
|
23
|
+
"types": "./types/react.d.ts",
|
|
24
|
+
"default": "./src/react.js"
|
|
21
25
|
}
|
|
22
26
|
},
|
|
23
27
|
"files": [
|
|
@@ -25,14 +29,23 @@
|
|
|
25
29
|
"types"
|
|
26
30
|
],
|
|
27
31
|
"sideEffects": [
|
|
28
|
-
"src/
|
|
32
|
+
"src/wick-chart.js",
|
|
33
|
+
"src/react.js"
|
|
29
34
|
],
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"react": ">=16.8"
|
|
37
|
+
},
|
|
38
|
+
"peerDependenciesMeta": {
|
|
39
|
+
"react": {
|
|
40
|
+
"optional": true
|
|
41
|
+
}
|
|
42
|
+
},
|
|
30
43
|
"scripts": {
|
|
31
44
|
"dev": "npx --yes serve . -l 5173",
|
|
32
45
|
"test": "node --test \"tests/*.test.mjs\"",
|
|
33
|
-
"build:types": "tsc -p tsconfig.json",
|
|
46
|
+
"build:types": "node -e \"require('fs').rmSync('types', { recursive: true, force: true });\" && tsc -p tsconfig.json",
|
|
34
47
|
"prepack": "npm run build:types",
|
|
35
|
-
"ci": "npm run build:types && npm test && node --check src/
|
|
48
|
+
"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 src/react.js && node --check src/react-core.js && node --check demo/app.js"
|
|
36
49
|
},
|
|
37
50
|
"keywords": [
|
|
38
51
|
"chart",
|
|
@@ -52,6 +65,10 @@
|
|
|
52
65
|
"url": "git+https://github.com/benyblack/wickchart.git"
|
|
53
66
|
},
|
|
54
67
|
"devDependencies": {
|
|
68
|
+
"@types/react": "^19.1.0",
|
|
69
|
+
"jsdom": "^26.1.0",
|
|
70
|
+
"react": "^19.1.0",
|
|
71
|
+
"react-dom": "^19.1.0",
|
|
55
72
|
"typescript": "^7.0.2"
|
|
56
73
|
}
|
|
57
74
|
}
|