@qfo/qfchart 0.6.4 → 0.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/qfchart.min.browser.js +15 -15
- package/dist/qfchart.min.es.js +15 -15
- package/package.json +1 -1
- package/src/QFChart.ts +9 -6
- package/src/components/LayoutManager.ts +53 -49
- package/src/components/SeriesBuilder.ts +250 -941
- package/src/components/SeriesRendererFactory.ts +36 -0
- package/src/components/renderers/BackgroundRenderer.ts +47 -0
- package/src/components/renderers/FillRenderer.ts +99 -0
- package/src/components/renderers/HistogramRenderer.ts +20 -0
- package/src/components/renderers/LineRenderer.ts +44 -0
- package/src/components/renderers/OHLCBarRenderer.ts +161 -0
- package/src/components/renderers/ScatterRenderer.ts +54 -0
- package/src/components/renderers/SeriesRenderer.ts +20 -0
- package/src/components/renderers/ShapeRenderer.ts +121 -0
- package/src/components/renderers/StepRenderer.ts +39 -0
- package/src/types.ts +205 -205
- package/src/utils/AxisUtils.ts +63 -0
- package/src/utils/ColorUtils.ts +32 -0
- package/src/utils/ShapeUtils.ts +140 -0
- /package/src/{Utils.ts → utils/CanvasUtils.ts} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { QFChartOptions, Indicator as IndicatorType } from '../types';
|
|
1
|
+
import { QFChartOptions, Indicator as IndicatorType, OHLCV } from '../types';
|
|
2
|
+
import { AxisUtils } from '../utils/AxisUtils';
|
|
2
3
|
|
|
3
4
|
export interface PaneConfiguration {
|
|
4
5
|
index: number;
|
|
@@ -42,23 +43,6 @@ export class LayoutManager {
|
|
|
42
43
|
// Get Y-axis padding percentage (default 5%)
|
|
43
44
|
const yAxisPaddingPercent = options.yAxisPadding !== undefined ? options.yAxisPadding : 5;
|
|
44
45
|
|
|
45
|
-
// Create min/max functions that apply padding
|
|
46
|
-
const createMinFunction = (paddingPercent: number) => {
|
|
47
|
-
return (value: any) => {
|
|
48
|
-
const range = value.max - value.min;
|
|
49
|
-
const padding = range * (paddingPercent / 100);
|
|
50
|
-
return value.min - padding;
|
|
51
|
-
};
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const createMaxFunction = (paddingPercent: number) => {
|
|
55
|
-
return (value: any) => {
|
|
56
|
-
const range = value.max - value.min;
|
|
57
|
-
const padding = range * (paddingPercent / 100);
|
|
58
|
-
return value.max + padding;
|
|
59
|
-
};
|
|
60
|
-
};
|
|
61
|
-
|
|
62
46
|
// Identify unique separate panes (indices > 0) and sort them
|
|
63
47
|
const separatePaneIndices = Array.from(indicators.values())
|
|
64
48
|
.map((ind) => ind.paneIndex)
|
|
@@ -163,12 +147,18 @@ export class LayoutManager {
|
|
|
163
147
|
|
|
164
148
|
if (i === 0 && maximizeTargetIndex === 0) {
|
|
165
149
|
// Main pane is maximized, use custom values if provided
|
|
166
|
-
yMin =
|
|
167
|
-
|
|
150
|
+
yMin =
|
|
151
|
+
options.yAxisMin !== undefined && options.yAxisMin !== 'auto'
|
|
152
|
+
? options.yAxisMin
|
|
153
|
+
: AxisUtils.createMinFunction(yAxisPaddingPercent);
|
|
154
|
+
yMax =
|
|
155
|
+
options.yAxisMax !== undefined && options.yAxisMax !== 'auto'
|
|
156
|
+
? options.yAxisMax
|
|
157
|
+
: AxisUtils.createMaxFunction(yAxisPaddingPercent);
|
|
168
158
|
} else {
|
|
169
159
|
// Separate panes always use dynamic scaling
|
|
170
|
-
yMin = createMinFunction(yAxisPaddingPercent);
|
|
171
|
-
yMax = createMaxFunction(yAxisPaddingPercent);
|
|
160
|
+
yMin = AxisUtils.createMinFunction(yAxisPaddingPercent);
|
|
161
|
+
yMax = AxisUtils.createMaxFunction(yAxisPaddingPercent);
|
|
172
162
|
}
|
|
173
163
|
|
|
174
164
|
yAxis.push({
|
|
@@ -186,11 +176,10 @@ export class LayoutManager {
|
|
|
186
176
|
if (options.yAxisLabelFormatter) {
|
|
187
177
|
return options.yAxisLabelFormatter(value);
|
|
188
178
|
}
|
|
189
|
-
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
return String(value);
|
|
179
|
+
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
180
|
+
? options.yAxisDecimalPlaces
|
|
181
|
+
: AxisUtils.autoDetectDecimals(marketData as OHLCV[]);
|
|
182
|
+
return AxisUtils.formatValue(value, decimals);
|
|
194
183
|
},
|
|
195
184
|
},
|
|
196
185
|
splitLine: {
|
|
@@ -377,11 +366,10 @@ export class LayoutManager {
|
|
|
377
366
|
if (options.yAxisLabelFormatter) {
|
|
378
367
|
return options.yAxisLabelFormatter(value);
|
|
379
368
|
}
|
|
380
|
-
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
return String(value);
|
|
369
|
+
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
370
|
+
? options.yAxisDecimalPlaces
|
|
371
|
+
: AxisUtils.autoDetectDecimals(marketData as OHLCV[]);
|
|
372
|
+
return AxisUtils.formatValue(value, decimals);
|
|
385
373
|
},
|
|
386
374
|
},
|
|
387
375
|
axisTick: { show: !isMainCollapsed },
|
|
@@ -425,13 +413,13 @@ export class LayoutManager {
|
|
|
425
413
|
if (options.yAxisMin !== undefined && options.yAxisMin !== 'auto') {
|
|
426
414
|
mainYAxisMin = options.yAxisMin;
|
|
427
415
|
} else {
|
|
428
|
-
mainYAxisMin = createMinFunction(yAxisPaddingPercent);
|
|
416
|
+
mainYAxisMin = AxisUtils.createMinFunction(yAxisPaddingPercent);
|
|
429
417
|
}
|
|
430
418
|
|
|
431
419
|
if (options.yAxisMax !== undefined && options.yAxisMax !== 'auto') {
|
|
432
420
|
mainYAxisMax = options.yAxisMax;
|
|
433
421
|
} else {
|
|
434
|
-
mainYAxisMax = createMaxFunction(yAxisPaddingPercent);
|
|
422
|
+
mainYAxisMax = AxisUtils.createMaxFunction(yAxisPaddingPercent);
|
|
435
423
|
}
|
|
436
424
|
|
|
437
425
|
// Main Y-Axis (for candlesticks)
|
|
@@ -454,11 +442,10 @@ export class LayoutManager {
|
|
|
454
442
|
if (options.yAxisLabelFormatter) {
|
|
455
443
|
return options.yAxisLabelFormatter(value);
|
|
456
444
|
}
|
|
457
|
-
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
return String(value);
|
|
445
|
+
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
446
|
+
? options.yAxisDecimalPlaces
|
|
447
|
+
: AxisUtils.autoDetectDecimals(marketData as OHLCV[]);
|
|
448
|
+
return AxisUtils.formatValue(value, decimals);
|
|
462
449
|
},
|
|
463
450
|
},
|
|
464
451
|
});
|
|
@@ -554,12 +541,30 @@ export class LayoutManager {
|
|
|
554
541
|
// Create Y-axes for incompatible plots
|
|
555
542
|
// nextYAxisIndex already incremented in the loop above, so we know how many axes we need
|
|
556
543
|
const numOverlayAxes = overlayYAxisMap.size > 0 ? nextYAxisIndex - 1 : 0;
|
|
544
|
+
|
|
545
|
+
// Track which overlay axes are for visual-only plots (background, barcolor, etc.)
|
|
546
|
+
const visualOnlyAxes = new Set<number>();
|
|
547
|
+
overlayYAxisMap.forEach((yAxisIdx, plotKey) => {
|
|
548
|
+
// Check if this plot is visual-only by looking at the original indicator
|
|
549
|
+
indicators.forEach((indicator) => {
|
|
550
|
+
Object.entries(indicator.plots).forEach(([plotName, plot]) => {
|
|
551
|
+
const key = `${indicator.id}::${plotName}`;
|
|
552
|
+
if (key === plotKey && ['background', 'barcolor', 'char'].includes(plot.options.style)) {
|
|
553
|
+
visualOnlyAxes.add(yAxisIdx);
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
});
|
|
557
|
+
});
|
|
558
|
+
|
|
557
559
|
for (let i = 0; i < numOverlayAxes; i++) {
|
|
560
|
+
const yAxisIndex = i + 1; // Y-axis indices start at 1 for overlays
|
|
561
|
+
const isVisualOnly = visualOnlyAxes.has(yAxisIndex);
|
|
562
|
+
|
|
558
563
|
yAxis.push({
|
|
559
564
|
position: 'left',
|
|
560
|
-
scale:
|
|
561
|
-
min: createMinFunction(yAxisPaddingPercent),
|
|
562
|
-
max: createMaxFunction(yAxisPaddingPercent),
|
|
565
|
+
scale: !isVisualOnly, // Disable scaling for visual-only plots
|
|
566
|
+
min: isVisualOnly ? 0 : AxisUtils.createMinFunction(yAxisPaddingPercent), // Fixed range for visual plots
|
|
567
|
+
max: isVisualOnly ? 1 : AxisUtils.createMaxFunction(yAxisPaddingPercent), // Fixed range for visual plots
|
|
563
568
|
gridIndex: 0,
|
|
564
569
|
show: false, // Hide the axis visual elements
|
|
565
570
|
splitLine: { show: false },
|
|
@@ -574,8 +579,8 @@ export class LayoutManager {
|
|
|
574
579
|
yAxis.push({
|
|
575
580
|
position: 'right',
|
|
576
581
|
scale: true,
|
|
577
|
-
min: createMinFunction(yAxisPaddingPercent),
|
|
578
|
-
max: createMaxFunction(yAxisPaddingPercent),
|
|
582
|
+
min: AxisUtils.createMinFunction(yAxisPaddingPercent),
|
|
583
|
+
max: AxisUtils.createMaxFunction(yAxisPaddingPercent),
|
|
579
584
|
gridIndex: i + 1,
|
|
580
585
|
splitLine: {
|
|
581
586
|
show: !pane.isCollapsed,
|
|
@@ -590,11 +595,10 @@ export class LayoutManager {
|
|
|
590
595
|
if (options.yAxisLabelFormatter) {
|
|
591
596
|
return options.yAxisLabelFormatter(value);
|
|
592
597
|
}
|
|
593
|
-
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
return String(value);
|
|
598
|
+
const decimals = options.yAxisDecimalPlaces !== undefined
|
|
599
|
+
? options.yAxisDecimalPlaces
|
|
600
|
+
: AxisUtils.autoDetectDecimals(marketData as OHLCV[]);
|
|
601
|
+
return AxisUtils.formatValue(value, decimals);
|
|
598
602
|
},
|
|
599
603
|
},
|
|
600
604
|
axisLine: { show: !pane.isCollapsed, lineStyle: { color: '#334155' } },
|