wickchart 0.3.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 +404 -0
- package/package.json +57 -0
- package/src/core.js +1055 -0
- package/src/feeds.js +192 -0
- package/src/hab-chart.js +2621 -0
- package/src/hab-feed.js +255 -0
- package/types/core.d.ts +572 -0
- package/types/feeds.d.ts +58 -0
- package/types/hab-chart.d.ts +377 -0
- package/types/hab-feed.d.ts +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
# HabView
|
|
2
|
+
|
|
3
|
+
**`<hab-chart>` — a modern, simpler, more useful charting web component.**
|
|
4
|
+
|
|
5
|
+
A TradingView-style financial chart as a single framework-agnostic Web Component.
|
|
6
|
+
One file, zero dependencies, one HTML tag. Canvas-rendered, fast, themeable, and
|
|
7
|
+
streaming-ready.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install wickchart
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
// any bundler / framework — TypeScript types included
|
|
17
|
+
import 'wickchart'; // registers <hab-chart>
|
|
18
|
+
import HabChart from 'wickchart'; // for HabChart.registerIndicator(...)
|
|
19
|
+
import { encodeStateQuery } from 'wickchart/core'; // pure helpers
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or straight from a CDN — no install, no build:
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<script type="module" src="https://unpkg.com/wickchart"></script>
|
|
26
|
+
|
|
27
|
+
<hab-chart label="BTC · 1h" type="candles" indicators="sma:20 volume"></hab-chart>
|
|
28
|
+
|
|
29
|
+
<script type="module">
|
|
30
|
+
const chart = document.querySelector('hab-chart');
|
|
31
|
+
chart.setData(bars); // [{ time, open, high, low, close, volume }]
|
|
32
|
+
chart.update(bar); // stream live updates
|
|
33
|
+
</script>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Works in plain HTML, React, Vue, Svelte, Angular — anywhere a `<div>` works.
|
|
37
|
+
TypeScript declarations ship inside the package (generated at pack time from
|
|
38
|
+
the JSDoc-annotated source — the repo itself stays 100% dependency-free JS).
|
|
39
|
+
|
|
40
|
+
## Declarative live charts with `<hab-feed>`
|
|
41
|
+
|
|
42
|
+
One more script tag and your chart is fully live — data, backfill, streaming —
|
|
43
|
+
with **zero JavaScript written**:
|
|
44
|
+
|
|
45
|
+
```html
|
|
46
|
+
<script type="module" src="https://unpkg.com/wickchart/feed"></script>
|
|
47
|
+
|
|
48
|
+
<hab-feed for="chart" binance="BTCUSDT" tf="1h"></hab-feed>
|
|
49
|
+
<hab-chart id="chart" indicators="sma:20 volume" profile></hab-chart>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
| Attribute | Meaning |
|
|
53
|
+
| ----------- | ----------------------------------------------------------------------- |
|
|
54
|
+
| `for` | target `<hab-chart>` id (auto-pairs with the first chart when omitted) |
|
|
55
|
+
| `binance` | Binance symbol (`BTCUSDT`) — REST load + WebSocket live + backfill |
|
|
56
|
+
| `demo` | deterministic offline synthetic feed (`demo="ETH"` picks a base price) |
|
|
57
|
+
| `url` | generic REST endpoint returning a JSON array of bars (+ `poll="10"` sec) |
|
|
58
|
+
| `tf` | timeframe: `1m 3m 5m 15m 30m 1h 2h 4h 6h 12h 1d 3d 1w` |
|
|
59
|
+
| `limit` | initial bars (default 500) |
|
|
60
|
+
| `live` | `live="false"` loads history without streaming |
|
|
61
|
+
|
|
62
|
+
The element reflects its state in the `status` attribute (`loading`, `live`,
|
|
63
|
+
`polling`, `fallback`, `loaded`, `waiting`, `idle`) and emits
|
|
64
|
+
`hab-feed:status` / `hab-feed:fallback` events. When Binance is unreachable
|
|
65
|
+
(geo-blocked, offline), it degrades gracefully: WebSocket → REST polling → a
|
|
66
|
+
synthetic stream bridged from the last real price, so the chart never goes
|
|
67
|
+
blank. It also wires `chart.onloadmore` for infinite backfill automatically.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Why another chart library?
|
|
72
|
+
|
|
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
|
|
75
|
+
bet:
|
|
76
|
+
|
|
77
|
+
- **Zero dependencies, single file** (~40 KB unminified, no build step required)
|
|
78
|
+
- **One tag, sane defaults** — drop it in and it renders; everything optional
|
|
79
|
+
- **Built-in usefulness** — crosshair + OHLC legend, last-price line, wheel zoom,
|
|
80
|
+
drag pan, pinch, keyboard navigation, live streaming, PNG export
|
|
81
|
+
- **Themeable with CSS variables** — two built-in themes, full control from
|
|
82
|
+
outside the component (Shadow DOM friendly)
|
|
83
|
+
- **Accessible** — focusable, arrow-key crosshair, ARIA summary of the data
|
|
84
|
+
|
|
85
|
+
## Run the demo
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npm run dev # serves on http://localhost:5173
|
|
89
|
+
# or: npx serve . -l 5173
|
|
90
|
+
# or: python -m http.server 5173
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Then open **http://localhost:5173/demo/**.
|
|
94
|
+
|
|
95
|
+
The demo ships with an offline synthetic feed (random walk with volatility
|
|
96
|
+
regimes + live ticking), and optionally loads **real Binance data** (REST +
|
|
97
|
+
WebSocket) for BTC/ETH/SOL when the API is reachable from your network —
|
|
98
|
+
with graceful fallback to synthetic data if it isn't.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Data format
|
|
103
|
+
|
|
104
|
+
Bars are plain objects; `time` accepts **milliseconds or seconds** (auto-detected).
|
|
105
|
+
For line-style data you can pass `{ time, value }` instead of full OHLCV.
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
chart.setData([
|
|
109
|
+
{ time: 1694000000000, open: 100.5, high: 101.2, low: 99.8, close: 100.9, volume: 1200 },
|
|
110
|
+
// ...
|
|
111
|
+
]);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Attributes
|
|
115
|
+
|
|
116
|
+
| Attribute | Default | Description |
|
|
117
|
+
| ------------- | ---------- | ------------------------------------------------------------------ |
|
|
118
|
+
| `theme` | `dark` | `dark` or `light` |
|
|
119
|
+
| `type` | `candles` | `candles`, `line`, `area`, `bars` (OHLC), `hollow` (hollow up-candles), `heikin` (Heikin-Ashi) |
|
|
120
|
+
| `indicators` | `volume`* | Space/comma-separated: `sma:20`, `ema:50`, `bb:20`, `rsi:14`, `macd:12/26/9`, `volume`, or any registered indicator |
|
|
121
|
+
| `label` | – | Text shown in the legend (e.g. `"BTC · 1h"`) |
|
|
122
|
+
| `log` | off | Logarithmic price scale |
|
|
123
|
+
| `auto` | on | Keep the right edge pinned to the latest bar while streaming |
|
|
124
|
+
| `precision` | auto | Forced decimal places for prices (auto-detected from magnitude) |
|
|
125
|
+
| `stats` | off | Live statistics chip for the visible range |
|
|
126
|
+
| `profile` | off | Volume profile overlay (POC + 70% value area) |
|
|
127
|
+
| `annotations` | off | Smart annotations (volume spikes, gaps, pivots, RSI divergences) |
|
|
128
|
+
|
|
129
|
+
\* `indicators=""` disables everything, including volume. Token syntax:
|
|
130
|
+
`name[:param[/param…]][@color]` — e.g. `sma:20@#ff0000`, `macd:12/26/9`.
|
|
131
|
+
|
|
132
|
+
### Built-in indicators
|
|
133
|
+
|
|
134
|
+
| Name | Kind | Params | Notes |
|
|
135
|
+
|---|---|---|---|
|
|
136
|
+
| `sma` | overlay | `period` (20) | |
|
|
137
|
+
| `ema` | overlay | `period` (50) | |
|
|
138
|
+
| `bb` | overlay | `period`, `mult` (20, 2) | Bollinger bands (3 lines) |
|
|
139
|
+
| `rsi` | pane | `period` (14) | fixed 0–100 scale, 30/70 guides |
|
|
140
|
+
| `macd` | pane | `fast/slow/signal` (12/26/9) | 2 lines + histogram |
|
|
141
|
+
| `volume` | overlay | – | histogram at the bottom of the price pane |
|
|
142
|
+
|
|
143
|
+
### Custom indicators
|
|
144
|
+
|
|
145
|
+
Register your own — anything from a one-liner moving average to a multi-line
|
|
146
|
+
pane:
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
HabChart.registerIndicator('vwap', {
|
|
150
|
+
kind: 'overlay', // or 'pane'
|
|
151
|
+
params: { period: 20 }, // defaults; set via indicators="vwap:30"
|
|
152
|
+
compute(bars, params) { // bars: normalized {time,open,high,low,close,volume}
|
|
153
|
+
const out = new Array(bars.length).fill(null);
|
|
154
|
+
let pv = 0, vv = 0;
|
|
155
|
+
for (let i = 0; i < bars.length; i++) {
|
|
156
|
+
pv += bars[i].close * bars[i].volume;
|
|
157
|
+
vv += bars[i].volume;
|
|
158
|
+
out[i] = vv ? pv / vv : null;
|
|
159
|
+
}
|
|
160
|
+
return out; // single series — or { lines:[{name,values}], histogram }
|
|
161
|
+
},
|
|
162
|
+
// pane-only extras: guides:[30,70], range:[0,100], fmt:'price'|'fixed1'
|
|
163
|
+
});
|
|
164
|
+
chart.indicators = 'vwap:20';
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`import HabChart from 'wickchart'` gives you the class for
|
|
168
|
+
`HabChart.registerIndicator(...)` (the element is registered as a side effect
|
|
169
|
+
of importing the package).
|
|
170
|
+
|
|
171
|
+
### Sonification — the chart by ear
|
|
172
|
+
|
|
173
|
+
`<hab-chart sonify>` maps price to pitch (180–880 Hz across the visible
|
|
174
|
+
scale, log-aware): moving the crosshair with the mouse or **arrow keys** plays
|
|
175
|
+
a short tone per bar, so trend and shape are audible — a rare accessibility
|
|
176
|
+
win for screen-reader users. `chart.playRange()` sweeps the whole visible
|
|
177
|
+
range as a ~4-second pitch sequence, riding the crosshair along for sighted
|
|
178
|
+
users. Audio starts lazily within the enabling user gesture (autoplay-policy
|
|
179
|
+
safe).
|
|
180
|
+
|
|
181
|
+
### Cross-tab co-view
|
|
182
|
+
|
|
183
|
+
Tag charts with the same channel and they share pointers — across browser
|
|
184
|
+
tabs, or between multiple charts on one page:
|
|
185
|
+
|
|
186
|
+
```html
|
|
187
|
+
<hab-chart co-view="btc-room"></hab-chart>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Hovering in one tab draws a ghost crosshair (accent, dotted, with the time
|
|
191
|
+
pill) in every peer. Positions are synced by bar **time**, so peers with
|
|
192
|
+
different history depths still line up. Ghosts fade ~2.5 s after the peer
|
|
193
|
+
stops moving. Same-origin only (BroadcastChannel); the connection follows the
|
|
194
|
+
`co-view` attribute and closes with the element.
|
|
195
|
+
|
|
196
|
+
### Smart annotations
|
|
197
|
+
|
|
198
|
+
`<hab-chart annotations>` marks notable events on the visible range — volume
|
|
199
|
+
spikes (>3× average), price gaps, 41-bar pivot highs/lows, and RSI
|
|
200
|
+
divergences — with lettered badges (V/G/H/L/D). Hover a badged bar and the
|
|
201
|
+
legend shows a one-line insight ("Volume 4.2× average", "Bearish RSI
|
|
202
|
+
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
|
|
204
|
+
hidden at extreme zoom-out, where bars collapse into columns.
|
|
205
|
+
|
|
206
|
+
### Example: VWAP via the registry
|
|
207
|
+
|
|
208
|
+
VWAP ships in the demo but *not* as a builtin — it's the reference for writing
|
|
209
|
+
your own (session-anchored, resets each trading day):
|
|
210
|
+
|
|
211
|
+
```js
|
|
212
|
+
import HabChart from 'wickchart';
|
|
213
|
+
|
|
214
|
+
HabChart.registerIndicator('vwap', {
|
|
215
|
+
kind: 'overlay',
|
|
216
|
+
params: {},
|
|
217
|
+
compute(bars) {
|
|
218
|
+
const out = new Array(bars.length).fill(null);
|
|
219
|
+
let pv = 0, vv = 0, day = -1;
|
|
220
|
+
for (let i = 0; i < bars.length; i++) {
|
|
221
|
+
const b = bars[i];
|
|
222
|
+
const d = new Date(b.time).setHours(0, 0, 0, 0);
|
|
223
|
+
if (d !== day) { day = d; pv = 0; vv = 0; }
|
|
224
|
+
const tp = (b.high + b.low + b.close) / 3;
|
|
225
|
+
pv += tp * b.volume;
|
|
226
|
+
vv += b.volume;
|
|
227
|
+
out[i] = vv ? pv / vv : null;
|
|
228
|
+
}
|
|
229
|
+
return out;
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
chart.indicators = 'vwap';
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Methods
|
|
236
|
+
|
|
237
|
+
| Method | Description |
|
|
238
|
+
| ------------------------------- | ------------------------------------------------ |
|
|
239
|
+
| `setData(bars)` | Replace the dataset (sorted automatically) |
|
|
240
|
+
| `update(bar)` | Stream: replaces last bar or appends a new one |
|
|
241
|
+
| `clearData()` | Empty the chart |
|
|
242
|
+
| `fit()` | Reset zoom to the default view (~150 bars) |
|
|
243
|
+
| `getVisibleRange()` | → `{ from, to }` (ms timestamps) |
|
|
244
|
+
| `setVisibleRange({from, to})` | Jump to a time window |
|
|
245
|
+
| `exportPNG()` | → PNG data URL of the current canvas |
|
|
246
|
+
| `getState()` | → serializable snapshot (type, indicators, view, positions, alerts) |
|
|
247
|
+
| `setState(state)` | Apply a snapshot; a pending view applies after the next `setData()` |
|
|
248
|
+
|
|
249
|
+
### Infinite history (`loadMore`)
|
|
250
|
+
|
|
251
|
+
Assign a callback and the chart fetches older bars whenever the user scrolls
|
|
252
|
+
toward the left edge — the view stays anchored while data is prepended:
|
|
253
|
+
|
|
254
|
+
```js
|
|
255
|
+
chart.onloadmore = async (fromTime) => {
|
|
256
|
+
const res = await fetch(`/api/bars?before=${fromTime}&limit=500`);
|
|
257
|
+
return res.json(); // [{ time, open, high, low, close, volume }, …]
|
|
258
|
+
};
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Return an empty array (or throw) when history is exhausted and the chart stops
|
|
262
|
+
asking. Data gaps (weekends, session breaks) are marked with subtle dashed
|
|
263
|
+
dividers on the time axis.
|
|
264
|
+
|
|
265
|
+
### Positions & alerts
|
|
266
|
+
|
|
267
|
+
Visualize trades directly on the chart — entry/stop/target zones, a live P&L
|
|
268
|
+
chip, and price alerts that fire during streaming updates:
|
|
269
|
+
|
|
270
|
+
```js
|
|
271
|
+
chart.addPosition({ side: 'long', entry: 64200, stop: 62900, target: 66800, qty: 0.5 });
|
|
272
|
+
chart.addPosition({ id: 'x1', side: 'short', entry: 66000, qty: 1 });
|
|
273
|
+
chart.removePosition('x1');
|
|
274
|
+
|
|
275
|
+
chart.addAlert({ price: 65000, direction: 'above' }); // 'above' | 'below' | 'cross'
|
|
276
|
+
chart.addEventListener('hab:alert', (e) => {
|
|
277
|
+
console.log('crossed!', e.detail.id, e.detail.price);
|
|
278
|
+
});
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
The P&L chip recalculates on every streamed bar. Alerts are edge-triggered
|
|
282
|
+
(fire once per crossing) and one-shot by default (`once: false` to re-arm).
|
|
283
|
+
|
|
284
|
+
### Stats & measure
|
|
285
|
+
|
|
286
|
+
`<hab-chart stats>` shows live statistics of the visible range — return %,
|
|
287
|
+
max drawdown, annualized volatility, up/down bar counts, average volume —
|
|
288
|
+
recalculated as you pan and zoom.
|
|
289
|
+
|
|
290
|
+
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
|
|
292
|
+
release (`detail.from` / `detail.to` carry index, time and price). Click or
|
|
293
|
+
press `Esc` to clear.
|
|
294
|
+
|
|
295
|
+
### Shareable URLs
|
|
296
|
+
|
|
297
|
+
`getState()` / `setState()` serialize everything about the chart, and
|
|
298
|
+
`encodeStateQuery` / `decodeStateQuery` (exported from `src/core.js`) turn a
|
|
299
|
+
state into a compact query string — the demo maps it to the page hash, so any
|
|
300
|
+
chart configuration is one link away:
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
import { encodeStateQuery, decodeStateQuery } from 'wickchart/core';
|
|
304
|
+
|
|
305
|
+
const link = `${location.origin}#${encodeStateQuery(chart.getState())}`;
|
|
306
|
+
history.replaceState(null, '', link);
|
|
307
|
+
// later, on load:
|
|
308
|
+
chart.setState(decodeStateQuery(location.hash.slice(1)));
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Reflected properties (`chart.type = 'line'`) work for `theme`, `type`, `label`,
|
|
312
|
+
`indicators`.
|
|
313
|
+
|
|
314
|
+
## Events
|
|
315
|
+
|
|
316
|
+
| Event | Detail |
|
|
317
|
+
| --------------- | ---------------------------------------------------------- |
|
|
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) |
|
|
321
|
+
|
|
322
|
+
## Theming
|
|
323
|
+
|
|
324
|
+
All colors are CSS custom properties settable on the element (they pierce the
|
|
325
|
+
Shadow DOM):
|
|
326
|
+
|
|
327
|
+
```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 */
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Interactions
|
|
344
|
+
|
|
345
|
+
| Gesture | Action |
|
|
346
|
+
| -------------------------- | ----------------------------------- |
|
|
347
|
+
| Mouse wheel / trackpad ⌘+scroll | Zoom, anchored at the cursor |
|
|
348
|
+
| Trackpad horizontal scroll | Pan |
|
|
349
|
+
| Drag | Pan (auto-follow re-arms at the right edge) |
|
|
350
|
+
| Pinch (touch) | Zoom |
|
|
351
|
+
| Double-click | Reset view |
|
|
352
|
+
| `←` `→` (`+Shift` ×10) | Move crosshair |
|
|
353
|
+
| `+` / `−` | Zoom in / out |
|
|
354
|
+
| `Home` / `End` | Jump to oldest / newest |
|
|
355
|
+
| `Esc` | Clear crosshair |
|
|
356
|
+
|
|
357
|
+
## Performance
|
|
358
|
+
|
|
359
|
+
Canvas 2D with a rAF-batched, visible-range-only render pipeline. Measured on a
|
|
360
|
+
desktop (Chromium, 1100×760, all indicators on: SMA + EMA + RSI + volume):
|
|
361
|
+
|
|
362
|
+
| Scenario | Per full render |
|
|
363
|
+
| ----------------------------------------- | --------------- |
|
|
364
|
+
| 600–50,000 bars, default view (~150 visible) | **~0.2 ms** |
|
|
365
|
+
| 5,000 bars, max zoom-out (~2,900 visible) | ~6 ms |
|
|
366
|
+
| 50,000 bars, max zoom-out (~3,100 visible) | ~16 ms |
|
|
367
|
+
| Streaming tick (update + full re-render) | 0.5–19 ms |
|
|
368
|
+
|
|
369
|
+
A 60 fps frame budget is 16.7 ms, so the default view uses ~1% of a frame.
|
|
370
|
+
Hot paths are deliberately allocation-light: date labels are built lazily only
|
|
371
|
+
for actual axis ticks (with cached `Intl.DateTimeFormat`s), and candles/volume
|
|
372
|
+
are drawn in two batched passes by direction instead of one draw call per bar.
|
|
373
|
+
|
|
374
|
+
**Deep zoom-outs are columnar**: when more bars are visible than ~1.5× the
|
|
375
|
+
pixel width, bars aggregate into per-pixel min/max columns (first open / max
|
|
376
|
+
high / min low / last close / summed volume), so rendering any history at any
|
|
377
|
+
zoom costs O(screen width), not O(bars). The minimum zoom level adapts to the
|
|
378
|
+
dataset — every chart can be zoomed out until the entire history fits.
|
|
379
|
+
|
|
380
|
+
If you ever push past this (100k+ simultaneously visible bars, dozens of
|
|
381
|
+
series, high-frequency ticks), the scaling levers are: incremental indicator
|
|
382
|
+
updates (SMA/EMA/RSI are O(1) online), min/max columnar downsampling per pixel
|
|
383
|
+
column, and an offscreen layer so hover only repaints the crosshair.
|
|
384
|
+
|
|
385
|
+
## Architecture notes
|
|
386
|
+
|
|
387
|
+
- Single ES module, Custom Element + Shadow DOM, Canvas 2D with
|
|
388
|
+
devicePixelRatio scaling and rAF-batched invalidation
|
|
389
|
+
- Only visible bars are drawn; indicator series are computed lazily and cached
|
|
390
|
+
per data version (prefix-sum SMA, Wilder RSI)
|
|
391
|
+
- Time axis picks tick steps from bar interval (minutes → months) and labels
|
|
392
|
+
day/month boundaries like a pro terminal
|
|
393
|
+
- No dependencies, no build step required — but it bundles/tree-shakes fine
|
|
394
|
+
|
|
395
|
+
## Roadmap ideas
|
|
396
|
+
|
|
397
|
+
- More overlays (Bollinger, VWAP), MACD pane, drawing tools
|
|
398
|
+
- Data callbacks (`loadMore` for infinite history)
|
|
399
|
+
- Incremental (O(1)) indicator updates for high-frequency streaming
|
|
400
|
+
- Min/max downsampling and/or an offscreen hover layer if profiling ever demands
|
|
401
|
+
|
|
402
|
+
## License
|
|
403
|
+
|
|
404
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
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.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/hab-chart.js",
|
|
7
|
+
"module": "src/hab-chart.js",
|
|
8
|
+
"types": "types/hab-chart.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./types/hab-chart.d.ts",
|
|
12
|
+
"default": "./src/hab-chart.js"
|
|
13
|
+
},
|
|
14
|
+
"./core": {
|
|
15
|
+
"types": "./types/core.d.ts",
|
|
16
|
+
"default": "./src/core.js"
|
|
17
|
+
},
|
|
18
|
+
"./feed": {
|
|
19
|
+
"types": "./types/hab-feed.d.ts",
|
|
20
|
+
"default": "./src/hab-feed.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"src",
|
|
25
|
+
"types"
|
|
26
|
+
],
|
|
27
|
+
"sideEffects": [
|
|
28
|
+
"src/hab-chart.js"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"dev": "npx --yes serve . -l 5173",
|
|
32
|
+
"test": "node --test \"tests/*.test.mjs\"",
|
|
33
|
+
"build:types": "tsc -p tsconfig.json",
|
|
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"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"chart",
|
|
39
|
+
"charting",
|
|
40
|
+
"candlestick",
|
|
41
|
+
"trading",
|
|
42
|
+
"finance",
|
|
43
|
+
"web-component",
|
|
44
|
+
"custom-element",
|
|
45
|
+
"canvas",
|
|
46
|
+
"zero-dependency",
|
|
47
|
+
"tradingview"
|
|
48
|
+
],
|
|
49
|
+
"license": "MIT",
|
|
50
|
+
"repository": {
|
|
51
|
+
"type": "git",
|
|
52
|
+
"url": "git+https://github.com/benyblack/wickchart.git"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"typescript": "^7.0.2"
|
|
56
|
+
}
|
|
57
|
+
}
|