@qfo/qfchart 0.6.6 → 0.6.8
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.d.ts +1 -1
- package/dist/qfchart.min.browser.js +16 -16
- package/dist/qfchart.min.es.js +16 -16
- package/package.json +81 -81
- package/src/QFChart.ts +1434 -1434
- package/src/components/SeriesBuilder.ts +251 -250
- package/src/components/SeriesRendererFactory.ts +42 -36
- package/src/components/renderers/DrawingLineRenderer.ts +188 -0
- package/src/components/renderers/FillRenderer.ts +99 -99
- package/src/components/renderers/LabelRenderer.ts +274 -0
- package/src/components/renderers/LinefillRenderer.ts +167 -0
- package/src/components/renderers/SeriesRenderer.ts +21 -20
- package/src/types.ts +207 -205
- package/src/utils/ShapeUtils.ts +148 -140
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { SeriesRenderer, RenderContext } from './SeriesRenderer';
|
|
2
|
+
import { ColorUtils } from '../../utils/ColorUtils';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Renderer for Pine Script linefill.* drawing objects.
|
|
6
|
+
* Each linefill fills the area between two line objects as a polygon.
|
|
7
|
+
*
|
|
8
|
+
* Style name: 'linefill'
|
|
9
|
+
*/
|
|
10
|
+
export class LinefillRenderer implements SeriesRenderer {
|
|
11
|
+
render(context: RenderContext): any {
|
|
12
|
+
const { seriesName, xAxisIndex, yAxisIndex, dataArray, dataIndexOffset } = context;
|
|
13
|
+
const offset = dataIndexOffset || 0;
|
|
14
|
+
|
|
15
|
+
// Collect all non-deleted linefill objects from the sparse dataArray.
|
|
16
|
+
// Same aggregation pattern as DrawingLineRenderer — objects are stored
|
|
17
|
+
// as an array in a single data entry.
|
|
18
|
+
const fillObjects: any[] = [];
|
|
19
|
+
const fillData: number[][] = [];
|
|
20
|
+
|
|
21
|
+
for (let i = 0; i < dataArray.length; i++) {
|
|
22
|
+
const val = dataArray[i];
|
|
23
|
+
if (!val) continue;
|
|
24
|
+
|
|
25
|
+
const items = Array.isArray(val) ? val : [val];
|
|
26
|
+
for (const lf of items) {
|
|
27
|
+
if (!lf || typeof lf !== 'object' || lf._deleted) continue;
|
|
28
|
+
|
|
29
|
+
const line1 = lf.line1;
|
|
30
|
+
const line2 = lf.line2;
|
|
31
|
+
if (!line1 || !line2 || line1._deleted || line2._deleted) continue;
|
|
32
|
+
|
|
33
|
+
fillObjects.push(lf);
|
|
34
|
+
|
|
35
|
+
// Store all 8 coordinates for the two lines
|
|
36
|
+
const xOff1 = line1.xloc === 'bar_index' ? offset : 0;
|
|
37
|
+
const xOff2 = line2.xloc === 'bar_index' ? offset : 0;
|
|
38
|
+
fillData.push([
|
|
39
|
+
line1.x1 + xOff1, line1.y1,
|
|
40
|
+
line1.x2 + xOff1, line1.y2,
|
|
41
|
+
line2.x1 + xOff2, line2.y1,
|
|
42
|
+
line2.x2 + xOff2, line2.y2,
|
|
43
|
+
]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (fillData.length === 0) {
|
|
48
|
+
return { name: seriesName, type: 'custom', xAxisIndex, yAxisIndex, data: [], silent: true };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
name: seriesName,
|
|
53
|
+
type: 'custom',
|
|
54
|
+
xAxisIndex,
|
|
55
|
+
yAxisIndex,
|
|
56
|
+
renderItem: (params: any, api: any) => {
|
|
57
|
+
const idx = params.dataIndex;
|
|
58
|
+
const lf = fillObjects[idx];
|
|
59
|
+
if (!lf || lf._deleted) return;
|
|
60
|
+
|
|
61
|
+
const line1 = lf.line1;
|
|
62
|
+
const line2 = lf.line2;
|
|
63
|
+
if (!line1 || !line2 || line1._deleted || line2._deleted) return;
|
|
64
|
+
|
|
65
|
+
// Get data values: line1 start, line1 end, line2 start, line2 end
|
|
66
|
+
const l1x1 = api.value(0);
|
|
67
|
+
const l1y1 = api.value(1);
|
|
68
|
+
const l1x2 = api.value(2);
|
|
69
|
+
const l1y2 = api.value(3);
|
|
70
|
+
const l2x1 = api.value(4);
|
|
71
|
+
const l2y1 = api.value(5);
|
|
72
|
+
const l2x2 = api.value(6);
|
|
73
|
+
const l2y2 = api.value(7);
|
|
74
|
+
|
|
75
|
+
// Convert to pixel coordinates
|
|
76
|
+
let p1Start = api.coord([l1x1, l1y1]);
|
|
77
|
+
let p1End = api.coord([l1x2, l1y2]);
|
|
78
|
+
let p2Start = api.coord([l2x1, l2y1]);
|
|
79
|
+
let p2End = api.coord([l2x2, l2y2]);
|
|
80
|
+
|
|
81
|
+
// Handle line extensions — if lines are extended, extend the fill too
|
|
82
|
+
const extend1 = line1.extend || 'none';
|
|
83
|
+
const extend2 = line2.extend || 'none';
|
|
84
|
+
if (extend1 !== 'none' || extend2 !== 'none') {
|
|
85
|
+
const cs = params.coordSys;
|
|
86
|
+
const left = cs.x;
|
|
87
|
+
const right = cs.x + cs.width;
|
|
88
|
+
const top = cs.y;
|
|
89
|
+
const bottom = cs.y + cs.height;
|
|
90
|
+
|
|
91
|
+
if (extend1 !== 'none') {
|
|
92
|
+
[p1Start, p1End] = this.extendLine(p1Start, p1End, extend1, left, right, top, bottom);
|
|
93
|
+
}
|
|
94
|
+
if (extend2 !== 'none') {
|
|
95
|
+
[p2Start, p2End] = this.extendLine(p2Start, p2End, extend2, left, right, top, bottom);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Parse color
|
|
100
|
+
const { color: fillColor, opacity: fillOpacity } = ColorUtils.parseColor(lf.color || 'rgba(128, 128, 128, 0.2)');
|
|
101
|
+
|
|
102
|
+
// Create a polygon: line1.start → line1.end → line2.end → line2.start
|
|
103
|
+
return {
|
|
104
|
+
type: 'polygon',
|
|
105
|
+
shape: {
|
|
106
|
+
points: [
|
|
107
|
+
p1Start,
|
|
108
|
+
p1End,
|
|
109
|
+
p2End,
|
|
110
|
+
p2Start,
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
style: {
|
|
114
|
+
fill: fillColor,
|
|
115
|
+
opacity: fillOpacity,
|
|
116
|
+
},
|
|
117
|
+
silent: true,
|
|
118
|
+
};
|
|
119
|
+
},
|
|
120
|
+
data: fillData,
|
|
121
|
+
z: 10, // Behind lines (z=15) but above other elements
|
|
122
|
+
silent: true,
|
|
123
|
+
emphasis: { disabled: true },
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private extendLine(
|
|
128
|
+
p1: number[],
|
|
129
|
+
p2: number[],
|
|
130
|
+
extend: string,
|
|
131
|
+
left: number,
|
|
132
|
+
right: number,
|
|
133
|
+
top: number,
|
|
134
|
+
bottom: number,
|
|
135
|
+
): [number[], number[]] {
|
|
136
|
+
const dx = p2[0] - p1[0];
|
|
137
|
+
const dy = p2[1] - p1[1];
|
|
138
|
+
|
|
139
|
+
if (dx === 0 && dy === 0) return [p1, p2];
|
|
140
|
+
|
|
141
|
+
const extendPoint = (origin: number[], dir: number[]): number[] => {
|
|
142
|
+
let tMax = Infinity;
|
|
143
|
+
if (dir[0] !== 0) {
|
|
144
|
+
const tx = dir[0] > 0 ? (right - origin[0]) / dir[0] : (left - origin[0]) / dir[0];
|
|
145
|
+
tMax = Math.min(tMax, tx);
|
|
146
|
+
}
|
|
147
|
+
if (dir[1] !== 0) {
|
|
148
|
+
const ty = dir[1] > 0 ? (bottom - origin[1]) / dir[1] : (top - origin[1]) / dir[1];
|
|
149
|
+
tMax = Math.min(tMax, ty);
|
|
150
|
+
}
|
|
151
|
+
if (!isFinite(tMax)) tMax = 0;
|
|
152
|
+
return [origin[0] + tMax * dir[0], origin[1] + tMax * dir[1]];
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
let newP1 = p1;
|
|
156
|
+
let newP2 = p2;
|
|
157
|
+
|
|
158
|
+
if (extend === 'right' || extend === 'both') {
|
|
159
|
+
newP2 = extendPoint(p1, [dx, dy]);
|
|
160
|
+
}
|
|
161
|
+
if (extend === 'left' || extend === 'both') {
|
|
162
|
+
newP1 = extendPoint(p2, [-dx, -dy]);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return [newP1, newP2];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import { IndicatorPlot, OHLCV } from '../../types';
|
|
2
|
-
|
|
3
|
-
export interface RenderContext {
|
|
4
|
-
seriesName: string;
|
|
5
|
-
xAxisIndex: number;
|
|
6
|
-
yAxisIndex: number;
|
|
7
|
-
dataArray: any[];
|
|
8
|
-
colorArray: any[];
|
|
9
|
-
optionsArray: any[];
|
|
10
|
-
plotOptions: any;
|
|
11
|
-
candlestickData?: OHLCV[]; // For shape positioning
|
|
12
|
-
plotDataArrays?: Map<string, number[]>; // For fill plots
|
|
13
|
-
indicatorId?: string;
|
|
14
|
-
plotName?: string;
|
|
15
|
-
indicator?: any; // Reference to parent indicator object if needed
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
import { IndicatorPlot, OHLCV } from '../../types';
|
|
2
|
+
|
|
3
|
+
export interface RenderContext {
|
|
4
|
+
seriesName: string;
|
|
5
|
+
xAxisIndex: number;
|
|
6
|
+
yAxisIndex: number;
|
|
7
|
+
dataArray: any[];
|
|
8
|
+
colorArray: any[];
|
|
9
|
+
optionsArray: any[];
|
|
10
|
+
plotOptions: any;
|
|
11
|
+
candlestickData?: OHLCV[]; // For shape positioning
|
|
12
|
+
plotDataArrays?: Map<string, number[]>; // For fill plots
|
|
13
|
+
indicatorId?: string;
|
|
14
|
+
plotName?: string;
|
|
15
|
+
indicator?: any; // Reference to parent indicator object if needed
|
|
16
|
+
dataIndexOffset?: number; // Padding offset for converting bar_index to ECharts index
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface SeriesRenderer {
|
|
20
|
+
render(context: RenderContext): any;
|
|
21
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,205 +1,207 @@
|
|
|
1
|
-
import { EventBus } from './utils/EventBus';
|
|
2
|
-
|
|
3
|
-
export interface OHLCV {
|
|
4
|
-
time: number;
|
|
5
|
-
open: number;
|
|
6
|
-
high: number;
|
|
7
|
-
low: number;
|
|
8
|
-
close: number;
|
|
9
|
-
volume: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface IndicatorPoint {
|
|
13
|
-
time: number;
|
|
14
|
-
value: number | number[] | null;
|
|
15
|
-
options?: {
|
|
16
|
-
color?: string;
|
|
17
|
-
offset?: number;
|
|
18
|
-
wickcolor?: string;
|
|
19
|
-
bordercolor?: string;
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type IndicatorStyle =
|
|
24
|
-
| 'line'
|
|
25
|
-
| 'step'
|
|
26
|
-
| 'columns'
|
|
27
|
-
| 'histogram'
|
|
28
|
-
| 'circles'
|
|
29
|
-
| 'cross'
|
|
30
|
-
| 'background'
|
|
31
|
-
| 'shape'
|
|
32
|
-
| 'char'
|
|
33
|
-
| 'bar'
|
|
34
|
-
| 'candle'
|
|
35
|
-
| 'barcolor'
|
|
36
|
-
| 'fill'
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
export
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
1
|
+
import { EventBus } from './utils/EventBus';
|
|
2
|
+
|
|
3
|
+
export interface OHLCV {
|
|
4
|
+
time: number;
|
|
5
|
+
open: number;
|
|
6
|
+
high: number;
|
|
7
|
+
low: number;
|
|
8
|
+
close: number;
|
|
9
|
+
volume: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface IndicatorPoint {
|
|
13
|
+
time: number;
|
|
14
|
+
value: number | number[] | null;
|
|
15
|
+
options?: {
|
|
16
|
+
color?: string;
|
|
17
|
+
offset?: number;
|
|
18
|
+
wickcolor?: string;
|
|
19
|
+
bordercolor?: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type IndicatorStyle =
|
|
24
|
+
| 'line'
|
|
25
|
+
| 'step'
|
|
26
|
+
| 'columns'
|
|
27
|
+
| 'histogram'
|
|
28
|
+
| 'circles'
|
|
29
|
+
| 'cross'
|
|
30
|
+
| 'background'
|
|
31
|
+
| 'shape'
|
|
32
|
+
| 'char'
|
|
33
|
+
| 'bar'
|
|
34
|
+
| 'candle'
|
|
35
|
+
| 'barcolor'
|
|
36
|
+
| 'fill'
|
|
37
|
+
| 'label'
|
|
38
|
+
| 'drawing_line';
|
|
39
|
+
|
|
40
|
+
export interface IndicatorOptions {
|
|
41
|
+
style: IndicatorStyle;
|
|
42
|
+
color: string;
|
|
43
|
+
overlay?: boolean; // Override indicator-level overlay setting for this specific plot
|
|
44
|
+
offset?: number;
|
|
45
|
+
linewidth?: number;
|
|
46
|
+
smooth?: boolean;
|
|
47
|
+
shape?: string;
|
|
48
|
+
size?: string;
|
|
49
|
+
text?: string;
|
|
50
|
+
textcolor?: string;
|
|
51
|
+
location?: string;
|
|
52
|
+
width?: number;
|
|
53
|
+
height?: number;
|
|
54
|
+
wickcolor?: string;
|
|
55
|
+
bordercolor?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface IndicatorPlot {
|
|
59
|
+
data?: IndicatorPoint[]; // Optional for fill plots
|
|
60
|
+
options: IndicatorOptions;
|
|
61
|
+
plot1?: string; // For fill plots: reference to first plot ID
|
|
62
|
+
plot2?: string; // For fill plots: reference to second plot ID
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// A collection of plots that make up a single indicator (e.g. MACD has macd line, signal line, histogram)
|
|
66
|
+
export interface Indicator {
|
|
67
|
+
id: string;
|
|
68
|
+
plots: { [name: string]: IndicatorPlot };
|
|
69
|
+
paneIndex: number;
|
|
70
|
+
height?: number; // Desired height in percentage (e.g. 15 for 15%)
|
|
71
|
+
collapsed?: boolean;
|
|
72
|
+
titleColor?: string;
|
|
73
|
+
controls?: {
|
|
74
|
+
collapse?: boolean;
|
|
75
|
+
maximize?: boolean;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface QFChartOptions {
|
|
80
|
+
title?: string; // Title for the main chart (e.g. "BTC/USDT")
|
|
81
|
+
titleColor?: string;
|
|
82
|
+
backgroundColor?: string;
|
|
83
|
+
upColor?: string;
|
|
84
|
+
downColor?: string;
|
|
85
|
+
fontColor?: string;
|
|
86
|
+
fontFamily?: string;
|
|
87
|
+
padding?: number; // Horizontal padding (empty candles on sides), defaults to 0.2
|
|
88
|
+
yAxisPadding?: number; // Vertical Y-axis padding in percentage (e.g., 5 = 5% padding), defaults to 5
|
|
89
|
+
yAxisMin?: number | 'auto'; // Fixed minimum value for main Y-axis, or 'auto' for dynamic
|
|
90
|
+
yAxisMax?: number | 'auto'; // Fixed maximum value for main Y-axis, or 'auto' for dynamic
|
|
91
|
+
yAxisLabelFormatter?: (value: number) => string; // Custom formatter function for Y-axis labels
|
|
92
|
+
yAxisDecimalPlaces?: number; // Number of decimal places for Y-axis labels. If undefined, auto-detected from data.
|
|
93
|
+
lastPriceLine?: {
|
|
94
|
+
// Configuration for the horizontal line showing the last price
|
|
95
|
+
visible?: boolean;
|
|
96
|
+
color?: string; // Defaults to current candle color or '#fff'
|
|
97
|
+
lineStyle?: 'solid' | 'dashed' | 'dotted'; // Defaults to 'dashed'
|
|
98
|
+
showCountdown?: boolean; // Show countdown to bar close
|
|
99
|
+
};
|
|
100
|
+
interval?: number; // Bar interval in milliseconds (required for countdown)
|
|
101
|
+
height?: string | number;
|
|
102
|
+
controls?: {
|
|
103
|
+
collapse?: boolean;
|
|
104
|
+
maximize?: boolean;
|
|
105
|
+
fullscreen?: boolean;
|
|
106
|
+
};
|
|
107
|
+
dataZoom?: {
|
|
108
|
+
visible?: boolean;
|
|
109
|
+
position?: 'top' | 'bottom';
|
|
110
|
+
height?: number; // height in %, default 6
|
|
111
|
+
start?: number; // 0-100, default 50
|
|
112
|
+
end?: number; // 0-100, default 100
|
|
113
|
+
zoomOnTouch?: boolean; // Enable inside zoom on touch devices, default true
|
|
114
|
+
};
|
|
115
|
+
databox?: {
|
|
116
|
+
position: 'floating' | 'left' | 'right';
|
|
117
|
+
triggerOn?: 'mousemove' | 'click' | 'none'; // When to show tooltip/crosshair, default 'mousemove'
|
|
118
|
+
};
|
|
119
|
+
layout?: {
|
|
120
|
+
mainPaneHeight: string; // e.g. "60%"
|
|
121
|
+
gap: number; // e.g. 5 (percent)
|
|
122
|
+
};
|
|
123
|
+
watermark?: boolean; // Default true
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Plugin System Types
|
|
127
|
+
|
|
128
|
+
export interface Coordinate {
|
|
129
|
+
x: number;
|
|
130
|
+
y: number;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface DataCoordinate {
|
|
134
|
+
timeIndex: number;
|
|
135
|
+
value: number;
|
|
136
|
+
paneIndex?: number; // Optional pane index
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface ChartContext {
|
|
140
|
+
// Core Access
|
|
141
|
+
getChart(): any; // echarts.ECharts instance
|
|
142
|
+
getMarketData(): OHLCV[];
|
|
143
|
+
getTimeToIndex(): Map<number, number>;
|
|
144
|
+
getOptions(): QFChartOptions;
|
|
145
|
+
|
|
146
|
+
// Event Bus
|
|
147
|
+
events: EventBus;
|
|
148
|
+
|
|
149
|
+
// Helpers
|
|
150
|
+
coordinateConversion: {
|
|
151
|
+
pixelToData: (point: Coordinate) => DataCoordinate | null;
|
|
152
|
+
dataToPixel: (point: DataCoordinate) => Coordinate | null;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Interaction Control
|
|
156
|
+
disableTools(): void; // To disable other active tools
|
|
157
|
+
|
|
158
|
+
// Zoom Control
|
|
159
|
+
setZoom(start: number, end: number): void;
|
|
160
|
+
|
|
161
|
+
// Drawing Management
|
|
162
|
+
addDrawing(drawing: DrawingElement): void;
|
|
163
|
+
removeDrawing(id: string): void;
|
|
164
|
+
getDrawing(id: string): DrawingElement | undefined;
|
|
165
|
+
updateDrawing(drawing: DrawingElement): void;
|
|
166
|
+
|
|
167
|
+
// Interaction Locking
|
|
168
|
+
lockChart(): void;
|
|
169
|
+
unlockChart(): void;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export type DrawingType = 'line' | 'fibonacci';
|
|
173
|
+
|
|
174
|
+
export interface DrawingElement {
|
|
175
|
+
id: string;
|
|
176
|
+
type: DrawingType;
|
|
177
|
+
points: DataCoordinate[]; // [start, end]
|
|
178
|
+
paneIndex?: number; // Pane where this drawing belongs (default 0)
|
|
179
|
+
style?: {
|
|
180
|
+
color?: string;
|
|
181
|
+
lineWidth?: number;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export interface PluginConfig {
|
|
186
|
+
id: string;
|
|
187
|
+
name?: string;
|
|
188
|
+
icon?: string;
|
|
189
|
+
hotkey?: string;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export interface Plugin {
|
|
193
|
+
id: string;
|
|
194
|
+
name?: string;
|
|
195
|
+
icon?: string;
|
|
196
|
+
|
|
197
|
+
init(context: ChartContext): void;
|
|
198
|
+
|
|
199
|
+
// Called when the tool button is clicked/activated
|
|
200
|
+
activate?(): void;
|
|
201
|
+
|
|
202
|
+
// Called when the tool is deactivated
|
|
203
|
+
deactivate?(): void;
|
|
204
|
+
|
|
205
|
+
// Cleanup when plugin is removed
|
|
206
|
+
destroy?(): void;
|
|
207
|
+
}
|