@mmflow/charts 0.1.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/dist/index.cjs +6992 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1617 -0
- package/dist/index.d.ts +1617 -0
- package/dist/index.js +6900 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,1617 @@
|
|
|
1
|
+
import REGL from 'regl';
|
|
2
|
+
|
|
3
|
+
type EngineEvent = {
|
|
4
|
+
type: "viewport:changed";
|
|
5
|
+
} | {
|
|
6
|
+
type: "data:updated";
|
|
7
|
+
series: string;
|
|
8
|
+
} | {
|
|
9
|
+
type: "crosshair:hover";
|
|
10
|
+
timeIdx: number | null;
|
|
11
|
+
px: number;
|
|
12
|
+
py: number;
|
|
13
|
+
} | {
|
|
14
|
+
type: "crosshair:leave";
|
|
15
|
+
} | {
|
|
16
|
+
type: "theme:changed";
|
|
17
|
+
};
|
|
18
|
+
type Listener = (e: EngineEvent) => void;
|
|
19
|
+
declare class EventBus {
|
|
20
|
+
private readonly listeners;
|
|
21
|
+
on(fn: Listener): () => void;
|
|
22
|
+
emit(e: EngineEvent): void;
|
|
23
|
+
clear(): void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface OhlcvBuffer {
|
|
27
|
+
time: Float64Array;
|
|
28
|
+
open: Float64Array;
|
|
29
|
+
high: Float64Array;
|
|
30
|
+
low: Float64Array;
|
|
31
|
+
close: Float64Array;
|
|
32
|
+
volume: Float64Array;
|
|
33
|
+
length: number;
|
|
34
|
+
capacity: number;
|
|
35
|
+
}
|
|
36
|
+
interface OhlcvColumns {
|
|
37
|
+
time: ArrayLike<number>;
|
|
38
|
+
open: ArrayLike<number>;
|
|
39
|
+
high: ArrayLike<number>;
|
|
40
|
+
low: ArrayLike<number>;
|
|
41
|
+
close: ArrayLike<number>;
|
|
42
|
+
volume: ArrayLike<number>;
|
|
43
|
+
/** Valid row count. Defaults to the shortest supplied column length. */
|
|
44
|
+
length?: number;
|
|
45
|
+
}
|
|
46
|
+
declare class DataStore {
|
|
47
|
+
private readonly bus;
|
|
48
|
+
private candles_;
|
|
49
|
+
private ready_;
|
|
50
|
+
constructor(bus: EventBus);
|
|
51
|
+
get candles(): OhlcvBuffer;
|
|
52
|
+
get ready(): boolean;
|
|
53
|
+
setCandles(rows: ReadonlyArray<{
|
|
54
|
+
t: number;
|
|
55
|
+
o: number;
|
|
56
|
+
h: number;
|
|
57
|
+
l: number;
|
|
58
|
+
c: number;
|
|
59
|
+
v: number;
|
|
60
|
+
}> | OhlcvColumns): void;
|
|
61
|
+
setCandleColumns(columns: OhlcvColumns): void;
|
|
62
|
+
applyTrade(bucketStart: number, price: number, size: number): boolean;
|
|
63
|
+
clear(): void;
|
|
64
|
+
priceExtent(tStart: number, tEnd: number): [number, number] | null;
|
|
65
|
+
barIndexContaining(t: number): number;
|
|
66
|
+
barTimeContaining(t: number): number | null;
|
|
67
|
+
firstIndexAtOrAfter(t: number): number;
|
|
68
|
+
requestRange(coin: string, interval: string, startTime: number, endTime: number, signal?: AbortSignal, fetcher?: RangeFetcher): Promise<boolean>;
|
|
69
|
+
}
|
|
70
|
+
interface RawCandleRow {
|
|
71
|
+
t: number;
|
|
72
|
+
o: string | number;
|
|
73
|
+
h: string | number;
|
|
74
|
+
l: string | number;
|
|
75
|
+
c: string | number;
|
|
76
|
+
v: string | number;
|
|
77
|
+
}
|
|
78
|
+
interface RangeFetchRequest {
|
|
79
|
+
coin: string;
|
|
80
|
+
interval: string;
|
|
81
|
+
startTime: number;
|
|
82
|
+
endTime: number;
|
|
83
|
+
signal?: AbortSignal;
|
|
84
|
+
}
|
|
85
|
+
type RangeFetcher = (req: RangeFetchRequest) => Promise<ReadonlyArray<RawCandleRow>>;
|
|
86
|
+
|
|
87
|
+
interface RendererGLOptions {
|
|
88
|
+
canvas: HTMLCanvasElement;
|
|
89
|
+
onContextLost?: () => void;
|
|
90
|
+
onContextRestored?: () => void;
|
|
91
|
+
}
|
|
92
|
+
declare class RendererGL {
|
|
93
|
+
readonly regl: REGL.Regl;
|
|
94
|
+
private readonly canvas;
|
|
95
|
+
private readonly handleLost;
|
|
96
|
+
private readonly handleRestored;
|
|
97
|
+
private disposed;
|
|
98
|
+
constructor(opts: RendererGLOptions);
|
|
99
|
+
resize(cssW: number, cssH: number, dpr: number): void;
|
|
100
|
+
clear(color: [number, number, number, number]): void;
|
|
101
|
+
dispose(): void;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
interface Padding {
|
|
105
|
+
top: number;
|
|
106
|
+
right: number;
|
|
107
|
+
bottom: number;
|
|
108
|
+
left: number;
|
|
109
|
+
}
|
|
110
|
+
declare class Viewport {
|
|
111
|
+
private readonly bus;
|
|
112
|
+
width: number;
|
|
113
|
+
height: number;
|
|
114
|
+
dpr: number;
|
|
115
|
+
padding: Padding;
|
|
116
|
+
private t0;
|
|
117
|
+
private t1;
|
|
118
|
+
private p0;
|
|
119
|
+
private p1;
|
|
120
|
+
static readonly MIN_TIME_SPAN_MS = 100;
|
|
121
|
+
static readonly MIN_PRICE_SPAN_FRACTION = 1e-9;
|
|
122
|
+
priceAutoFit: boolean;
|
|
123
|
+
constructor(bus: EventBus);
|
|
124
|
+
setSize(cssWidth: number, cssHeight: number, dpr: number): void;
|
|
125
|
+
get plot(): {
|
|
126
|
+
x: number;
|
|
127
|
+
y: number;
|
|
128
|
+
w: number;
|
|
129
|
+
h: number;
|
|
130
|
+
};
|
|
131
|
+
setTimeRange(t0: number, t1: number): void;
|
|
132
|
+
setPriceRange(p0: number, p1: number, userInitiated?: boolean): void;
|
|
133
|
+
get timeRange(): [number, number];
|
|
134
|
+
get priceRange(): [number, number];
|
|
135
|
+
timeToX(t: number): number;
|
|
136
|
+
xToTime(x: number): number;
|
|
137
|
+
priceToY(p: number): number;
|
|
138
|
+
yToPrice(y: number): number;
|
|
139
|
+
timeToClipX(t: number): number;
|
|
140
|
+
priceToClipY(p: number): number;
|
|
141
|
+
pxXToClipX(cssX: number): number;
|
|
142
|
+
pxYToClipY(cssY: number): number;
|
|
143
|
+
pxWidthToClip(cssW: number): number;
|
|
144
|
+
pxHeightToClip(cssH: number): number;
|
|
145
|
+
get plotCss(): {
|
|
146
|
+
x: number;
|
|
147
|
+
y: number;
|
|
148
|
+
w: number;
|
|
149
|
+
h: number;
|
|
150
|
+
};
|
|
151
|
+
panPixels(dxCss: number, dyCss: number): void;
|
|
152
|
+
zoomTimeAt(cssX: number, factor: number): void;
|
|
153
|
+
zoomPriceAt(cssY: number, factor: number): void;
|
|
154
|
+
resetPriceAutoFit(): void;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
interface ChartTheme {
|
|
158
|
+
background: [number, number, number, number];
|
|
159
|
+
text: [number, number, number, number];
|
|
160
|
+
textDim: [number, number, number, number];
|
|
161
|
+
grid: [number, number, number, number];
|
|
162
|
+
gridVert: [number, number, number, number];
|
|
163
|
+
border: [number, number, number, number];
|
|
164
|
+
candleUp: [number, number, number, number];
|
|
165
|
+
candleDown: [number, number, number, number];
|
|
166
|
+
volUp: [number, number, number, number];
|
|
167
|
+
volDown: [number, number, number, number];
|
|
168
|
+
crosshair: [number, number, number, number];
|
|
169
|
+
crosshairLabelBg: [number, number, number, number];
|
|
170
|
+
crosshairLabelText: [number, number, number, number];
|
|
171
|
+
fontFamily: string;
|
|
172
|
+
}
|
|
173
|
+
type RGBA = [number, number, number, number];
|
|
174
|
+
type RGB$1 = [number, number, number];
|
|
175
|
+
type CandleColorThemeKey = "mmflow" | "percept" | "classic" | "sourgum" | "carbon";
|
|
176
|
+
interface CandleColorThemeOption {
|
|
177
|
+
key: CandleColorThemeKey;
|
|
178
|
+
label: string;
|
|
179
|
+
upHex: string;
|
|
180
|
+
downHex: string;
|
|
181
|
+
}
|
|
182
|
+
declare const DEFAULT_CANDLE_COLOR_THEME: CandleColorThemeKey;
|
|
183
|
+
declare const CANDLE_COLOR_THEMES: CandleColorThemeOption[];
|
|
184
|
+
declare const isCandleColorThemeKey: (value: string | null | undefined) => value is CandleColorThemeKey;
|
|
185
|
+
declare const darkTheme: (candleColorTheme?: CandleColorThemeKey) => ChartTheme;
|
|
186
|
+
declare const CHART_COLORS: {
|
|
187
|
+
sma: RGBA;
|
|
188
|
+
ema: RGBA;
|
|
189
|
+
vwap: RGBA;
|
|
190
|
+
rsi: RGBA;
|
|
191
|
+
rsiThreshold: RGBA;
|
|
192
|
+
openInterest: RGBA;
|
|
193
|
+
whaleBuy: RGBA;
|
|
194
|
+
whaleSell: RGBA;
|
|
195
|
+
liqLong: RGBA;
|
|
196
|
+
liqShort: RGBA;
|
|
197
|
+
vpPoc: RGBA;
|
|
198
|
+
vpValueArea: RGBA;
|
|
199
|
+
volumeBars: RGBA;
|
|
200
|
+
volumeProfile: RGBA;
|
|
201
|
+
footprint: RGBA;
|
|
202
|
+
obh: RGBA;
|
|
203
|
+
keyLevels: RGBA;
|
|
204
|
+
liqLevels: RGBA;
|
|
205
|
+
gex: RGBA;
|
|
206
|
+
};
|
|
207
|
+
type ChartColorKey = keyof typeof CHART_COLORS;
|
|
208
|
+
declare const swatchOf: (key: ChartColorKey) => RGB$1;
|
|
209
|
+
|
|
210
|
+
interface GlyphInfo {
|
|
211
|
+
u0: number;
|
|
212
|
+
v0: number;
|
|
213
|
+
u1: number;
|
|
214
|
+
v1: number;
|
|
215
|
+
width: number;
|
|
216
|
+
height: number;
|
|
217
|
+
advance: number;
|
|
218
|
+
}
|
|
219
|
+
interface GlyphAtlasOptions {
|
|
220
|
+
fontSize: number;
|
|
221
|
+
fontFamily?: string;
|
|
222
|
+
lineHeight?: number;
|
|
223
|
+
chars?: string;
|
|
224
|
+
}
|
|
225
|
+
declare class GlyphAtlas {
|
|
226
|
+
readonly fontSize: number;
|
|
227
|
+
readonly fontFamily: string;
|
|
228
|
+
readonly lineHeight: number;
|
|
229
|
+
readonly chars: string;
|
|
230
|
+
private readonly glyphs;
|
|
231
|
+
texture: REGL.Texture2D | null;
|
|
232
|
+
atlasWidth: number;
|
|
233
|
+
atlasHeight: number;
|
|
234
|
+
private builtAtPixelRatio;
|
|
235
|
+
constructor(opts: GlyphAtlasOptions);
|
|
236
|
+
glyph(ch: string): GlyphInfo | undefined;
|
|
237
|
+
build(regl: REGL.Regl, pixelRatio?: number): void;
|
|
238
|
+
measure(s: string): number;
|
|
239
|
+
get pixelRatio(): number;
|
|
240
|
+
dispose(): void;
|
|
241
|
+
private destroyTexture;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
interface LayerStats {
|
|
245
|
+
/** Median paint time in milliseconds. */
|
|
246
|
+
p50: number;
|
|
247
|
+
/** 95th-percentile paint time in milliseconds. */
|
|
248
|
+
p95: number;
|
|
249
|
+
/** Number of samples held in the ring buffer. */
|
|
250
|
+
count: number;
|
|
251
|
+
}
|
|
252
|
+
declare class PerfReporter {
|
|
253
|
+
private readonly windowSize;
|
|
254
|
+
private readonly samples;
|
|
255
|
+
constructor(windowSize?: number);
|
|
256
|
+
/**
|
|
257
|
+
* Record one paint-time sample for a layer. Called from
|
|
258
|
+
* Pane.paint / Scene.paint inside the per-layer try block;
|
|
259
|
+
* no-op if the layer is hidden because paint never ran.
|
|
260
|
+
*/
|
|
261
|
+
record(layerId: string, paintMs: number): void;
|
|
262
|
+
/**
|
|
263
|
+
* Snapshot of the current per-layer stats. The returned Map's
|
|
264
|
+
* iteration order is the insertion order — typically matches
|
|
265
|
+
* the layer paint order (BackgroundLayer → CandleLayer → …)
|
|
266
|
+
* which is what the overlay wants to display.
|
|
267
|
+
*/
|
|
268
|
+
getAllStats(): Map<string, LayerStats>;
|
|
269
|
+
/**
|
|
270
|
+
* Clear all samples — handy if the dev overlay is being
|
|
271
|
+
* re-armed mid-session and the user wants a fresh baseline.
|
|
272
|
+
*/
|
|
273
|
+
reset(): void;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
interface RenderContext {
|
|
277
|
+
regl: REGL.Regl;
|
|
278
|
+
viewport: Viewport;
|
|
279
|
+
theme: ChartTheme;
|
|
280
|
+
glyphAtlas: GlyphAtlas;
|
|
281
|
+
/**
|
|
282
|
+
* Optional dev-only sampler. When present, Pane.paint and
|
|
283
|
+
* Scene.paint wrap each layer's paint call with
|
|
284
|
+
* `performance.now()` bookends and feed the delta to the
|
|
285
|
+
* reporter. The `?perf=1` URL param activates it in dev
|
|
286
|
+
* builds; production renders never construct one.
|
|
287
|
+
*/
|
|
288
|
+
perfReporter?: PerfReporter;
|
|
289
|
+
}
|
|
290
|
+
interface Layer {
|
|
291
|
+
readonly id: string;
|
|
292
|
+
dirty: boolean;
|
|
293
|
+
/**
|
|
294
|
+
* When `false`, Pane.paint and Scene.paint skip this layer — its
|
|
295
|
+
* `dirty` flag still clears so a later visibility toggle paints a
|
|
296
|
+
* single frame instead of replaying every queued dirty since the
|
|
297
|
+
* layer went dark. `undefined` is treated as visible (default-on)
|
|
298
|
+
* so existing layers don't need to set this field.
|
|
299
|
+
*
|
|
300
|
+
* The flag is mutated by the host (ChartEngine) in response to
|
|
301
|
+
* IndicatorPicker toggles, with a `scene.markAllDirty()` so the
|
|
302
|
+
* next rAF paints the new visibility state.
|
|
303
|
+
*/
|
|
304
|
+
visible?: boolean;
|
|
305
|
+
paint(ctx: RenderContext): void;
|
|
306
|
+
onContextLost?(): void;
|
|
307
|
+
dispose?(): void;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
interface PaneBounds {
|
|
311
|
+
yStartFraction: number;
|
|
312
|
+
yEndFraction: number;
|
|
313
|
+
}
|
|
314
|
+
interface PanePadding {
|
|
315
|
+
top: number;
|
|
316
|
+
bottom: number;
|
|
317
|
+
}
|
|
318
|
+
declare class Pane {
|
|
319
|
+
readonly id: string;
|
|
320
|
+
readonly viewport: Viewport;
|
|
321
|
+
readonly layers: Layer[];
|
|
322
|
+
bounds: PaneBounds;
|
|
323
|
+
paddingInternal: PanePadding;
|
|
324
|
+
priceInteractive: boolean;
|
|
325
|
+
/**
|
|
326
|
+
* Whether the pane participates in painting. Defaults true.
|
|
327
|
+
*
|
|
328
|
+
* When set to `false`, `Scene.paint` skips the pane entirely —
|
|
329
|
+
* no scissor region is allocated, none of its layers run paint,
|
|
330
|
+
* and the slice it would have occupied is unavailable to layers
|
|
331
|
+
* in other panes (the host re-distributes bounds across the
|
|
332
|
+
* remaining visible panes via Scene.relayout).
|
|
333
|
+
*
|
|
334
|
+
* Unlike `Layer.visible`, this is NOT consulted by paint loops
|
|
335
|
+
* inside the pane itself — Scene gates entry. Toggling this
|
|
336
|
+
* field requires the host to also recompute bounds + call
|
|
337
|
+
* `Scene.relayout()` so the remaining panes claim the freed
|
|
338
|
+
* space, otherwise the hidden pane leaves a blank stripe.
|
|
339
|
+
*/
|
|
340
|
+
visible: boolean;
|
|
341
|
+
constructor(id: string, bus: EventBus, bounds: PaneBounds, paddingInternal?: PanePadding);
|
|
342
|
+
addLayer(layer: Layer): void;
|
|
343
|
+
removeLayer(id: string): void;
|
|
344
|
+
layout(cssWidth: number, cssHeight: number, dpr: number, globalPadding: {
|
|
345
|
+
top: number;
|
|
346
|
+
bottom: number;
|
|
347
|
+
}): void;
|
|
348
|
+
markAllDirty(): void;
|
|
349
|
+
notifyContextLost(): void;
|
|
350
|
+
paint(buildCtx: (viewport: Viewport) => RenderContext): void;
|
|
351
|
+
get hasDirtyLayer(): boolean;
|
|
352
|
+
dispose(): void;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
interface ScenePadding {
|
|
356
|
+
top: number;
|
|
357
|
+
bottom: number;
|
|
358
|
+
}
|
|
359
|
+
declare class Scene {
|
|
360
|
+
private readonly renderer;
|
|
361
|
+
private readonly theme;
|
|
362
|
+
private readonly bus;
|
|
363
|
+
private readonly glyphAtlas;
|
|
364
|
+
private readonly panes;
|
|
365
|
+
private readonly globalLayers;
|
|
366
|
+
private raf;
|
|
367
|
+
private viewportDirty;
|
|
368
|
+
private detachBus;
|
|
369
|
+
private running;
|
|
370
|
+
private frameErrors;
|
|
371
|
+
private cssWidth;
|
|
372
|
+
private cssHeight;
|
|
373
|
+
private dpr;
|
|
374
|
+
private perfReporter;
|
|
375
|
+
globalPadding: ScenePadding;
|
|
376
|
+
constructor(renderer: RendererGL, theme: () => ChartTheme, bus: EventBus, glyphAtlas: GlyphAtlas);
|
|
377
|
+
addPane(pane: Pane): void;
|
|
378
|
+
addGlobalLayer(layer: Layer): void;
|
|
379
|
+
removePane(id: string): void;
|
|
380
|
+
getPane(id: string): Pane | undefined;
|
|
381
|
+
primary(): Pane | undefined;
|
|
382
|
+
bottom(): Pane | undefined;
|
|
383
|
+
setSize(cssWidth: number, cssHeight: number, dpr: number): void;
|
|
384
|
+
panTimePixels(dxCss: number): void;
|
|
385
|
+
zoomTimeAt(cssX: number, factor: number): void;
|
|
386
|
+
setTimeRange(t0: number, t1: number): void;
|
|
387
|
+
paneAt(cssY: number): Pane | undefined;
|
|
388
|
+
markAllDirty(): void;
|
|
389
|
+
/**
|
|
390
|
+
* Re-run the per-pane layout pass using the Scene's current size +
|
|
391
|
+
* DPR. The host calls this after mutating any pane's `bounds`
|
|
392
|
+
* (e.g. when redistributing space across remaining visible panes
|
|
393
|
+
* after a sub-pane toggle) so the viewport padding picks up the
|
|
394
|
+
* new slice. Doesn't call `markAllDirty()` itself — the host can
|
|
395
|
+
* batch a single dirty pass after multiple bound mutations.
|
|
396
|
+
*
|
|
397
|
+
* No-op when the Scene hasn't been sized yet (pre-first-frame);
|
|
398
|
+
* the next `setSize` call will pick up the new bounds naturally.
|
|
399
|
+
*/
|
|
400
|
+
relayout(): void;
|
|
401
|
+
/**
|
|
402
|
+
* Attach a dev-only paint-time sampler. Pass `null` to detach.
|
|
403
|
+
* The reporter is threaded through the RenderContext built for
|
|
404
|
+
* each pane + global layer, where Pane.paint and Scene.paint
|
|
405
|
+
* wrap individual layer paints with `performance.now()` bookends
|
|
406
|
+
* when present. Cost when null: a single `?? undefined` per
|
|
407
|
+
* frame in the buildCtx call.
|
|
408
|
+
*/
|
|
409
|
+
setPerfReporter(reporter: PerfReporter | null): void;
|
|
410
|
+
notifyContextLost(): void;
|
|
411
|
+
start(): void;
|
|
412
|
+
stop(): void;
|
|
413
|
+
private frame;
|
|
414
|
+
dispose(): void;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
type Anchor = "left" | "center" | "right";
|
|
418
|
+
declare class TextBatch {
|
|
419
|
+
private readonly atlas;
|
|
420
|
+
private cmd;
|
|
421
|
+
private cornerBuf;
|
|
422
|
+
private rectBuf;
|
|
423
|
+
private uvBuf;
|
|
424
|
+
private colorBuf;
|
|
425
|
+
private rect;
|
|
426
|
+
private uv;
|
|
427
|
+
private color;
|
|
428
|
+
private count;
|
|
429
|
+
constructor(atlas: GlyphAtlas);
|
|
430
|
+
reset(): void;
|
|
431
|
+
addLabel(s: string, cssX: number, cssY: number, color: [number, number, number, number], viewport: Viewport, anchor?: Anchor, scale?: number): void;
|
|
432
|
+
flush(regl: REGL.Regl): void;
|
|
433
|
+
onContextLost(): void;
|
|
434
|
+
dispose(): void;
|
|
435
|
+
private ensureCapacity;
|
|
436
|
+
private compile;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
declare const RGB: {
|
|
440
|
+
readonly surface: "15,17,23";
|
|
441
|
+
readonly line: "35,39,51";
|
|
442
|
+
readonly dim: "138,143,156";
|
|
443
|
+
readonly value: "205,210,220";
|
|
444
|
+
readonly black: "0,0,0";
|
|
445
|
+
readonly up: "30,199,122";
|
|
446
|
+
};
|
|
447
|
+
declare const alpha: (rgb: string, a: number) => string;
|
|
448
|
+
declare const UI: {
|
|
449
|
+
/** Translucent panel background — floating palettes / triggers. */
|
|
450
|
+
readonly overlayBg: string;
|
|
451
|
+
/** Slightly more opaque panel background — perf / info overlays. */
|
|
452
|
+
readonly panelBg: string;
|
|
453
|
+
/** Solid drawer surface — one step lighter than the chart bg so a
|
|
454
|
+
* portalled menu (the indicator picker) reads as raised. */
|
|
455
|
+
readonly drawerBg: "#13161f";
|
|
456
|
+
/** Hairline border on floating chrome. */
|
|
457
|
+
readonly border: string;
|
|
458
|
+
/** Primary readout text (legend values, perf "good" rows). */
|
|
459
|
+
readonly value: "#cdd2dc";
|
|
460
|
+
/** Up / down — match darkTheme candleUp / candleDown. */
|
|
461
|
+
readonly up: "#1ec77a";
|
|
462
|
+
readonly down: "#f6465d";
|
|
463
|
+
/** Status accents — perf overlay warn / danger thresholds. */
|
|
464
|
+
readonly warn: "#f5d94d";
|
|
465
|
+
readonly danger: "#ffa699";
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
declare function niceStep(rough: number): number;
|
|
469
|
+
declare function formatPrice(p: number, step: number): string;
|
|
470
|
+
declare function formatHoverPrice(p: number, step: number): string;
|
|
471
|
+
declare function formatVolume(v: number): string;
|
|
472
|
+
declare function paneLegendValue(value: number, format: (v: number) => string): string;
|
|
473
|
+
declare function stepForRange(p0: number, p1: number, targetTicks?: number): number;
|
|
474
|
+
|
|
475
|
+
declare const PLOT_GUTTER: {
|
|
476
|
+
readonly top: 8;
|
|
477
|
+
readonly right: 92;
|
|
478
|
+
readonly bottom: 32;
|
|
479
|
+
readonly left: 8;
|
|
480
|
+
};
|
|
481
|
+
declare const AXIS_LABEL_SCALE = 0.85;
|
|
482
|
+
|
|
483
|
+
type ChartTypeFamily = "Candle" | "Bars" | "Line" | "Area" | "Pro";
|
|
484
|
+
type ChartTypeKey = "candle" | "hollow-candle" | "heikin-ashi" | "bars" | "volume-bars" | "columns" | "high-low" | "line" | "line-markers" | "step-line" | "hlc-area" | "area" | "baseline" | "volume-footprint" | "tpo";
|
|
485
|
+
type ChartTypeAvailability = "available" | "partial" | "source_required";
|
|
486
|
+
interface ChartTypeOption {
|
|
487
|
+
key: ChartTypeKey;
|
|
488
|
+
family: ChartTypeFamily;
|
|
489
|
+
label: string;
|
|
490
|
+
short: string;
|
|
491
|
+
availability: ChartTypeAvailability;
|
|
492
|
+
}
|
|
493
|
+
declare const CHART_TYPE_FAMILIES: readonly ChartTypeFamily[];
|
|
494
|
+
declare const CHART_TYPE_OPTIONS: readonly ChartTypeOption[];
|
|
495
|
+
declare const DEFAULT_CHART_TYPE: ChartTypeKey;
|
|
496
|
+
declare const CHART_TYPE_BY_KEY: Record<ChartTypeKey, ChartTypeOption>;
|
|
497
|
+
declare function isChartTypeKey(value: string): value is ChartTypeKey;
|
|
498
|
+
declare function isChartTypeSelectable(option: ChartTypeOption): boolean;
|
|
499
|
+
declare function chartTypeAvailabilityLabel(availability: ChartTypeAvailability): "Live" | "Partial" | "Source";
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Every toggleable indicator key understood by the picker. The
|
|
503
|
+
* literal-union shape (rather than an enum) lets `Record<IndicatorKey,
|
|
504
|
+
* X>` types in callers fail at compile time if a new key is added
|
|
505
|
+
* without an entry — the same exhaustiveness pattern used for
|
|
506
|
+
* DrawingTool / DrawnShape kind.
|
|
507
|
+
*/
|
|
508
|
+
type IndicatorKey = "sma20" | "ema50" | "vwap" | "rsi" | "openInterest" | "obh" | "volumeBars" | "volumeProfile" | "vpLines" | "footprint" | "keyLevels" | "liqLevels" | "gex" | "liqEvents" | "whales";
|
|
509
|
+
/**
|
|
510
|
+
* Categories shown in the picker. Order is the display order; the
|
|
511
|
+
* picker renders one section per category in this order.
|
|
512
|
+
*
|
|
513
|
+
* Categories mirror the kiyotaka grouping in the P8 plan brief:
|
|
514
|
+
* Overlays (price-line indicators), Volume (anything reading volume
|
|
515
|
+
* across price levels), Order Flow (positioning / tape signals),
|
|
516
|
+
* Key Levels (static reference levels).
|
|
517
|
+
*/
|
|
518
|
+
type IndicatorCategory = "Overlays" | "Oscillators" | "Volume" | "Order Flow" | "Funding & OI" | "Key Levels";
|
|
519
|
+
declare const INDICATOR_CATEGORIES: readonly IndicatorCategory[];
|
|
520
|
+
interface IndicatorEntry {
|
|
521
|
+
key: IndicatorKey;
|
|
522
|
+
/** Display label shown in the picker. */
|
|
523
|
+
label: string;
|
|
524
|
+
/** Short blurb shown as a hover tooltip on the picker row. */
|
|
525
|
+
description: string;
|
|
526
|
+
category: IndicatorCategory;
|
|
527
|
+
/** Display-only swatch RGB (0–1). Picker renders a tiny colored dot. */
|
|
528
|
+
swatch: [number, number, number];
|
|
529
|
+
}
|
|
530
|
+
declare const INDICATOR_CATALOG: readonly IndicatorEntry[];
|
|
531
|
+
declare const INDICATOR_BY_KEY: Record<IndicatorKey, IndicatorEntry>;
|
|
532
|
+
/**
|
|
533
|
+
* Default-on set. Keep the first paint chart-native: candles, volume,
|
|
534
|
+
* VWAP, visible-range volume profile, and the order-book heatmap.
|
|
535
|
+
* Heavier footprint/OI/liquidation overlays remain explicit opt-ins.
|
|
536
|
+
*/
|
|
537
|
+
declare const DEFAULT_VISIBLE_INDICATORS: ReadonlySet<IndicatorKey>;
|
|
538
|
+
/**
|
|
539
|
+
* Map every IndicatorKey to the list of chart-layer ids it gates.
|
|
540
|
+
* Most are 1:1; the side-grouped ones (whales = buy+sell, liqEvents
|
|
541
|
+
* = long+short marker layers) are 1:N. Kept as a separate map (not
|
|
542
|
+
* embedded in INDICATOR_CATALOG) so the engine-side wiring concern
|
|
543
|
+
* stays out of the picker UI concerns — the picker doesn't need to
|
|
544
|
+
* know how many GL layers a checkbox controls.
|
|
545
|
+
*
|
|
546
|
+
* Layer ids match the `id` passed to each `new XxxLayer({id, ...})`
|
|
547
|
+
* in ChartEngine — when adding a new indicator, the engine-side
|
|
548
|
+
* lookup is by id, so the id strings here MUST match.
|
|
549
|
+
*/
|
|
550
|
+
declare const CHART_LAYERS_BY_INDICATOR: Record<IndicatorKey, readonly string[]>;
|
|
551
|
+
/**
|
|
552
|
+
* Map every IndicatorKey to the list of sub-pane ids it gates.
|
|
553
|
+
* Most catalog keys are main-pane layers and have an empty list
|
|
554
|
+
* here; only sub-pane toggles (rsi → "rsi" pane, openInterest →
|
|
555
|
+
* "oi" pane) populate this map.
|
|
556
|
+
*
|
|
557
|
+
* Why a separate map from CHART_LAYERS_BY_INDICATOR: layers and
|
|
558
|
+
* panes are different rendering units. A layer hidden via
|
|
559
|
+
* Layer.visible just stops painting; a pane hidden via Pane.visible
|
|
560
|
+
* also needs its allocated slice redistributed via Scene.relayout —
|
|
561
|
+
* the engine treats these branches differently so the maps stay
|
|
562
|
+
* structurally separate too.
|
|
563
|
+
*/
|
|
564
|
+
declare const CHART_PANES_BY_INDICATOR: Record<IndicatorKey, readonly string[]>;
|
|
565
|
+
|
|
566
|
+
type IndicatorLibraryGroup = "Aggregate Flow" | "Profiles" | "Order Flow" | "Exchange Data" | "Options & Macro" | "Sector" | "Technical";
|
|
567
|
+
type IndicatorAvailability = "available" | "partial" | "source_required";
|
|
568
|
+
interface IndicatorLibraryEntry {
|
|
569
|
+
id: string;
|
|
570
|
+
title: string;
|
|
571
|
+
short: string;
|
|
572
|
+
description: string;
|
|
573
|
+
group: IndicatorLibraryGroup;
|
|
574
|
+
key?: IndicatorKey;
|
|
575
|
+
availability?: IndicatorAvailability;
|
|
576
|
+
source?: string;
|
|
577
|
+
}
|
|
578
|
+
declare const INDICATOR_LIBRARY_GROUPS: readonly IndicatorLibraryGroup[];
|
|
579
|
+
declare const INDICATOR_LIBRARY: readonly IndicatorLibraryEntry[];
|
|
580
|
+
declare function indicatorAvailability(entry: IndicatorLibraryEntry): IndicatorAvailability;
|
|
581
|
+
declare function indicatorAvailabilityLabel(entry: IndicatorLibraryEntry): "LIVE" | "PARTIAL" | "SOURCE";
|
|
582
|
+
declare function indicatorCanToggle(entry: IndicatorLibraryEntry): boolean;
|
|
583
|
+
|
|
584
|
+
interface DrawnHLine {
|
|
585
|
+
id: string;
|
|
586
|
+
kind: "hline";
|
|
587
|
+
/** Price in instrument units. */
|
|
588
|
+
price: number;
|
|
589
|
+
/** Color RGBA in 0..1, premultiplied-safe (alpha applied at blend). */
|
|
590
|
+
color: [number, number, number, number];
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Vertical line — single time anchor, spans the full main pane
|
|
594
|
+
* height. Typical use: marking news events, daily/weekly opens,
|
|
595
|
+
* earnings releases, anything where the chart needs a "this
|
|
596
|
+
* moment" reference. Single-click commit (mirrors hline; no
|
|
597
|
+
* preview phase).
|
|
598
|
+
*/
|
|
599
|
+
interface DrawnVLine {
|
|
600
|
+
id: string;
|
|
601
|
+
kind: "vline";
|
|
602
|
+
/** Time in ms since epoch. */
|
|
603
|
+
t: number;
|
|
604
|
+
color: [number, number, number, number];
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Trend line — segment between two (time, price) anchors. Anchored in
|
|
608
|
+
* data space (not pixel space) so the segment follows the data when
|
|
609
|
+
* the user pans or zooms. `t1`/`t2` are ms-since-epoch; `price1`/
|
|
610
|
+
* `price2` are in instrument units.
|
|
611
|
+
*/
|
|
612
|
+
interface DrawnTrend {
|
|
613
|
+
id: string;
|
|
614
|
+
kind: "trend";
|
|
615
|
+
t1: number;
|
|
616
|
+
price1: number;
|
|
617
|
+
t2: number;
|
|
618
|
+
price2: number;
|
|
619
|
+
color: [number, number, number, number];
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Rectangle outline between two (time, price) anchors — same anchor
|
|
623
|
+
* shape as DrawnTrend, but the renderer emits four edge segments to
|
|
624
|
+
* form a box. Outline-only (no fill) keeps the candles underneath
|
|
625
|
+
* legible. Hit-test treats any of the four edges as the click
|
|
626
|
+
* target; the interior is non-interactive.
|
|
627
|
+
*/
|
|
628
|
+
interface DrawnRect {
|
|
629
|
+
id: string;
|
|
630
|
+
kind: "rect";
|
|
631
|
+
t1: number;
|
|
632
|
+
price1: number;
|
|
633
|
+
t2: number;
|
|
634
|
+
price2: number;
|
|
635
|
+
color: [number, number, number, number];
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Fibonacci retracement — same two (time, price) anchors as trend/
|
|
639
|
+
* rect, renders as N horizontal levels at the standard retracement
|
|
640
|
+
* ratios between price1 and price2. Lines span x(t1)→x(t2) so the
|
|
641
|
+
* fib stays local to the user's selected range (matches the
|
|
642
|
+
* TradingView default; differs from "full-width hlines" which
|
|
643
|
+
* would lose the local-range signal). Hit-test treats any of the
|
|
644
|
+
* levels as the click target — deleting the fib removes the whole
|
|
645
|
+
* set, not a single level.
|
|
646
|
+
*/
|
|
647
|
+
interface DrawnFib {
|
|
648
|
+
id: string;
|
|
649
|
+
kind: "fib";
|
|
650
|
+
t1: number;
|
|
651
|
+
price1: number;
|
|
652
|
+
t2: number;
|
|
653
|
+
price2: number;
|
|
654
|
+
color: [number, number, number, number];
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Parallel channel — a base trend segment (t1,price1)→(t2,price2)
|
|
658
|
+
* plus a second line parallel to it, offset vertically in price by
|
|
659
|
+
* `priceOffset`. The parallel line runs
|
|
660
|
+
* (t1, price1+priceOffset)→(t2, price2+priceOffset), so both lines
|
|
661
|
+
* share the base slope (price2-price1)/(t2-t1) in data space and
|
|
662
|
+
* stay parallel through any pan/zoom.
|
|
663
|
+
*
|
|
664
|
+
* The offset is stored as a single vertical price delta rather than
|
|
665
|
+
* a third (t,price) anchor: the parallel constraint makes the shift
|
|
666
|
+
* one degree of freedom (a vertical price gap), so the third click's
|
|
667
|
+
* time coordinate is redundant for rendering. Storing the delta
|
|
668
|
+
* keeps the shape compact and the render math a straight reuse of
|
|
669
|
+
* the trend-line projection. A future "drag the parallel handle"
|
|
670
|
+
* edit slice recomputes `priceOffset` from the dragged point the
|
|
671
|
+
* same way the commit does.
|
|
672
|
+
*
|
|
673
|
+
* Three-click authoring (two for the base line, one for the offset)
|
|
674
|
+
* is the only multi-anchor-phase tool in the set; see the
|
|
675
|
+
* ChartEngine pending-draw state machine.
|
|
676
|
+
*/
|
|
677
|
+
interface DrawnChannel {
|
|
678
|
+
id: string;
|
|
679
|
+
kind: "channel";
|
|
680
|
+
t1: number;
|
|
681
|
+
price1: number;
|
|
682
|
+
t2: number;
|
|
683
|
+
price2: number;
|
|
684
|
+
/** Vertical price offset of the parallel line from the base line. */
|
|
685
|
+
priceOffset: number;
|
|
686
|
+
color: [number, number, number, number];
|
|
687
|
+
}
|
|
688
|
+
type DrawnShape = DrawnHLine | DrawnTrend | DrawnRect | DrawnFib | DrawnVLine | DrawnChannel;
|
|
689
|
+
/**
|
|
690
|
+
* Standard fibonacci retracement ratios. 0.0 is the price1 anchor
|
|
691
|
+
* end; 1.0 is the price2 anchor end. The 0.618 "golden ratio" and
|
|
692
|
+
* 0.5 midpoint are the most-traded levels; 0.236 / 0.382 / 0.786
|
|
693
|
+
* are common secondary levels.
|
|
694
|
+
*
|
|
695
|
+
* Exported so a future fib label / hover-info slice can reuse the
|
|
696
|
+
* exact set the layer renders without redeclaring.
|
|
697
|
+
*/
|
|
698
|
+
declare const FIB_RATIOS: readonly number[];
|
|
699
|
+
declare const DEFAULT_HLINE_COLOR: [number, number, number, number];
|
|
700
|
+
declare const DEFAULT_TREND_COLOR: [number, number, number, number];
|
|
701
|
+
declare const DEFAULT_RECT_COLOR: [number, number, number, number];
|
|
702
|
+
declare const DEFAULT_FIB_COLOR: [number, number, number, number];
|
|
703
|
+
declare const DEFAULT_VLINE_COLOR: [number, number, number, number];
|
|
704
|
+
declare const DEFAULT_CHANNEL_COLOR: [number, number, number, number];
|
|
705
|
+
declare const DEFAULT_COLOR_BY_KIND: Record<DrawnShape["kind"], [
|
|
706
|
+
number,
|
|
707
|
+
number,
|
|
708
|
+
number,
|
|
709
|
+
number
|
|
710
|
+
]>;
|
|
711
|
+
declare const PREVIEW_SHAPE_ID = "__preview__";
|
|
712
|
+
/**
|
|
713
|
+
* Generate a stable shape id. Uses crypto.randomUUID when present,
|
|
714
|
+
* falls back to a Math.random base36 token for old browsers / SSR.
|
|
715
|
+
* IDs are opaque — code never parses them.
|
|
716
|
+
*/
|
|
717
|
+
declare function makeShapeId(): string;
|
|
718
|
+
/**
|
|
719
|
+
* Load drawings for a coin. Returns [] if storage is unavailable,
|
|
720
|
+
* the entry is missing, or the payload fails the shape guard. Bad
|
|
721
|
+
* payloads are NOT auto-cleared — a transient parse failure during
|
|
722
|
+
* a multi-tab race shouldn't nuke the user's work.
|
|
723
|
+
*
|
|
724
|
+
* Multi-tab note: load is called on coin change and on mount. Two
|
|
725
|
+
* tabs editing the same coin will diverge until one of them
|
|
726
|
+
* triggers a reload; cross-tab sync via `storage` events is an
|
|
727
|
+
* explicit non-goal for the first slice (most users edit one chart
|
|
728
|
+
* at a time and multi-tab divergence is acceptable for v1).
|
|
729
|
+
*/
|
|
730
|
+
declare function loadDrawings(coin: string): DrawnShape[];
|
|
731
|
+
/**
|
|
732
|
+
* Save drawings for a coin. No-op if storage is unavailable.
|
|
733
|
+
* Empty array deletes the storage entry so we don't accumulate
|
|
734
|
+
* keys for coins the user briefly browsed but never drew on.
|
|
735
|
+
*/
|
|
736
|
+
declare function saveDrawings(coin: string, shapes: DrawnShape[]): void;
|
|
737
|
+
|
|
738
|
+
declare class BackgroundLayer implements Layer {
|
|
739
|
+
readonly id = "background";
|
|
740
|
+
dirty: boolean;
|
|
741
|
+
private cmd;
|
|
742
|
+
private horizontalBuffer;
|
|
743
|
+
private verticalBuffer;
|
|
744
|
+
private disposed;
|
|
745
|
+
private horizontalScratch;
|
|
746
|
+
private verticalScratch;
|
|
747
|
+
paint(ctx: RenderContext): void;
|
|
748
|
+
onContextLost(): void;
|
|
749
|
+
private compile;
|
|
750
|
+
dispose(): void;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
declare class CandleLayer implements Layer {
|
|
754
|
+
private readonly store;
|
|
755
|
+
readonly id = "candles";
|
|
756
|
+
dirty: boolean;
|
|
757
|
+
private bodyCmd;
|
|
758
|
+
private wickCmd;
|
|
759
|
+
private lineCmd;
|
|
760
|
+
private fillCmd;
|
|
761
|
+
private cornerBuf;
|
|
762
|
+
private xCenterBuf;
|
|
763
|
+
private halfWidthBuf;
|
|
764
|
+
private openYBuf;
|
|
765
|
+
private closeYBuf;
|
|
766
|
+
private isUpBuf;
|
|
767
|
+
private wickPosBuf;
|
|
768
|
+
private wickIsUpBuf;
|
|
769
|
+
private lineBuf;
|
|
770
|
+
private fillBuf;
|
|
771
|
+
private xCenter;
|
|
772
|
+
private halfWidth;
|
|
773
|
+
private openY;
|
|
774
|
+
private closeY;
|
|
775
|
+
private isUp;
|
|
776
|
+
private wickPos;
|
|
777
|
+
private wickIsUp;
|
|
778
|
+
private lineScratch;
|
|
779
|
+
private fillScratch;
|
|
780
|
+
private haOpen;
|
|
781
|
+
private haHigh;
|
|
782
|
+
private haLow;
|
|
783
|
+
private haClose;
|
|
784
|
+
private disposed;
|
|
785
|
+
private liveAnim;
|
|
786
|
+
private chartType;
|
|
787
|
+
constructor(store: DataStore);
|
|
788
|
+
setChartType(next: ChartTypeKey): void;
|
|
789
|
+
paint(ctx: RenderContext): void;
|
|
790
|
+
onContextLost(): void;
|
|
791
|
+
dispose(): void;
|
|
792
|
+
private effectiveType;
|
|
793
|
+
private paintCandleBodies;
|
|
794
|
+
private paintMarkers;
|
|
795
|
+
private paintCloseLine;
|
|
796
|
+
private paintCloseArea;
|
|
797
|
+
private paintHlcArea;
|
|
798
|
+
private paintOhlcBars;
|
|
799
|
+
private paintColumns;
|
|
800
|
+
private paintBodyOutlines;
|
|
801
|
+
private uploadBodies;
|
|
802
|
+
private computeHeikinAshi;
|
|
803
|
+
private estimateInterval;
|
|
804
|
+
private liveCandle;
|
|
805
|
+
private sampleLiveCandle;
|
|
806
|
+
private ensureCapacity;
|
|
807
|
+
private ensureLineCapacity;
|
|
808
|
+
private ensureFillCapacity;
|
|
809
|
+
private compile;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
type TimeSnap = (timeMs: number) => number | null;
|
|
813
|
+
interface SmoothCrosshairAxisResult {
|
|
814
|
+
value: number;
|
|
815
|
+
settled: boolean;
|
|
816
|
+
}
|
|
817
|
+
declare function smoothCrosshairAxis(current: number | null | undefined, target: number, factor?: number, epsilonPx?: number): SmoothCrosshairAxisResult;
|
|
818
|
+
declare class CrosshairLayer implements Layer {
|
|
819
|
+
readonly id = "crosshair";
|
|
820
|
+
dirty: boolean;
|
|
821
|
+
private cmd;
|
|
822
|
+
private buffer;
|
|
823
|
+
private fillCmd;
|
|
824
|
+
private fillBuffer;
|
|
825
|
+
private fillScratch;
|
|
826
|
+
private batch;
|
|
827
|
+
private scratch;
|
|
828
|
+
private detachBus;
|
|
829
|
+
private disposed;
|
|
830
|
+
private cssX;
|
|
831
|
+
private cssY;
|
|
832
|
+
private drawCssX;
|
|
833
|
+
private drawCssY;
|
|
834
|
+
private lastDpr;
|
|
835
|
+
private snap;
|
|
836
|
+
constructor(bus: EventBus, snap?: TimeSnap);
|
|
837
|
+
setSnap(snap: TimeSnap | null): void;
|
|
838
|
+
paint(ctx: RenderContext): void;
|
|
839
|
+
private pushRect;
|
|
840
|
+
onContextLost(): void;
|
|
841
|
+
dispose(): void;
|
|
842
|
+
private compile;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
declare class DrawingsLayer implements Layer {
|
|
846
|
+
readonly id: string;
|
|
847
|
+
dirty: boolean;
|
|
848
|
+
private cmd;
|
|
849
|
+
private buffer;
|
|
850
|
+
private scratch;
|
|
851
|
+
private shapes;
|
|
852
|
+
private hlineGroups;
|
|
853
|
+
private trendGroups;
|
|
854
|
+
private rectGroups;
|
|
855
|
+
private fibGroups;
|
|
856
|
+
private vlineGroups;
|
|
857
|
+
private channelGroups;
|
|
858
|
+
private preview;
|
|
859
|
+
private disposed;
|
|
860
|
+
constructor(id: string);
|
|
861
|
+
setShapes(shapes: DrawnShape[]): void;
|
|
862
|
+
/**
|
|
863
|
+
* Set or clear the in-progress shape from a multi-click drawing
|
|
864
|
+
* tool. Pass `null` to clear (e.g. on commit or cancel). The
|
|
865
|
+
* preview always renders on top of committed shapes since it's
|
|
866
|
+
* fed last into the per-color emit loop.
|
|
867
|
+
*/
|
|
868
|
+
setPreview(shape: DrawnShape | null): void;
|
|
869
|
+
clear(): void;
|
|
870
|
+
/**
|
|
871
|
+
* Generic per-color group builder. Filters by `kind`, applies the
|
|
872
|
+
* caller's `validate` predicate (per-shape finiteness), and
|
|
873
|
+
* groups by joined color string. Returned `items` are typed via
|
|
874
|
+
* the `Extract<DrawnShape, {kind: K}>` discriminator so the
|
|
875
|
+
* paint loops can read kind-specific fields without a runtime
|
|
876
|
+
* narrowing check.
|
|
877
|
+
*
|
|
878
|
+
* Three near-identical builders (one per shape kind) collapsed
|
|
879
|
+
* into this generic at the fib slice — the audit explicitly
|
|
880
|
+
* called the fourth tool as the right moment to take the
|
|
881
|
+
* refactor since the rule-of-three was met.
|
|
882
|
+
*/
|
|
883
|
+
private static buildGroupsBy;
|
|
884
|
+
/**
|
|
885
|
+
* Hit-test the user's committed shapes for right-click delete.
|
|
886
|
+
* Walks the shape array newest-first so a hit on the topmost
|
|
887
|
+
* shape wins. Preview shapes are not hit-tested.
|
|
888
|
+
*
|
|
889
|
+
* `thresholdPx` defaults to 6 — wider than the rendered 1-px line
|
|
890
|
+
* so click targets stay forgiving on hi-DPR displays.
|
|
891
|
+
*/
|
|
892
|
+
hitTest(viewport: Viewport, cssX: number, cssY: number, thresholdPx?: number): DrawnShape | null;
|
|
893
|
+
paint(ctx: RenderContext): void;
|
|
894
|
+
private emitPreview;
|
|
895
|
+
onContextLost(): void;
|
|
896
|
+
dispose(): void;
|
|
897
|
+
private compile;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
declare class ExtremaAnnotationLayer implements Layer {
|
|
901
|
+
private readonly store;
|
|
902
|
+
readonly id = "extrema-annotations";
|
|
903
|
+
dirty: boolean;
|
|
904
|
+
private lineCmd;
|
|
905
|
+
private fillCmd;
|
|
906
|
+
private lineBuffer;
|
|
907
|
+
private fillBuffer;
|
|
908
|
+
private batch;
|
|
909
|
+
private lineScratch;
|
|
910
|
+
private fillScratch;
|
|
911
|
+
private disposed;
|
|
912
|
+
constructor(store: DataStore);
|
|
913
|
+
paint(ctx: RenderContext): void;
|
|
914
|
+
onContextLost(): void;
|
|
915
|
+
dispose(): void;
|
|
916
|
+
private makeCallout;
|
|
917
|
+
private pushRect;
|
|
918
|
+
private compile;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
interface HLineSpec {
|
|
922
|
+
value: number;
|
|
923
|
+
color: [number, number, number, number];
|
|
924
|
+
}
|
|
925
|
+
declare class HorizontalLineLayer implements Layer {
|
|
926
|
+
readonly id: string;
|
|
927
|
+
dirty: boolean;
|
|
928
|
+
private cmd;
|
|
929
|
+
private buffer;
|
|
930
|
+
private scratch;
|
|
931
|
+
private disposed;
|
|
932
|
+
private groups;
|
|
933
|
+
private lines;
|
|
934
|
+
constructor(id: string, lines: HLineSpec[]);
|
|
935
|
+
setLines(lines: HLineSpec[]): void;
|
|
936
|
+
private static buildGroups;
|
|
937
|
+
paint(ctx: RenderContext): void;
|
|
938
|
+
onContextLost(): void;
|
|
939
|
+
dispose(): void;
|
|
940
|
+
private compile;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
interface IndicatorOptions {
|
|
944
|
+
id: string;
|
|
945
|
+
compute: (closes: number[], b: OhlcvBuffer) => number[];
|
|
946
|
+
color: [number, number, number, number];
|
|
947
|
+
}
|
|
948
|
+
declare class IndicatorLayer implements Layer {
|
|
949
|
+
private readonly store;
|
|
950
|
+
private readonly opts;
|
|
951
|
+
readonly id: string;
|
|
952
|
+
dirty: boolean;
|
|
953
|
+
private cmd;
|
|
954
|
+
private buffer;
|
|
955
|
+
private scratch;
|
|
956
|
+
private disposed;
|
|
957
|
+
private closes;
|
|
958
|
+
private closesLength;
|
|
959
|
+
private lastValues;
|
|
960
|
+
constructor(store: DataStore, opts: IndicatorOptions);
|
|
961
|
+
paint(ctx: RenderContext): void;
|
|
962
|
+
valueAt(i: number): number;
|
|
963
|
+
onContextLost(): void;
|
|
964
|
+
dispose(): void;
|
|
965
|
+
private ensureCapacity;
|
|
966
|
+
private compile;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
interface MarkerSpec {
|
|
970
|
+
t: number;
|
|
971
|
+
price: number;
|
|
972
|
+
/** USD notional; drives marker size via log scale. */
|
|
973
|
+
sizeUsd: number;
|
|
974
|
+
}
|
|
975
|
+
declare class MarkerLayer implements Layer {
|
|
976
|
+
private readonly opts;
|
|
977
|
+
readonly id: string;
|
|
978
|
+
dirty: boolean;
|
|
979
|
+
private markers;
|
|
980
|
+
private cmd;
|
|
981
|
+
private cornerBuf;
|
|
982
|
+
private xCenterBuf;
|
|
983
|
+
private yCenterBuf;
|
|
984
|
+
private halfWidthBuf;
|
|
985
|
+
private halfHeightBuf;
|
|
986
|
+
private xCenter;
|
|
987
|
+
private yCenter;
|
|
988
|
+
private halfWidth;
|
|
989
|
+
private halfHeight;
|
|
990
|
+
private disposed;
|
|
991
|
+
private readonly logMin;
|
|
992
|
+
private readonly logMax;
|
|
993
|
+
constructor(opts: {
|
|
994
|
+
id: string;
|
|
995
|
+
sideSign: 1 | -1;
|
|
996
|
+
color: [number, number, number, number];
|
|
997
|
+
/** Override the log10(USD) lower anchor for marker sizing. */
|
|
998
|
+
logMinUsd?: number;
|
|
999
|
+
/** Override the log10(USD) upper anchor for marker sizing. */
|
|
1000
|
+
logMaxUsd?: number;
|
|
1001
|
+
});
|
|
1002
|
+
setMarkers(markers: MarkerSpec[]): void;
|
|
1003
|
+
clear(): void;
|
|
1004
|
+
paint(ctx: RenderContext): void;
|
|
1005
|
+
onContextLost(): void;
|
|
1006
|
+
dispose(): void;
|
|
1007
|
+
private ensureCapacity;
|
|
1008
|
+
private compile;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
interface OiHistoryPoint {
|
|
1012
|
+
t: number;
|
|
1013
|
+
v: number;
|
|
1014
|
+
}
|
|
1015
|
+
declare class OiHistoryLayer implements Layer {
|
|
1016
|
+
private readonly opts;
|
|
1017
|
+
readonly id: string;
|
|
1018
|
+
dirty: boolean;
|
|
1019
|
+
private points;
|
|
1020
|
+
private cmd;
|
|
1021
|
+
private buffer;
|
|
1022
|
+
private scratch;
|
|
1023
|
+
private disposed;
|
|
1024
|
+
constructor(opts: {
|
|
1025
|
+
id: string;
|
|
1026
|
+
color: [number, number, number, number];
|
|
1027
|
+
});
|
|
1028
|
+
setPoints(points: OiHistoryPoint[]): void;
|
|
1029
|
+
clear(): void;
|
|
1030
|
+
valueAt(t: number): number;
|
|
1031
|
+
paint(ctx: RenderContext): void;
|
|
1032
|
+
onContextLost(): void;
|
|
1033
|
+
dispose(): void;
|
|
1034
|
+
private ensureCapacity;
|
|
1035
|
+
private compile;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
interface OrderBookHeatmapLevel {
|
|
1039
|
+
px: number;
|
|
1040
|
+
sz: number;
|
|
1041
|
+
}
|
|
1042
|
+
interface OrderBookHeatmapSnapshot {
|
|
1043
|
+
time: number;
|
|
1044
|
+
bids: OrderBookHeatmapLevel[];
|
|
1045
|
+
asks: OrderBookHeatmapLevel[];
|
|
1046
|
+
}
|
|
1047
|
+
interface OrderBookHeatmapHover {
|
|
1048
|
+
time: number;
|
|
1049
|
+
price: number;
|
|
1050
|
+
bucketLow: number;
|
|
1051
|
+
bucketHigh: number;
|
|
1052
|
+
bidUsd: number;
|
|
1053
|
+
askUsd: number;
|
|
1054
|
+
totalUsd: number;
|
|
1055
|
+
side: "bid" | "ask" | "both";
|
|
1056
|
+
snapshotAgeMs: number;
|
|
1057
|
+
}
|
|
1058
|
+
interface OrderBookHeatmapSummary {
|
|
1059
|
+
snapshotCount: number;
|
|
1060
|
+
oldestTime: number;
|
|
1061
|
+
newestTime: number;
|
|
1062
|
+
newestBidUsd: number;
|
|
1063
|
+
newestAskUsd: number;
|
|
1064
|
+
newestTotalUsd: number;
|
|
1065
|
+
maxBucketUsd: number;
|
|
1066
|
+
visibleBucketCount: number;
|
|
1067
|
+
}
|
|
1068
|
+
declare class OrderBookHeatmapLayer implements Layer {
|
|
1069
|
+
private readonly opts;
|
|
1070
|
+
readonly id = "obh";
|
|
1071
|
+
dirty: boolean;
|
|
1072
|
+
private snapshots;
|
|
1073
|
+
private bins;
|
|
1074
|
+
private texture;
|
|
1075
|
+
private cmd;
|
|
1076
|
+
private cornerBuf;
|
|
1077
|
+
private disposed;
|
|
1078
|
+
private lastSnapshotGen;
|
|
1079
|
+
private snapshotGen;
|
|
1080
|
+
private lastP0;
|
|
1081
|
+
private lastP1;
|
|
1082
|
+
constructor(opts?: {
|
|
1083
|
+
alpha?: number;
|
|
1084
|
+
});
|
|
1085
|
+
addSnapshot(snap: OrderBookHeatmapSnapshot): void;
|
|
1086
|
+
clear(): void;
|
|
1087
|
+
hoverAt(time: number, price: number, priceRange: readonly [number, number]): OrderBookHeatmapHover | null;
|
|
1088
|
+
summary(priceRange: readonly [number, number]): OrderBookHeatmapSummary | null;
|
|
1089
|
+
paint(ctx: RenderContext): void;
|
|
1090
|
+
onContextLost(): void;
|
|
1091
|
+
dispose(): void;
|
|
1092
|
+
private rebin;
|
|
1093
|
+
private binSide;
|
|
1094
|
+
private compile;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
declare class PaneLegendLayer implements Layer {
|
|
1098
|
+
private readonly opts;
|
|
1099
|
+
readonly id: string;
|
|
1100
|
+
dirty: boolean;
|
|
1101
|
+
private value;
|
|
1102
|
+
private batch;
|
|
1103
|
+
private disposed;
|
|
1104
|
+
constructor(opts: {
|
|
1105
|
+
id: string;
|
|
1106
|
+
label: string;
|
|
1107
|
+
labelColor: [number, number, number, number];
|
|
1108
|
+
});
|
|
1109
|
+
setValue(value: string): void;
|
|
1110
|
+
paint(ctx: RenderContext): void;
|
|
1111
|
+
onContextLost(): void;
|
|
1112
|
+
dispose(): void;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
declare class PriceAxisLayer implements Layer {
|
|
1116
|
+
readonly id = "price-axis";
|
|
1117
|
+
dirty: boolean;
|
|
1118
|
+
private batch;
|
|
1119
|
+
private disposed;
|
|
1120
|
+
paint(ctx: RenderContext): void;
|
|
1121
|
+
onContextLost(): void;
|
|
1122
|
+
dispose(): void;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
declare class TimeAxisLayer implements Layer {
|
|
1126
|
+
readonly id = "time-axis";
|
|
1127
|
+
dirty: boolean;
|
|
1128
|
+
private batch;
|
|
1129
|
+
private disposed;
|
|
1130
|
+
paint(ctx: RenderContext): void;
|
|
1131
|
+
onContextLost(): void;
|
|
1132
|
+
dispose(): void;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
interface FootprintLevel {
|
|
1136
|
+
price: number;
|
|
1137
|
+
buyVol: number;
|
|
1138
|
+
sellVol: number;
|
|
1139
|
+
}
|
|
1140
|
+
interface FootprintBar {
|
|
1141
|
+
t: number;
|
|
1142
|
+
open: number;
|
|
1143
|
+
high: number;
|
|
1144
|
+
low: number;
|
|
1145
|
+
close: number;
|
|
1146
|
+
levels: FootprintLevel[];
|
|
1147
|
+
}
|
|
1148
|
+
declare class VolumeFootprintLayer implements Layer {
|
|
1149
|
+
private readonly opts;
|
|
1150
|
+
readonly id = "footprint";
|
|
1151
|
+
dirty: boolean;
|
|
1152
|
+
private bars;
|
|
1153
|
+
private intervalMs;
|
|
1154
|
+
private cmd;
|
|
1155
|
+
private cornerBuf;
|
|
1156
|
+
private xCenterBuf;
|
|
1157
|
+
private halfWidthBuf;
|
|
1158
|
+
private bottomYBuf;
|
|
1159
|
+
private topYBuf;
|
|
1160
|
+
private intensityBuf;
|
|
1161
|
+
private xCenter;
|
|
1162
|
+
private halfWidth;
|
|
1163
|
+
private bottomY;
|
|
1164
|
+
private topY;
|
|
1165
|
+
private intensity;
|
|
1166
|
+
private disposed;
|
|
1167
|
+
constructor(opts?: {
|
|
1168
|
+
alpha?: number;
|
|
1169
|
+
});
|
|
1170
|
+
setBars(bars: FootprintBar[], intervalMs: number): void;
|
|
1171
|
+
clear(): void;
|
|
1172
|
+
paint(ctx: RenderContext): void;
|
|
1173
|
+
onContextLost(): void;
|
|
1174
|
+
dispose(): void;
|
|
1175
|
+
private ensureCapacity;
|
|
1176
|
+
private compile;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
declare class VolumeLayer implements Layer {
|
|
1180
|
+
private readonly store;
|
|
1181
|
+
readonly id = "volume";
|
|
1182
|
+
dirty: boolean;
|
|
1183
|
+
private cmd;
|
|
1184
|
+
private cornerBuf;
|
|
1185
|
+
private xCenterBuf;
|
|
1186
|
+
private halfWidthBuf;
|
|
1187
|
+
private openYBuf;
|
|
1188
|
+
private closeYBuf;
|
|
1189
|
+
private isUpBuf;
|
|
1190
|
+
private xCenter;
|
|
1191
|
+
private halfWidth;
|
|
1192
|
+
private openY;
|
|
1193
|
+
private closeY;
|
|
1194
|
+
private isUp;
|
|
1195
|
+
private disposed;
|
|
1196
|
+
private liveAnim;
|
|
1197
|
+
constructor(store: DataStore);
|
|
1198
|
+
paint(ctx: RenderContext): void;
|
|
1199
|
+
onContextLost(): void;
|
|
1200
|
+
dispose(): void;
|
|
1201
|
+
private estimateInterval;
|
|
1202
|
+
private liveVolume;
|
|
1203
|
+
private sampleLiveVolume;
|
|
1204
|
+
private ensureCapacity;
|
|
1205
|
+
private compile;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
interface VolumeProfileBin {
|
|
1209
|
+
px: number;
|
|
1210
|
+
totalUsd: number;
|
|
1211
|
+
}
|
|
1212
|
+
interface VolumeProfileSnapshot {
|
|
1213
|
+
bins: VolumeProfileBin[];
|
|
1214
|
+
/** Bin width in price units — both edges of each bin sit ± binWidth/2. */
|
|
1215
|
+
binWidth: number;
|
|
1216
|
+
/** Inclusive value-area bounds. Bins in [val, vah] render at full
|
|
1217
|
+
* intensity; bins outside fade. */
|
|
1218
|
+
val: number;
|
|
1219
|
+
vah: number;
|
|
1220
|
+
}
|
|
1221
|
+
declare class VolumeProfileLayer implements Layer {
|
|
1222
|
+
readonly id = "volume-profile";
|
|
1223
|
+
dirty: boolean;
|
|
1224
|
+
private snapshot;
|
|
1225
|
+
private cmd;
|
|
1226
|
+
private cornerBuf;
|
|
1227
|
+
private xCenterBuf;
|
|
1228
|
+
private halfWidthBuf;
|
|
1229
|
+
private bottomYBuf;
|
|
1230
|
+
private topYBuf;
|
|
1231
|
+
private intensityBuf;
|
|
1232
|
+
private xCenter;
|
|
1233
|
+
private halfWidth;
|
|
1234
|
+
private bottomY;
|
|
1235
|
+
private topY;
|
|
1236
|
+
private intensity;
|
|
1237
|
+
private disposed;
|
|
1238
|
+
private readonly widthFraction;
|
|
1239
|
+
constructor(opts?: {
|
|
1240
|
+
alpha?: number;
|
|
1241
|
+
widthFraction?: number;
|
|
1242
|
+
});
|
|
1243
|
+
private alpha;
|
|
1244
|
+
setProfile(snapshot: VolumeProfileSnapshot): void;
|
|
1245
|
+
clear(): void;
|
|
1246
|
+
paint(ctx: RenderContext): void;
|
|
1247
|
+
onContextLost(): void;
|
|
1248
|
+
dispose(): void;
|
|
1249
|
+
private ensureCapacity;
|
|
1250
|
+
private compile;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
interface Candle {
|
|
1254
|
+
time: number;
|
|
1255
|
+
open: number;
|
|
1256
|
+
high: number;
|
|
1257
|
+
low: number;
|
|
1258
|
+
close: number;
|
|
1259
|
+
volume: number;
|
|
1260
|
+
}
|
|
1261
|
+
interface CandleRange {
|
|
1262
|
+
symbol: string;
|
|
1263
|
+
resolution: string;
|
|
1264
|
+
from: number;
|
|
1265
|
+
to: number;
|
|
1266
|
+
}
|
|
1267
|
+
interface FeedUpdate {
|
|
1268
|
+
symbol: string;
|
|
1269
|
+
price: number;
|
|
1270
|
+
size: number;
|
|
1271
|
+
time: number;
|
|
1272
|
+
side?: "buy" | "sell";
|
|
1273
|
+
}
|
|
1274
|
+
type Unsubscribe = () => void;
|
|
1275
|
+
interface DataFeed {
|
|
1276
|
+
fetchCandles(range: CandleRange): Promise<Candle[]>;
|
|
1277
|
+
fetchCandlesColumns?(range: CandleRange): Promise<OhlcvColumns>;
|
|
1278
|
+
subscribe(symbol: string, resolution: string, onUpdate: (u: FeedUpdate) => void): Unsubscribe;
|
|
1279
|
+
subscribeOrderBook?(symbol: string, onBook: (b: OrderBookHeatmapSnapshot) => void): Unsubscribe;
|
|
1280
|
+
fetchFootprint?(symbol: string, opts: {
|
|
1281
|
+
resolution: string;
|
|
1282
|
+
from: number;
|
|
1283
|
+
to: number;
|
|
1284
|
+
intervalMs: number;
|
|
1285
|
+
}): Promise<FootprintBar[]>;
|
|
1286
|
+
fetchVolumeProfile?(symbol: string, opts: {
|
|
1287
|
+
resolution: string;
|
|
1288
|
+
from: number;
|
|
1289
|
+
to: number;
|
|
1290
|
+
intervalMs: number;
|
|
1291
|
+
}): Promise<VolumeProfileSnapshot>;
|
|
1292
|
+
subscribeMarkers?(symbol: string, onMarkers: (m: MarkerSpec[] | {
|
|
1293
|
+
buys?: MarkerSpec[];
|
|
1294
|
+
sells?: MarkerSpec[];
|
|
1295
|
+
}) => void): Unsubscribe;
|
|
1296
|
+
}
|
|
1297
|
+
declare function createDataFeed(impl: DataFeed): DataFeed;
|
|
1298
|
+
declare function composeFeeds(parts: {
|
|
1299
|
+
candles?: DataFeed;
|
|
1300
|
+
orderbook?: DataFeed;
|
|
1301
|
+
footprint?: DataFeed;
|
|
1302
|
+
volumeProfile?: DataFeed;
|
|
1303
|
+
markers?: DataFeed;
|
|
1304
|
+
}): DataFeed;
|
|
1305
|
+
|
|
1306
|
+
type ParamSpec = {
|
|
1307
|
+
type: "number";
|
|
1308
|
+
label: string;
|
|
1309
|
+
default: number;
|
|
1310
|
+
min?: number;
|
|
1311
|
+
max?: number;
|
|
1312
|
+
step?: number;
|
|
1313
|
+
} | {
|
|
1314
|
+
type: "int";
|
|
1315
|
+
label: string;
|
|
1316
|
+
default: number;
|
|
1317
|
+
min?: number;
|
|
1318
|
+
max?: number;
|
|
1319
|
+
} | {
|
|
1320
|
+
type: "bool";
|
|
1321
|
+
label: string;
|
|
1322
|
+
default: boolean;
|
|
1323
|
+
} | {
|
|
1324
|
+
type: "color";
|
|
1325
|
+
label: string;
|
|
1326
|
+
default: string;
|
|
1327
|
+
} | {
|
|
1328
|
+
type: "select";
|
|
1329
|
+
label: string;
|
|
1330
|
+
default: string;
|
|
1331
|
+
options: {
|
|
1332
|
+
value: string;
|
|
1333
|
+
label: string;
|
|
1334
|
+
}[];
|
|
1335
|
+
};
|
|
1336
|
+
type ParamValues<P extends Record<string, ParamSpec>> = {
|
|
1337
|
+
[K in keyof P]: P[K] extends {
|
|
1338
|
+
type: "bool";
|
|
1339
|
+
} ? boolean : P[K] extends {
|
|
1340
|
+
type: "color" | "select";
|
|
1341
|
+
} ? string : number;
|
|
1342
|
+
};
|
|
1343
|
+
interface PlotLine {
|
|
1344
|
+
id: string;
|
|
1345
|
+
data: number[];
|
|
1346
|
+
color?: string;
|
|
1347
|
+
lineWidth?: number;
|
|
1348
|
+
}
|
|
1349
|
+
interface IndicatorResult {
|
|
1350
|
+
lines: PlotLine[];
|
|
1351
|
+
histogram?: number[];
|
|
1352
|
+
bands?: [number, number][];
|
|
1353
|
+
}
|
|
1354
|
+
interface CandleView {
|
|
1355
|
+
readonly time: Float64Array | number[];
|
|
1356
|
+
readonly open: Float64Array | number[];
|
|
1357
|
+
readonly high: Float64Array | number[];
|
|
1358
|
+
readonly low: Float64Array | number[];
|
|
1359
|
+
readonly close: Float64Array | number[];
|
|
1360
|
+
readonly volume: Float64Array | number[];
|
|
1361
|
+
readonly length: number;
|
|
1362
|
+
}
|
|
1363
|
+
interface IndicatorDefinition<P extends Record<string, ParamSpec>> {
|
|
1364
|
+
name: string;
|
|
1365
|
+
overlay: boolean;
|
|
1366
|
+
params: P;
|
|
1367
|
+
compute(candles: CandleView, params: ParamValues<P>): IndicatorResult;
|
|
1368
|
+
}
|
|
1369
|
+
interface IndicatorInstance {
|
|
1370
|
+
name: string;
|
|
1371
|
+
overlay: boolean;
|
|
1372
|
+
params: Record<string, ParamSpec>;
|
|
1373
|
+
values: Record<string, unknown>;
|
|
1374
|
+
compute(candles: CandleView): IndicatorResult;
|
|
1375
|
+
}
|
|
1376
|
+
interface ControlDescriptor {
|
|
1377
|
+
key: string;
|
|
1378
|
+
kind: string;
|
|
1379
|
+
label: string;
|
|
1380
|
+
value: unknown;
|
|
1381
|
+
min?: number;
|
|
1382
|
+
max?: number;
|
|
1383
|
+
step?: number;
|
|
1384
|
+
options?: {
|
|
1385
|
+
value: string;
|
|
1386
|
+
label: string;
|
|
1387
|
+
}[];
|
|
1388
|
+
}
|
|
1389
|
+
declare function defineIndicator<P extends Record<string, ParamSpec>>(def: IndicatorDefinition<P>): (overrides?: Partial<ParamValues<P>>) => IndicatorInstance;
|
|
1390
|
+
declare function getControls(inst: IndicatorInstance): ControlDescriptor[];
|
|
1391
|
+
|
|
1392
|
+
interface ThemeTokens {
|
|
1393
|
+
background?: string;
|
|
1394
|
+
text?: string;
|
|
1395
|
+
grid?: string;
|
|
1396
|
+
border?: string;
|
|
1397
|
+
candleUp?: string;
|
|
1398
|
+
candleDown?: string;
|
|
1399
|
+
volumeUp?: string;
|
|
1400
|
+
volumeDown?: string;
|
|
1401
|
+
crosshair?: string;
|
|
1402
|
+
fontFamily?: string;
|
|
1403
|
+
palette?: Record<string, string>;
|
|
1404
|
+
}
|
|
1405
|
+
declare function resolveTheme(tokens: ThemeTokens, base?: "dark" | "light"): ChartTheme;
|
|
1406
|
+
declare function defineTheme(tokens: ThemeTokens): ChartTheme;
|
|
1407
|
+
declare const presets: {
|
|
1408
|
+
dark: ChartTheme;
|
|
1409
|
+
light: ChartTheme;
|
|
1410
|
+
};
|
|
1411
|
+
|
|
1412
|
+
declare const smaStudy: (overrides?: Partial<ParamValues<{
|
|
1413
|
+
period: {
|
|
1414
|
+
type: "int";
|
|
1415
|
+
label: string;
|
|
1416
|
+
default: number;
|
|
1417
|
+
min: number;
|
|
1418
|
+
max: number;
|
|
1419
|
+
};
|
|
1420
|
+
}>> | undefined) => IndicatorInstance;
|
|
1421
|
+
declare const emaStudy: (overrides?: Partial<ParamValues<{
|
|
1422
|
+
period: {
|
|
1423
|
+
type: "int";
|
|
1424
|
+
label: string;
|
|
1425
|
+
default: number;
|
|
1426
|
+
min: number;
|
|
1427
|
+
max: number;
|
|
1428
|
+
};
|
|
1429
|
+
}>> | undefined) => IndicatorInstance;
|
|
1430
|
+
declare const vwapStudy: (overrides?: Partial<ParamValues<{}>> | undefined) => IndicatorInstance;
|
|
1431
|
+
declare const rsiStudy: (overrides?: Partial<ParamValues<{
|
|
1432
|
+
period: {
|
|
1433
|
+
type: "int";
|
|
1434
|
+
label: string;
|
|
1435
|
+
default: number;
|
|
1436
|
+
min: number;
|
|
1437
|
+
max: number;
|
|
1438
|
+
};
|
|
1439
|
+
}>> | undefined) => IndicatorInstance;
|
|
1440
|
+
declare const macdStudy: (overrides?: Partial<ParamValues<{
|
|
1441
|
+
fast: {
|
|
1442
|
+
type: "int";
|
|
1443
|
+
label: string;
|
|
1444
|
+
default: number;
|
|
1445
|
+
min: number;
|
|
1446
|
+
max: number;
|
|
1447
|
+
};
|
|
1448
|
+
slow: {
|
|
1449
|
+
type: "int";
|
|
1450
|
+
label: string;
|
|
1451
|
+
default: number;
|
|
1452
|
+
min: number;
|
|
1453
|
+
max: number;
|
|
1454
|
+
};
|
|
1455
|
+
signal: {
|
|
1456
|
+
type: "int";
|
|
1457
|
+
label: string;
|
|
1458
|
+
default: number;
|
|
1459
|
+
min: number;
|
|
1460
|
+
max: number;
|
|
1461
|
+
};
|
|
1462
|
+
}>> | undefined) => IndicatorInstance;
|
|
1463
|
+
/** Registry of the built-in study factories, keyed by short name. */
|
|
1464
|
+
declare const builtinStudies: {
|
|
1465
|
+
readonly sma: (overrides?: Partial<ParamValues<{
|
|
1466
|
+
period: {
|
|
1467
|
+
type: "int";
|
|
1468
|
+
label: string;
|
|
1469
|
+
default: number;
|
|
1470
|
+
min: number;
|
|
1471
|
+
max: number;
|
|
1472
|
+
};
|
|
1473
|
+
}>> | undefined) => IndicatorInstance;
|
|
1474
|
+
readonly ema: (overrides?: Partial<ParamValues<{
|
|
1475
|
+
period: {
|
|
1476
|
+
type: "int";
|
|
1477
|
+
label: string;
|
|
1478
|
+
default: number;
|
|
1479
|
+
min: number;
|
|
1480
|
+
max: number;
|
|
1481
|
+
};
|
|
1482
|
+
}>> | undefined) => IndicatorInstance;
|
|
1483
|
+
readonly vwap: (overrides?: Partial<ParamValues<{}>> | undefined) => IndicatorInstance;
|
|
1484
|
+
readonly rsi: (overrides?: Partial<ParamValues<{
|
|
1485
|
+
period: {
|
|
1486
|
+
type: "int";
|
|
1487
|
+
label: string;
|
|
1488
|
+
default: number;
|
|
1489
|
+
min: number;
|
|
1490
|
+
max: number;
|
|
1491
|
+
};
|
|
1492
|
+
}>> | undefined) => IndicatorInstance;
|
|
1493
|
+
readonly macd: (overrides?: Partial<ParamValues<{
|
|
1494
|
+
fast: {
|
|
1495
|
+
type: "int";
|
|
1496
|
+
label: string;
|
|
1497
|
+
default: number;
|
|
1498
|
+
min: number;
|
|
1499
|
+
max: number;
|
|
1500
|
+
};
|
|
1501
|
+
slow: {
|
|
1502
|
+
type: "int";
|
|
1503
|
+
label: string;
|
|
1504
|
+
default: number;
|
|
1505
|
+
min: number;
|
|
1506
|
+
max: number;
|
|
1507
|
+
};
|
|
1508
|
+
signal: {
|
|
1509
|
+
type: "int";
|
|
1510
|
+
label: string;
|
|
1511
|
+
default: number;
|
|
1512
|
+
min: number;
|
|
1513
|
+
max: number;
|
|
1514
|
+
};
|
|
1515
|
+
}>> | undefined) => IndicatorInstance;
|
|
1516
|
+
};
|
|
1517
|
+
|
|
1518
|
+
interface ChartOptions {
|
|
1519
|
+
container: HTMLElement;
|
|
1520
|
+
theme?: ThemeTokens;
|
|
1521
|
+
autosize?: boolean;
|
|
1522
|
+
devicePixelRatio?: number;
|
|
1523
|
+
perf?: boolean;
|
|
1524
|
+
}
|
|
1525
|
+
interface IChartApi {
|
|
1526
|
+
addCandleSeries(): CandleLayer;
|
|
1527
|
+
addVolumeSeries(): VolumeLayer;
|
|
1528
|
+
addFootprintSeries(opts?: FootprintSeriesOptions): FootprintSeriesApi;
|
|
1529
|
+
addVolumeProfile(opts?: VolumeProfileOptions): VolumeProfileApi;
|
|
1530
|
+
addOrderBookHeatmap(opts?: OrderBookHeatmapOptions): OrderBookHeatmapApi;
|
|
1531
|
+
addMarkers(opts?: MarkerSeriesOptions): MarkerSeriesApi;
|
|
1532
|
+
addIndicator(inst: IndicatorInstance): {
|
|
1533
|
+
remove(): void;
|
|
1534
|
+
setParams(v: Record<string, unknown>): void;
|
|
1535
|
+
};
|
|
1536
|
+
addPane(opts?: PaneOptions): PaneApi;
|
|
1537
|
+
setData(feed: DataFeed, opts?: {
|
|
1538
|
+
symbol?: string;
|
|
1539
|
+
resolution?: string;
|
|
1540
|
+
}): void;
|
|
1541
|
+
applyTheme(theme: ThemeTokens): void;
|
|
1542
|
+
resize(w: number, h: number): void;
|
|
1543
|
+
takeScreenshot(): HTMLCanvasElement;
|
|
1544
|
+
subscribeCrosshairMove(cb: (p: unknown) => void): Unsubscribe;
|
|
1545
|
+
on(ev: "contextlost" | "contextrestored", cb: () => void): Unsubscribe;
|
|
1546
|
+
dispose(): void;
|
|
1547
|
+
}
|
|
1548
|
+
interface FootprintSeriesOptions {
|
|
1549
|
+
bars?: FootprintBar[];
|
|
1550
|
+
intervalMs?: number;
|
|
1551
|
+
alpha?: number;
|
|
1552
|
+
}
|
|
1553
|
+
interface FootprintSeriesApi {
|
|
1554
|
+
layer: VolumeFootprintLayer;
|
|
1555
|
+
setBars(bars: FootprintBar[], intervalMs?: number): void;
|
|
1556
|
+
clear(): void;
|
|
1557
|
+
remove(): void;
|
|
1558
|
+
}
|
|
1559
|
+
interface VolumeProfileOptions {
|
|
1560
|
+
profile?: VolumeProfileSnapshot;
|
|
1561
|
+
alpha?: number;
|
|
1562
|
+
widthFraction?: number;
|
|
1563
|
+
}
|
|
1564
|
+
interface VolumeProfileApi {
|
|
1565
|
+
layer: VolumeProfileLayer;
|
|
1566
|
+
setProfile(profile: VolumeProfileSnapshot): void;
|
|
1567
|
+
clear(): void;
|
|
1568
|
+
remove(): void;
|
|
1569
|
+
}
|
|
1570
|
+
interface OrderBookHeatmapOptions {
|
|
1571
|
+
snapshots?: OrderBookHeatmapSnapshot[];
|
|
1572
|
+
alpha?: number;
|
|
1573
|
+
}
|
|
1574
|
+
interface OrderBookHeatmapApi {
|
|
1575
|
+
layer: OrderBookHeatmapLayer;
|
|
1576
|
+
addSnapshot(snapshot: OrderBookHeatmapSnapshot): void;
|
|
1577
|
+
setSnapshots(snapshots: OrderBookHeatmapSnapshot[]): void;
|
|
1578
|
+
clear(): void;
|
|
1579
|
+
remove(): void;
|
|
1580
|
+
}
|
|
1581
|
+
interface MarkerSeriesOptions {
|
|
1582
|
+
id?: string;
|
|
1583
|
+
markers?: MarkerSpec[];
|
|
1584
|
+
buys?: MarkerSpec[];
|
|
1585
|
+
sells?: MarkerSpec[];
|
|
1586
|
+
sideSign?: 1 | -1;
|
|
1587
|
+
color?: RGBA;
|
|
1588
|
+
buyColor?: RGBA;
|
|
1589
|
+
sellColor?: RGBA;
|
|
1590
|
+
logMinUsd?: number;
|
|
1591
|
+
logMaxUsd?: number;
|
|
1592
|
+
}
|
|
1593
|
+
interface MarkerSeriesApi {
|
|
1594
|
+
layers: MarkerLayer[];
|
|
1595
|
+
setMarkers(markers: MarkerSpec[] | {
|
|
1596
|
+
buys?: MarkerSpec[];
|
|
1597
|
+
sells?: MarkerSpec[];
|
|
1598
|
+
}): void;
|
|
1599
|
+
clear(): void;
|
|
1600
|
+
remove(): void;
|
|
1601
|
+
}
|
|
1602
|
+
interface PaneOptions {
|
|
1603
|
+
id?: string;
|
|
1604
|
+
interactive?: boolean;
|
|
1605
|
+
padding?: {
|
|
1606
|
+
top?: number;
|
|
1607
|
+
bottom?: number;
|
|
1608
|
+
};
|
|
1609
|
+
priceRange?: [number, number];
|
|
1610
|
+
}
|
|
1611
|
+
interface PaneApi {
|
|
1612
|
+
pane: Pane;
|
|
1613
|
+
remove(): void;
|
|
1614
|
+
}
|
|
1615
|
+
declare function createChart(options: ChartOptions): IChartApi;
|
|
1616
|
+
|
|
1617
|
+
export { AXIS_LABEL_SCALE, BackgroundLayer, CANDLE_COLOR_THEMES, CHART_COLORS, CHART_LAYERS_BY_INDICATOR, CHART_PANES_BY_INDICATOR, CHART_TYPE_BY_KEY, CHART_TYPE_FAMILIES, CHART_TYPE_OPTIONS, type Candle, type CandleColorThemeKey, type CandleColorThemeOption, CandleLayer, type CandleRange, type CandleView, type ChartColorKey, type ChartOptions, type ChartTheme, type ChartTypeAvailability, type ChartTypeFamily, type ChartTypeKey, type ChartTypeOption, type ControlDescriptor, CrosshairLayer, DEFAULT_CANDLE_COLOR_THEME, DEFAULT_CHANNEL_COLOR, DEFAULT_CHART_TYPE, DEFAULT_COLOR_BY_KIND, DEFAULT_FIB_COLOR, DEFAULT_HLINE_COLOR, DEFAULT_RECT_COLOR, DEFAULT_TREND_COLOR, DEFAULT_VISIBLE_INDICATORS, DEFAULT_VLINE_COLOR, type DataFeed, DataStore, DrawingsLayer, type DrawnChannel, type DrawnFib, type DrawnHLine, type DrawnRect, type DrawnShape, type DrawnTrend, type DrawnVLine, type EngineEvent, EventBus, ExtremaAnnotationLayer, FIB_RATIOS, type FeedUpdate, type FootprintBar, type FootprintLevel, type FootprintSeriesApi, type FootprintSeriesOptions, GlyphAtlas, type GlyphAtlasOptions, type HLineSpec, HorizontalLineLayer, type IChartApi, INDICATOR_BY_KEY, INDICATOR_CATALOG, INDICATOR_CATEGORIES, INDICATOR_LIBRARY, INDICATOR_LIBRARY_GROUPS, type IndicatorAvailability, type IndicatorCategory, type IndicatorDefinition, type IndicatorEntry, type IndicatorInstance, type IndicatorKey, IndicatorLayer, type IndicatorLibraryEntry, type IndicatorLibraryGroup, type IndicatorOptions, type IndicatorResult, type Layer, type LayerStats, MarkerLayer, type MarkerSeriesApi, type MarkerSeriesOptions, type MarkerSpec, type OhlcvBuffer, type OhlcvColumns, OiHistoryLayer, type OiHistoryPoint, type OrderBookHeatmapApi, type OrderBookHeatmapHover, OrderBookHeatmapLayer, type OrderBookHeatmapLevel, type OrderBookHeatmapOptions, type OrderBookHeatmapSnapshot, type OrderBookHeatmapSummary, PLOT_GUTTER, PREVIEW_SHAPE_ID, type Padding, Pane, type PaneApi, type PaneBounds, PaneLegendLayer, type PaneOptions, type PanePadding, type ParamSpec, type ParamValues, PerfReporter, type PlotLine, PriceAxisLayer, RGB, type RGBA, type RangeFetchRequest, type RangeFetcher, type RawCandleRow, type RenderContext, RendererGL, type RendererGLOptions, Scene, type SmoothCrosshairAxisResult, TextBatch, type ThemeTokens, TimeAxisLayer, type TimeSnap, UI, type Unsubscribe, Viewport, VolumeFootprintLayer, VolumeLayer, type VolumeProfileApi, type VolumeProfileBin, VolumeProfileLayer, type VolumeProfileOptions, type VolumeProfileSnapshot, alpha, builtinStudies, chartTypeAvailabilityLabel, composeFeeds, createChart, createDataFeed, darkTheme, defineIndicator, defineTheme, emaStudy, formatHoverPrice, formatPrice, formatVolume, getControls, indicatorAvailability, indicatorAvailabilityLabel, indicatorCanToggle, isCandleColorThemeKey, isChartTypeKey, isChartTypeSelectable, loadDrawings, macdStudy, makeShapeId, niceStep, paneLegendValue, presets, resolveTheme, rsiStudy, saveDrawings, smaStudy, smoothCrosshairAxis, stepForRange, swatchOf, vwapStudy };
|