wick-charts 0.4.0 → 0.5.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 +59 -20
- package/dist/index.d.ts +15 -2
- package/dist/index.js +17 -6
- package/dist/renderer.js +15 -1
- package/dist/series/line.d.ts +14 -0
- package/dist/series/line.js +65 -0
- package/dist/types.d.ts +12 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ npm install wick-charts
|
|
|
17
17
|
- [Install](#install)
|
|
18
18
|
- [Quick start](#quick-start)
|
|
19
19
|
- [Candle data](#candle-data)
|
|
20
|
+
- [Line charts](#line-charts)
|
|
20
21
|
- [Styling](#styling)
|
|
21
22
|
- [Reading chart state](#reading-chart-state)
|
|
22
23
|
- [Loading more history on demand](#loading-more-history-on-demand)
|
|
@@ -136,6 +137,39 @@ it — a dataset with no `volume` at all renders exactly as if the feature didn'
|
|
|
136
137
|
array) is safe. It resets pan/zoom/hover state — call it for a genuinely new dataset, and use
|
|
137
138
|
`setDataLoader()` (below) to extend the current one instead.
|
|
138
139
|
|
|
140
|
+
### Line charts
|
|
141
|
+
|
|
142
|
+
For a plain time series with no OHLC shape — an equity curve, a metric over time, anything
|
|
143
|
+
that's just one number per point — `createLineChart` is the line-series equivalent of
|
|
144
|
+
`createCandlestickChart` above. A line point is `{ time, value }`:
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import { createLineChart } from 'wick-charts';
|
|
148
|
+
|
|
149
|
+
const chart = createLineChart(canvas, {
|
|
150
|
+
style: { lineColor: '#2196f3', lineWidth: 1.5 }, // both shown here are the defaults
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
chart.setData([
|
|
154
|
+
{ time: '2024-01-01T00:00:00Z', value: 100 },
|
|
155
|
+
{ time: '2024-01-02T00:00:00Z', value: 103.4 },
|
|
156
|
+
{ time: '2024-01-03T00:00:00Z', value: 101.8 },
|
|
157
|
+
// ...
|
|
158
|
+
]);
|
|
159
|
+
|
|
160
|
+
chart.render();
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Everything else — pan/zoom/hover, `setDataLoader`, `addPlugin`, `addPane`, `background`/
|
|
164
|
+
`font`/`axis`/`crosshair`/`legend` styling — works exactly as it does for a candlestick chart,
|
|
165
|
+
since none of it is specific to what's actually plotted (see "Series types" under
|
|
166
|
+
Architecture). `value` accepts `NaN` (or any non-finite number) as an explicit gap: the line
|
|
167
|
+
breaks there and resumes at the next real value, rather than plotting a bogus point or
|
|
168
|
+
throwing — useful for a series with missing data at some points without pre-filtering it
|
|
169
|
+
yourself. The hover legend shows `Value <number>` (or `Value —` for a hovered gap) in place of
|
|
170
|
+
candlestick's OHLC breakdown; `new WickChart(canvas, { type: 'line' })` also works, the same
|
|
171
|
+
untyped escape hatch `type: 'candlestick'` has, if you'd rather not import the factory.
|
|
172
|
+
|
|
139
173
|
### Styling
|
|
140
174
|
|
|
141
175
|
Every visual aspect of the chart is an option — nothing is a fixed constant you can't reach.
|
|
@@ -455,31 +489,35 @@ for *this* series (a line series wouldn't have a body width or volume bars to co
|
|
|
455
489
|
|
|
456
490
|
### Series types
|
|
457
491
|
|
|
458
|
-
|
|
459
|
-
`WickChart` and `ChartRenderer` are generic over a point shape
|
|
460
|
-
`time`) and delegate every type-specific decision — how to compute
|
|
461
|
-
how to draw the visible points, what a hover legend says — to a
|
|
492
|
+
Candlestick and line are the two chart types today, and nothing above `src/series/` treats
|
|
493
|
+
either specially. `WickChart` and `ChartRenderer` are generic over a point shape
|
|
494
|
+
(`SeriesPoint` — just a `time`) and delegate every type-specific decision — how to compute
|
|
495
|
+
the value-axis range, how to draw the visible points, what a hover legend says — to a
|
|
462
496
|
`SeriesDefinition` (see `src/series/types.ts`) resolved at construction time from
|
|
463
497
|
`options.type` via a small registry (`src/series/registry.ts`). `src/series/candlestick.ts`
|
|
464
|
-
|
|
465
|
-
why importing `wick-charts` at all is enough to make
|
|
466
|
-
registering anything.
|
|
467
|
-
|
|
468
|
-
Adding a
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
498
|
+
and `src/series/line.ts` both register themselves (as `'candlestick'`/`'line'`) on import,
|
|
499
|
+
which is why importing `wick-charts` at all is enough to make either type available without
|
|
500
|
+
the caller registering anything.
|
|
501
|
+
|
|
502
|
+
Adding a chart type (area, bar, ...) means writing one new file that implements
|
|
503
|
+
`SeriesDefinition<TPoint, TStyle>` and calling `registerSeries` on it — `Viewport`, event
|
|
504
|
+
handling, data loading, and WASM scale dispatch are all untouched, and every existing chart
|
|
505
|
+
of another type keeps working exactly as before; `src/series/line.ts` is a second, smaller
|
|
506
|
+
worked example of this alongside candlestick's own. This is the extension point the `type`
|
|
507
|
+
option and `style` option are built around: `style` is whatever shape the chosen series's
|
|
508
|
+
`defaultStyle` declares (candlestick's is `{ upColor, downColor, ... }`, line's is `{
|
|
509
|
+
lineColor, lineWidth }`), merged over that default rather than hardcoded into the chart
|
|
510
|
+
itself.
|
|
475
511
|
|
|
476
512
|
`options.type` is a plain string the registry resolves at runtime, so `new WickChart(canvas,
|
|
477
513
|
{ type: 'candlestick', style: {...} })` type-checks even if `style` has nothing to do with
|
|
478
514
|
`CandlestickStyle` — nothing ties a runtime string to a specific `TPoint`/`TStyle` pair at the
|
|
479
|
-
type level. `createCandlestickChart()` (in `src/index.ts`)
|
|
480
|
-
|
|
481
|
-
should export an equivalent `create<Name>Chart` next
|
|
482
|
-
`WickChartOptions` itself, so each series's style shape stays
|
|
515
|
+
type level. `createCandlestickChart()`/`createLineChart()` (both in `src/index.ts`) are the
|
|
516
|
+
fix for the two built-in types: a thin wrapper per series that pins both generics so its
|
|
517
|
+
`style` is fully checked. A new series should export an equivalent `create<Name>Chart` next
|
|
518
|
+
to it rather than widening `WickChartOptions` itself, so each series's style shape stays
|
|
519
|
+
independent of every other's — line's factory is the second proof this pattern holds up, not
|
|
520
|
+
just a one-off written for candlestick.
|
|
483
521
|
|
|
484
522
|
### Plugins (markers, annotations, drawing tools)
|
|
485
523
|
|
|
@@ -598,7 +636,8 @@ hover state — and on-demand history loading via `setDataLoader`. Per-candle vo
|
|
|
598
636
|
the bottom fifth of the chart when a candle has `volume`, and are entirely omitted (nothing
|
|
599
637
|
drawn, nothing reserved) for data that doesn't.
|
|
600
638
|
Coordinate scaling runs on WASM once a frame's point count crosses the threshold, JS below
|
|
601
|
-
it. Candlestick
|
|
639
|
+
it. Candlestick and line are the two registered series types so far (`createCandlestickChart`/
|
|
640
|
+
`createLineChart`); the plugin extension point (draw
|
|
602
641
|
overlays plus, now, claimable pointer gestures for interactive tools — see "Plugins" above)
|
|
603
642
|
has no built-in users (see "Indicators" above for why) beyond `demo/index.html`'s example. No
|
|
604
643
|
concrete drawing tool ships yet, only the mechanism a trend line or similar would be built
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { DataLoader } from './dataSource.js';
|
|
2
2
|
import type { ChartPlugin } from './plugins/types.js';
|
|
3
3
|
import type { CandlestickStyle } from './series/candlestick.js';
|
|
4
|
-
import type {
|
|
5
|
-
|
|
4
|
+
import type { LineStyle } from './series/line.js';
|
|
5
|
+
import type { Candle, LinePoint, PaneOptions, WickChartOptions, SeriesPoint, ValueRange } from './types.js';
|
|
6
|
+
export type { BusinessDay, Candle, LinePoint, PaneOptions, WickChartOptions, WickTime, SeriesPoint, UnixMillis, ValueRange, } from './types.js';
|
|
6
7
|
export type { DataLoader, DataRequest } from './dataSource.js';
|
|
7
8
|
export type { ChartPlugin, ChartPointerEvent, PluginRenderApi } from './plugins/types.js';
|
|
8
9
|
export { distanceToSegment, hitTestPoint, hitTestSegment } from './hitTest.js';
|
|
@@ -11,7 +12,9 @@ export { mergeSeriesPoints } from './mergeSeries.js';
|
|
|
11
12
|
export { registerSeries, getSeries } from './series/registry.js';
|
|
12
13
|
export type { SeriesDefinition, SeriesDrawContext } from './series/types.js';
|
|
13
14
|
export type { CandlestickStyle } from './series/candlestick.js';
|
|
15
|
+
export type { LineStyle } from './series/line.js';
|
|
14
16
|
export { candlestickSeries } from './series/candlestick.js';
|
|
17
|
+
export { lineSeries } from './series/line.js';
|
|
15
18
|
export { LinearScale } from './scale.js';
|
|
16
19
|
export { toUnixSeconds } from './time.js';
|
|
17
20
|
export { Viewport } from './viewport.js';
|
|
@@ -268,3 +271,13 @@ export declare class WickChart<TPoint extends SeriesPoint = Candle> {
|
|
|
268
271
|
export declare function createCandlestickChart(canvas: HTMLCanvasElement, options?: Omit<WickChartOptions, 'type' | 'style'> & {
|
|
269
272
|
style?: Partial<CandlestickStyle>;
|
|
270
273
|
}): WickChart<Candle>;
|
|
274
|
+
/**
|
|
275
|
+
* The second series type's equivalent of `createCandlestickChart` above —
|
|
276
|
+
* pins `TPoint` (`LinePoint`) and `TStyle` (`LineStyle`) so `style` is
|
|
277
|
+
* fully checked here the same way, rather than accepted as the untyped
|
|
278
|
+
* `Record<string, unknown>` `WickChartOptions.style` allows for `new
|
|
279
|
+
* WickChart(canvas, { type: 'line', style })`.
|
|
280
|
+
*/
|
|
281
|
+
export declare function createLineChart(canvas: HTMLCanvasElement, options?: Omit<WickChartOptions, 'type' | 'style'> & {
|
|
282
|
+
style?: Partial<LineStyle>;
|
|
283
|
+
}): WickChart<LinePoint>;
|
package/dist/index.js
CHANGED
|
@@ -9,13 +9,14 @@ import { importRealWasm } from './wasmImporter.js';
|
|
|
9
9
|
export { distanceToSegment, hitTestPoint, hitTestSegment } from './hitTest.js';
|
|
10
10
|
export { mergeSeriesPoints } from './mergeSeries.js';
|
|
11
11
|
export { registerSeries, getSeries } from './series/registry.js';
|
|
12
|
-
// Also registers
|
|
13
|
-
// src/series/candlestick.ts and src/series/registry.ts.
|
|
14
|
-
// gets the same treatment: implement SeriesDefinition,
|
|
15
|
-
// have the consuming app import it directly before
|
|
16
|
-
// that type), and `type: '<its key>'` becomes
|
|
17
|
-
// to this file.
|
|
12
|
+
// Also registers 'candlestick'/'line' as a module-load side effect — see
|
|
13
|
+
// src/series/candlestick.ts, src/series/line.ts, and src/series/registry.ts.
|
|
14
|
+
// A new series type gets the same treatment: implement SeriesDefinition,
|
|
15
|
+
// export it here (or have the consuming app import it directly before
|
|
16
|
+
// constructing a chart of that type), and `type: '<its key>'` becomes
|
|
17
|
+
// usable with no other change to this file.
|
|
18
18
|
export { candlestickSeries } from './series/candlestick.js';
|
|
19
|
+
export { lineSeries } from './series/line.js';
|
|
19
20
|
export { LinearScale } from './scale.js';
|
|
20
21
|
export { toUnixSeconds } from './time.js';
|
|
21
22
|
export { Viewport } from './viewport.js';
|
|
@@ -781,3 +782,13 @@ export class WickChart {
|
|
|
781
782
|
export function createCandlestickChart(canvas, options) {
|
|
782
783
|
return new WickChart(canvas, { ...options, type: 'candlestick' });
|
|
783
784
|
}
|
|
785
|
+
/**
|
|
786
|
+
* The second series type's equivalent of `createCandlestickChart` above —
|
|
787
|
+
* pins `TPoint` (`LinePoint`) and `TStyle` (`LineStyle`) so `style` is
|
|
788
|
+
* fully checked here the same way, rather than accepted as the untyped
|
|
789
|
+
* `Record<string, unknown>` `WickChartOptions.style` allows for `new
|
|
790
|
+
* WickChart(canvas, { type: 'line', style })`.
|
|
791
|
+
*/
|
|
792
|
+
export function createLineChart(canvas, options) {
|
|
793
|
+
return new WickChart(canvas, { ...options, type: 'line' });
|
|
794
|
+
}
|
package/dist/renderer.js
CHANGED
|
@@ -142,7 +142,21 @@ export class ChartRenderer {
|
|
|
142
142
|
// Exact inverse of xForIndex above — solving
|
|
143
143
|
// `x = (index - viewport.startIndex) * slotWidth + slotWidth / 2` for `index`.
|
|
144
144
|
const indexForX = (x) => viewport.startIndex + (x - slotWidth / 2) / slotWidth;
|
|
145
|
-
|
|
145
|
+
// save/restore isolates whatever canvas state a series's draw()
|
|
146
|
+
// touches (lineWidth, line dash, ...) from the axis/crosshair/plugin
|
|
147
|
+
// drawing that follows — the same isolation each plugin already gets
|
|
148
|
+
// around its own draw() call below. Without this, a property no
|
|
149
|
+
// series happened to set before (lineSeries.draw() is the first
|
|
150
|
+
// built-in one to set ctx.lineWidth) would silently leak into every
|
|
151
|
+
// subsequent stroke() this frame, including axis boundary lines,
|
|
152
|
+
// grid lines, and the crosshair.
|
|
153
|
+
ctx.save();
|
|
154
|
+
try {
|
|
155
|
+
seriesDefinition.draw({ ctx, visible, startIndex: startIdx, xForIndex, slotWidth, yScale, chartHeight }, style);
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
ctx.restore();
|
|
159
|
+
}
|
|
146
160
|
const priceStep = this.currentPriceStep(valueMin, valueMax);
|
|
147
161
|
this.renderPriceAxis(valueMin, valueMax, priceStep, yScale, chartWidth, chartHeight, mainRect.top);
|
|
148
162
|
for (const { rect, min, max, scale } of paneScales) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LinePoint } from '../types.js';
|
|
2
|
+
import type { SeriesDefinition } from './types.js';
|
|
3
|
+
/** The second built-in series type — a plain single-value line, the
|
|
4
|
+
* simplest possible `SeriesDefinition` beyond candlestick's OHLC. Exists as
|
|
5
|
+
* much to exercise the registry with a genuinely different `TPoint`/`TStyle`
|
|
6
|
+
* pair as to be useful on its own; see `src/series/candlestick.ts` for the
|
|
7
|
+
* reference implementation this mirrors. */
|
|
8
|
+
export interface LineStyle {
|
|
9
|
+
/** Stroke color. Defaults to `'#2196f3'`. */
|
|
10
|
+
lineColor: string;
|
|
11
|
+
/** Stroke width, in px. Defaults to 1.5. */
|
|
12
|
+
lineWidth: number;
|
|
13
|
+
}
|
|
14
|
+
export declare const lineSeries: SeriesDefinition<LinePoint, LineStyle>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { fitRange } from '../priceRange.js';
|
|
2
|
+
import { registerSeries } from './registry.js';
|
|
3
|
+
const DEFAULT_STYLE = {
|
|
4
|
+
lineColor: '#2196f3',
|
|
5
|
+
lineWidth: 1.5,
|
|
6
|
+
};
|
|
7
|
+
/** Falls back to a fixed `[0, 1]` range when every visible point is a gap
|
|
8
|
+
* (`NaN`/non-finite `value`) — `Math.min()`/`Math.max()` over an empty
|
|
9
|
+
* array are `Infinity`/`-Infinity`, which would otherwise feed `fitRange`
|
|
10
|
+
* a `NaN` midpoint. Candlestick never needs this: `Candle`'s OHLC fields
|
|
11
|
+
* aren't optional or gap-tolerant the way a line point's `value` is. */
|
|
12
|
+
function getValueRange(visible, scaleFactor) {
|
|
13
|
+
const values = visible.map((p) => p.value).filter((v) => Number.isFinite(v));
|
|
14
|
+
if (values.length === 0)
|
|
15
|
+
return { min: 0, max: 1 };
|
|
16
|
+
return fitRange(Math.min(...values), Math.max(...values), scaleFactor);
|
|
17
|
+
}
|
|
18
|
+
function draw(context, style) {
|
|
19
|
+
const { ctx, visible, startIndex, xForIndex, yScale } = context;
|
|
20
|
+
if (visible.length === 0)
|
|
21
|
+
return;
|
|
22
|
+
// Batched through mapMany (one call per array) rather than once per
|
|
23
|
+
// point in the loop below — same reasoning as candlestick's draw(): it's
|
|
24
|
+
// what lets the WASM path pay the JS<->WASM boundary cost once per frame.
|
|
25
|
+
// NaN values map to NaN here, harmlessly — skipped below rather than
|
|
26
|
+
// filtered out first, so `ys[i]` still lines up with `visible[i]`.
|
|
27
|
+
const ys = yScale.mapMany(visible.map((p) => p.value));
|
|
28
|
+
ctx.strokeStyle = style.lineColor;
|
|
29
|
+
ctx.lineWidth = style.lineWidth;
|
|
30
|
+
ctx.beginPath();
|
|
31
|
+
// `drawing` tracks whether the path is mid-segment — a non-finite value
|
|
32
|
+
// (a gap in the data) breaks it, and the line resumes fresh at the next
|
|
33
|
+
// real value rather than jumping straight across the gap.
|
|
34
|
+
let drawing = false;
|
|
35
|
+
visible.forEach((point, i) => {
|
|
36
|
+
if (!Number.isFinite(point.value)) {
|
|
37
|
+
drawing = false;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const x = xForIndex(startIndex + i);
|
|
41
|
+
const y = ys[i];
|
|
42
|
+
if (drawing) {
|
|
43
|
+
ctx.lineTo(x, y);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
ctx.moveTo(x, y);
|
|
47
|
+
drawing = true;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
ctx.stroke();
|
|
51
|
+
}
|
|
52
|
+
function formatLegend(point) {
|
|
53
|
+
return [`Value ${Number.isFinite(point.value) ? point.value.toLocaleString('en-US') : '—'}`];
|
|
54
|
+
}
|
|
55
|
+
export const lineSeries = {
|
|
56
|
+
type: 'line',
|
|
57
|
+
defaultStyle: DEFAULT_STYLE,
|
|
58
|
+
getValueRange,
|
|
59
|
+
draw,
|
|
60
|
+
formatLegend,
|
|
61
|
+
};
|
|
62
|
+
// Registered as a module-level side effect, same as candlestickSeries — see
|
|
63
|
+
// src/series/candlestick.ts for why. src/index.ts imports this file so
|
|
64
|
+
// 'line' is available the moment the package itself is imported.
|
|
65
|
+
registerSeries(lineSeries);
|
package/dist/types.d.ts
CHANGED
|
@@ -57,6 +57,18 @@ export interface Candle extends SeriesPoint {
|
|
|
57
57
|
close: number;
|
|
58
58
|
volume?: number;
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* A single value plotted against time — the point shape for the built-in
|
|
62
|
+
* `'line'` series (see `src/series/line.ts`), and the simplest possible
|
|
63
|
+
* instance of `SeriesPoint` beyond `Candle`: a plain time series with
|
|
64
|
+
* nothing OHLC-specific about it. `value` is `NaN`-tolerant: a `NaN`
|
|
65
|
+
* (or non-finite) value is treated as a gap — the line breaks there and
|
|
66
|
+
* resumes at the next real value, rather than plotting a bogus point or
|
|
67
|
+
* throwing.
|
|
68
|
+
*/
|
|
69
|
+
export interface LinePoint extends SeriesPoint {
|
|
70
|
+
value: number;
|
|
71
|
+
}
|
|
60
72
|
/** Text styling shared by every label the chart draws — axis ticks,
|
|
61
73
|
* crosshair axis labels, and the hover legend. `axisSize`/`legendSize` are
|
|
62
74
|
* separate since the legend has historically been drawn one px larger to
|