@qfo/qfchart 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.
@@ -0,0 +1,465 @@
1
+ import * as echarts from 'echarts';
2
+
3
+ type EventType = 'mouse:down' | 'mouse:move' | 'mouse:up' | 'mouse:click' | 'chart:resize' | 'chart:dataZoom' | 'chart:updated' | 'plugin:activated' | 'plugin:deactivated' | 'drawing:hover' | 'drawing:mouseout' | 'drawing:mousedown' | 'drawing:click' | 'drawing:point:hover' | 'drawing:point:mouseout' | 'drawing:point:mousedown' | 'drawing:point:click' | 'drawing:selected' | 'drawing:deselected' | 'drawing:deleted';
4
+ type EventHandler<T = any> = (payload: T) => void;
5
+ declare class EventBus {
6
+ private handlers;
7
+ on<T = any>(event: EventType, handler: EventHandler<T>): void;
8
+ off<T = any>(event: EventType, handler: EventHandler<T>): void;
9
+ emit<T = any>(event: EventType, payload?: T): void;
10
+ clear(): void;
11
+ }
12
+
13
+ interface OHLCV {
14
+ time: number;
15
+ open: number;
16
+ high: number;
17
+ low: number;
18
+ close: number;
19
+ volume: number;
20
+ }
21
+ interface IndicatorPoint {
22
+ time: number;
23
+ value: number | null;
24
+ options?: {
25
+ color?: string;
26
+ };
27
+ }
28
+ type IndicatorStyle = 'line' | 'columns' | 'histogram' | 'circles' | 'cross' | 'background';
29
+ interface IndicatorOptions {
30
+ style: IndicatorStyle;
31
+ color: string;
32
+ linewidth?: number;
33
+ }
34
+ interface IndicatorPlot {
35
+ data: IndicatorPoint[];
36
+ options: IndicatorOptions;
37
+ }
38
+ interface Indicator$1 {
39
+ id: string;
40
+ plots: {
41
+ [name: string]: IndicatorPlot;
42
+ };
43
+ paneIndex: number;
44
+ height?: number;
45
+ collapsed?: boolean;
46
+ titleColor?: string;
47
+ controls?: {
48
+ collapse?: boolean;
49
+ maximize?: boolean;
50
+ };
51
+ }
52
+ interface QFChartOptions {
53
+ title?: string;
54
+ titleColor?: string;
55
+ backgroundColor?: string;
56
+ upColor?: string;
57
+ downColor?: string;
58
+ fontColor?: string;
59
+ fontFamily?: string;
60
+ padding?: number;
61
+ height?: string | number;
62
+ controls?: {
63
+ collapse?: boolean;
64
+ maximize?: boolean;
65
+ fullscreen?: boolean;
66
+ };
67
+ dataZoom?: {
68
+ visible?: boolean;
69
+ position?: 'top' | 'bottom';
70
+ height?: number;
71
+ start?: number;
72
+ end?: number;
73
+ };
74
+ databox?: {
75
+ position: 'floating' | 'left' | 'right';
76
+ };
77
+ layout?: {
78
+ mainPaneHeight: string;
79
+ gap: number;
80
+ };
81
+ watermark?: boolean;
82
+ }
83
+ interface Coordinate {
84
+ x: number;
85
+ y: number;
86
+ }
87
+ interface DataCoordinate {
88
+ timeIndex: number;
89
+ value: number;
90
+ paneIndex?: number;
91
+ }
92
+ interface ChartContext {
93
+ getChart(): any;
94
+ getMarketData(): OHLCV[];
95
+ getTimeToIndex(): Map<number, number>;
96
+ getOptions(): QFChartOptions;
97
+ events: EventBus;
98
+ coordinateConversion: {
99
+ pixelToData: (point: Coordinate) => DataCoordinate | null;
100
+ dataToPixel: (point: DataCoordinate) => Coordinate | null;
101
+ };
102
+ disableTools(): void;
103
+ setZoom(start: number, end: number): void;
104
+ addDrawing(drawing: DrawingElement): void;
105
+ removeDrawing(id: string): void;
106
+ getDrawing(id: string): DrawingElement | undefined;
107
+ updateDrawing(drawing: DrawingElement): void;
108
+ lockChart(): void;
109
+ unlockChart(): void;
110
+ }
111
+ type DrawingType = 'line' | 'fibonacci';
112
+ interface DrawingElement {
113
+ id: string;
114
+ type: DrawingType;
115
+ points: DataCoordinate[];
116
+ paneIndex?: number;
117
+ style?: {
118
+ color?: string;
119
+ lineWidth?: number;
120
+ };
121
+ }
122
+ interface PluginConfig {
123
+ id: string;
124
+ name?: string;
125
+ icon?: string;
126
+ hotkey?: string;
127
+ }
128
+ interface Plugin {
129
+ id: string;
130
+ name?: string;
131
+ icon?: string;
132
+ init(context: ChartContext): void;
133
+ activate?(): void;
134
+ deactivate?(): void;
135
+ destroy?(): void;
136
+ }
137
+
138
+ declare class Indicator implements Indicator$1 {
139
+ id: string;
140
+ plots: {
141
+ [name: string]: IndicatorPlot;
142
+ };
143
+ paneIndex: number;
144
+ height?: number;
145
+ collapsed: boolean;
146
+ titleColor?: string;
147
+ controls?: {
148
+ collapse?: boolean;
149
+ maximize?: boolean;
150
+ };
151
+ constructor(id: string, plots: {
152
+ [name: string]: IndicatorPlot;
153
+ }, paneIndex: number, options?: {
154
+ height?: number;
155
+ collapsed?: boolean;
156
+ titleColor?: string;
157
+ controls?: {
158
+ collapse?: boolean;
159
+ maximize?: boolean;
160
+ };
161
+ });
162
+ toggleCollapse(): void;
163
+ isVisible(): boolean;
164
+ /**
165
+ * Update indicator data incrementally by merging new points
166
+ *
167
+ * @param plots - New plots data to merge (same structure as constructor)
168
+ *
169
+ * @remarks
170
+ * This method merges new indicator data with existing data by timestamp.
171
+ * - New timestamps are added
172
+ * - Existing timestamps are updated with new values
173
+ * - All data is automatically sorted by time after merge
174
+ *
175
+ * **Important**: This method only updates the indicator's internal data structure.
176
+ * To see the changes reflected in the chart, you MUST call `chart.updateData()`
177
+ * after updating indicator data.
178
+ *
179
+ * **Usage Pattern**:
180
+ * ```typescript
181
+ * // 1. Update indicator data first
182
+ * indicator.updateData({
183
+ * macd: { data: [{ time: 1234567890, value: 150 }], options: { style: 'line', color: '#2962FF' } }
184
+ * });
185
+ *
186
+ * // 2. Then update chart data to trigger re-render
187
+ * chart.updateData([
188
+ * { time: 1234567890, open: 100, high: 105, low: 99, close: 103, volume: 1000 }
189
+ * ]);
190
+ * ```
191
+ *
192
+ * **Note**: If you update indicator data without corresponding market data changes,
193
+ * this typically indicates a recalculation scenario. In normal workflows, indicator
194
+ * values are derived from market data, so indicator updates should correspond to
195
+ * new or modified market bars.
196
+ */
197
+ updateData(plots: {
198
+ [name: string]: IndicatorPlot;
199
+ }): void;
200
+ }
201
+
202
+ declare class QFChart implements ChartContext {
203
+ private chart;
204
+ private options;
205
+ private marketData;
206
+ private indicators;
207
+ private timeToIndex;
208
+ private pluginManager;
209
+ private drawingEditor;
210
+ events: EventBus;
211
+ private isMainCollapsed;
212
+ private maximizedPaneId;
213
+ private selectedDrawingId;
214
+ private drawings;
215
+ coordinateConversion: {
216
+ pixelToData: (point: {
217
+ x: number;
218
+ y: number;
219
+ }) => {
220
+ timeIndex: number;
221
+ value: number;
222
+ paneIndex: number;
223
+ } | null;
224
+ dataToPixel: (point: {
225
+ timeIndex: number;
226
+ value: number;
227
+ paneIndex?: number;
228
+ }) => {
229
+ x: number;
230
+ y: number;
231
+ } | null;
232
+ };
233
+ private readonly upColor;
234
+ private readonly downColor;
235
+ private readonly defaultPadding;
236
+ private padding;
237
+ private dataIndexOffset;
238
+ private rootContainer;
239
+ private layoutContainer;
240
+ private toolbarContainer;
241
+ private leftSidebar;
242
+ private rightSidebar;
243
+ private chartContainer;
244
+ constructor(container: HTMLElement, options?: QFChartOptions);
245
+ private onKeyDown;
246
+ private onFullscreenChange;
247
+ private bindDrawingEvents;
248
+ getChart(): echarts.ECharts;
249
+ getMarketData(): OHLCV[];
250
+ getTimeToIndex(): Map<number, number>;
251
+ getOptions(): QFChartOptions;
252
+ disableTools(): void;
253
+ registerPlugin(plugin: Plugin): void;
254
+ addDrawing(drawing: DrawingElement): void;
255
+ removeDrawing(id: string): void;
256
+ getDrawing(id: string): DrawingElement | undefined;
257
+ updateDrawing(drawing: DrawingElement): void;
258
+ private isLocked;
259
+ private lockedState;
260
+ lockChart(): void;
261
+ unlockChart(): void;
262
+ setZoom(start: number, end: number): void;
263
+ setMarketData(data: OHLCV[]): void;
264
+ /**
265
+ * Update market data incrementally without full re-render
266
+ * Merges new/updated OHLCV data with existing data by timestamp
267
+ *
268
+ * @param data - Array of OHLCV data to merge
269
+ *
270
+ * @remarks
271
+ * **Performance Optimization**: This method only triggers a chart update if the data array contains
272
+ * new or modified bars. If an empty array is passed, no update occurs (expected behavior).
273
+ *
274
+ * **Usage Pattern for Updating Indicators**:
275
+ * When updating both market data and indicators, follow this order:
276
+ *
277
+ * 1. Update indicator data first using `indicator.updateData(plots)`
278
+ * 2. Then call `chart.updateData(newBars)` with the new/modified market data
279
+ *
280
+ * The chart update will trigger a re-render that includes the updated indicator data.
281
+ *
282
+ * **Important**: If you update indicator data without updating market data (e.g., recalculation
283
+ * with same bars), you must still call `chart.updateData([...])` with at least one bar
284
+ * to trigger the re-render. Calling with an empty array will NOT trigger an update.
285
+ *
286
+ * @example
287
+ * ```typescript
288
+ * // Step 1: Update indicator data
289
+ * macdIndicator.updateData({
290
+ * macd: { data: [{ time: 1234567890, value: 150 }], options: { style: 'line', color: '#2962FF' } }
291
+ * });
292
+ *
293
+ * // Step 2: Update market data (triggers re-render with new indicator data)
294
+ * chart.updateData([
295
+ * { time: 1234567890, open: 100, high: 105, low: 99, close: 103, volume: 1000 }
296
+ * ]);
297
+ * ```
298
+ *
299
+ * @example
300
+ * ```typescript
301
+ * // If only updating existing bar (e.g., real-time tick updates):
302
+ * const lastBar = { ...existingBar, close: newPrice, high: Math.max(existingBar.high, newPrice) };
303
+ * chart.updateData([lastBar]); // Updates by timestamp
304
+ * ```
305
+ */
306
+ updateData(data: OHLCV[]): void;
307
+ addIndicator(id: string, plots: {
308
+ [name: string]: IndicatorPlot;
309
+ }, options?: {
310
+ isOverlay?: boolean;
311
+ height?: number;
312
+ titleColor?: string;
313
+ controls?: {
314
+ collapse?: boolean;
315
+ maximize?: boolean;
316
+ };
317
+ }): Indicator;
318
+ setIndicator(id: string, plot: IndicatorPlot, isOverlay?: boolean): void;
319
+ removeIndicator(id: string): void;
320
+ toggleIndicator(id: string, action?: 'collapse' | 'maximize' | 'fullscreen'): void;
321
+ resize(): void;
322
+ destroy(): void;
323
+ private rebuildTimeIndex;
324
+ private render;
325
+ }
326
+
327
+ declare abstract class AbstractPlugin implements Plugin {
328
+ id: string;
329
+ name?: string;
330
+ icon?: string;
331
+ protected context: ChartContext;
332
+ private eventListeners;
333
+ constructor(config: PluginConfig);
334
+ init(context: ChartContext): void;
335
+ /**
336
+ * Lifecycle hook called after context is initialized.
337
+ * Override this instead of init().
338
+ */
339
+ protected onInit(): void;
340
+ activate(): void;
341
+ /**
342
+ * Lifecycle hook called when the plugin is activated.
343
+ */
344
+ protected onActivate(): void;
345
+ deactivate(): void;
346
+ /**
347
+ * Lifecycle hook called when the plugin is deactivated.
348
+ */
349
+ protected onDeactivate(): void;
350
+ destroy(): void;
351
+ /**
352
+ * Lifecycle hook called when the plugin is destroyed.
353
+ */
354
+ protected onDestroy(): void;
355
+ /**
356
+ * Register an event listener that will be automatically cleaned up on destroy.
357
+ */
358
+ protected on(event: EventType, handler: EventHandler): void;
359
+ /**
360
+ * Remove a specific event listener.
361
+ */
362
+ protected off(event: EventType, handler: EventHandler): void;
363
+ /**
364
+ * Remove all listeners registered by this plugin.
365
+ */
366
+ protected removeAllListeners(): void;
367
+ /**
368
+ * Access to the ECharts instance.
369
+ */
370
+ protected get chart(): any;
371
+ /**
372
+ * Access to market data.
373
+ */
374
+ protected get marketData(): OHLCV[];
375
+ }
376
+
377
+ declare class MeasureTool extends AbstractPlugin {
378
+ private zr;
379
+ private state;
380
+ private startPoint;
381
+ private endPoint;
382
+ private group;
383
+ private rect;
384
+ private labelRect;
385
+ private labelText;
386
+ private lineV;
387
+ private lineH;
388
+ private arrowStart;
389
+ private arrowEnd;
390
+ constructor(options: {
391
+ name?: string;
392
+ icon?: string;
393
+ });
394
+ protected onInit(): void;
395
+ protected onActivate(): void;
396
+ protected onDeactivate(): void;
397
+ protected onDestroy(): void;
398
+ private onMouseDown;
399
+ private onChartInteraction;
400
+ private onClick;
401
+ private enableClearListeners;
402
+ private clearHandlers;
403
+ private disableClearListeners;
404
+ private onMouseMove;
405
+ private initGraphic;
406
+ private removeGraphic;
407
+ private updateGraphic;
408
+ }
409
+
410
+ declare class LineTool extends AbstractPlugin {
411
+ private zr;
412
+ private state;
413
+ private startPoint;
414
+ private endPoint;
415
+ private group;
416
+ private line;
417
+ private startCircle;
418
+ private endCircle;
419
+ constructor(options: {
420
+ name?: string;
421
+ icon?: string;
422
+ });
423
+ protected onInit(): void;
424
+ protected onActivate(): void;
425
+ protected onDeactivate(): void;
426
+ protected onDestroy(): void;
427
+ private onMouseDown;
428
+ private onChartInteraction;
429
+ private onClick;
430
+ private saveDataCoordinates;
431
+ private updateGraphicFromData;
432
+ private enableClearListeners;
433
+ private clearHandlers;
434
+ private disableClearListeners;
435
+ private onMouseMove;
436
+ private initGraphic;
437
+ private removeGraphic;
438
+ private updateGraphic;
439
+ }
440
+
441
+ declare class FibonacciTool extends AbstractPlugin {
442
+ private startPoint;
443
+ private endPoint;
444
+ private state;
445
+ private graphicGroup;
446
+ private readonly levels;
447
+ private readonly colors;
448
+ constructor(options?: {
449
+ name?: string;
450
+ icon?: string;
451
+ });
452
+ onActivate(): void;
453
+ onDeactivate(): void;
454
+ private bindEvents;
455
+ private unbindEvents;
456
+ private onClick;
457
+ private onMouseMove;
458
+ private initGraphic;
459
+ private removeGraphic;
460
+ private updateGraphic;
461
+ private saveDrawing;
462
+ }
463
+
464
+ export { AbstractPlugin, FibonacciTool, LineTool, MeasureTool, QFChart };
465
+ export type { ChartContext, Coordinate, DataCoordinate, DrawingElement, DrawingType, Indicator$1 as Indicator, IndicatorOptions, IndicatorPlot, IndicatorPoint, IndicatorStyle, OHLCV, Plugin, PluginConfig, QFChartOptions };