@363045841yyt/klinechart-core 0.7.12-alpha.1 → 0.7.13
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/controllers/createIndicatorSelectorController.d.ts.map +1 -1
- package/dist/controllers/createIndicatorSelectorController.js +4 -6
- package/dist/controllers/createIndicatorSelectorController.js.map +1 -1
- package/dist/controllers/index.d.ts +0 -1
- package/dist/controllers/index.d.ts.map +1 -1
- package/dist/controllers/index.js +0 -1
- package/dist/controllers/index.js.map +1 -1
- package/dist/engine/chart.d.ts +6 -2
- package/dist/engine/chart.d.ts.map +1 -1
- package/dist/engine/chart.js +26 -23
- package/dist/engine/chart.js.map +1 -1
- package/dist/engine/indicators/indicator.worker.js +32 -4
- package/dist/engine/indicators/indicator.worker.js.map +1 -1
- package/dist/engine/indicators/indicatorDefinitionRegistry.d.ts +5 -3
- package/dist/engine/indicators/indicatorDefinitionRegistry.d.ts.map +1 -1
- package/dist/engine/indicators/indicatorDefinitionRegistry.js +26 -1
- package/dist/engine/indicators/indicatorDefinitionRegistry.js.map +1 -1
- package/dist/engine/indicators/indicatorMetadata.d.ts +54 -1
- package/dist/engine/indicators/indicatorMetadata.d.ts.map +1 -1
- package/dist/engine/indicators/indicatorMetadata.js.map +1 -1
- package/dist/engine/indicators/indicatorRuntime.d.ts +12 -117
- package/dist/engine/indicators/indicatorRuntime.d.ts.map +1 -1
- package/dist/engine/indicators/indicatorRuntime.js +124 -1308
- package/dist/engine/indicators/indicatorRuntime.js.map +1 -1
- package/dist/engine/indicators/scheduler.d.ts +19 -143
- package/dist/engine/indicators/scheduler.d.ts.map +1 -1
- package/dist/engine/indicators/scheduler.js +81 -434
- package/dist/engine/indicators/scheduler.js.map +1 -1
- package/dist/engine/indicators/stateComposer.d.ts.map +1 -1
- package/dist/engine/indicators/stateComposer.js +15 -47
- package/dist/engine/indicators/stateComposer.js.map +1 -1
- package/dist/engine/indicators/workerProtocol.d.ts +12 -36
- package/dist/engine/indicators/workerProtocol.d.ts.map +1 -1
- package/dist/engine/indicators/workerProtocol.js.map +1 -1
- package/dist/engine/renderers/Indicator/atr.d.ts +2 -1
- package/dist/engine/renderers/Indicator/atr.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/atr.js +11 -9
- package/dist/engine/renderers/Indicator/atr.js.map +1 -1
- package/dist/engine/renderers/Indicator/boll.d.ts +4 -1
- package/dist/engine/renderers/Indicator/boll.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/boll.js +24 -10
- package/dist/engine/renderers/Indicator/boll.js.map +1 -1
- package/dist/engine/renderers/Indicator/cci.d.ts +2 -1
- package/dist/engine/renderers/Indicator/cci.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/cci.js +12 -10
- package/dist/engine/renderers/Indicator/cci.js.map +1 -1
- package/dist/engine/renderers/Indicator/chaikinVol.d.ts +4 -1
- package/dist/engine/renderers/Indicator/chaikinVol.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/chaikinVol.js +21 -7
- package/dist/engine/renderers/Indicator/chaikinVol.js.map +1 -1
- package/dist/engine/renderers/Indicator/cmf.d.ts +4 -1
- package/dist/engine/renderers/Indicator/cmf.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/cmf.js +20 -7
- package/dist/engine/renderers/Indicator/cmf.js.map +1 -1
- package/dist/engine/renderers/Indicator/dema.d.ts +2 -0
- package/dist/engine/renderers/Indicator/dema.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/dema.js +19 -8
- package/dist/engine/renderers/Indicator/dema.js.map +1 -1
- package/dist/engine/renderers/Indicator/donchian.d.ts +4 -1
- package/dist/engine/renderers/Indicator/donchian.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/donchian.js +20 -8
- package/dist/engine/renderers/Indicator/donchian.js.map +1 -1
- package/dist/engine/renderers/Indicator/ene.d.ts +12 -1
- package/dist/engine/renderers/Indicator/ene.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/ene.js +25 -11
- package/dist/engine/renderers/Indicator/ene.js.map +1 -1
- package/dist/engine/renderers/Indicator/expma.d.ts +4 -1
- package/dist/engine/renderers/Indicator/expma.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/expma.js +22 -9
- package/dist/engine/renderers/Indicator/expma.js.map +1 -1
- package/dist/engine/renderers/Indicator/fastk.d.ts +2 -1
- package/dist/engine/renderers/Indicator/fastk.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/fastk.js +12 -10
- package/dist/engine/renderers/Indicator/fastk.js.map +1 -1
- package/dist/engine/renderers/Indicator/fib.d.ts +2 -0
- package/dist/engine/renderers/Indicator/fib.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/fib.js +26 -8
- package/dist/engine/renderers/Indicator/fib.js.map +1 -1
- package/dist/engine/renderers/Indicator/hma.d.ts +2 -0
- package/dist/engine/renderers/Indicator/hma.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/hma.js +19 -8
- package/dist/engine/renderers/Indicator/hma.js.map +1 -1
- package/dist/engine/renderers/Indicator/hv.d.ts +4 -1
- package/dist/engine/renderers/Indicator/hv.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/hv.js +18 -8
- package/dist/engine/renderers/Indicator/hv.js.map +1 -1
- package/dist/engine/renderers/Indicator/ichimoku.d.ts +4 -1
- package/dist/engine/renderers/Indicator/ichimoku.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/ichimoku.js +27 -8
- package/dist/engine/renderers/Indicator/ichimoku.js.map +1 -1
- package/dist/engine/renderers/Indicator/index.d.ts +27 -28
- package/dist/engine/renderers/Indicator/index.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/index.js +27 -28
- package/dist/engine/renderers/Indicator/index.js.map +1 -1
- package/dist/engine/renderers/Indicator/kama.d.ts +2 -0
- package/dist/engine/renderers/Indicator/kama.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/kama.js +19 -8
- package/dist/engine/renderers/Indicator/kama.js.map +1 -1
- package/dist/engine/renderers/Indicator/keltner.d.ts +4 -1
- package/dist/engine/renderers/Indicator/keltner.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/keltner.js +20 -8
- package/dist/engine/renderers/Indicator/keltner.js.map +1 -1
- package/dist/engine/renderers/Indicator/kst.d.ts +2 -1
- package/dist/engine/renderers/Indicator/kst.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/kst.js +13 -11
- package/dist/engine/renderers/Indicator/kst.js.map +1 -1
- package/dist/engine/renderers/Indicator/ma.d.ts +4 -1
- package/dist/engine/renderers/Indicator/ma.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/ma.js +36 -8
- package/dist/engine/renderers/Indicator/ma.js.map +1 -1
- package/dist/engine/renderers/Indicator/macd.d.ts +1 -1
- package/dist/engine/renderers/Indicator/macd.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/macd.js +9 -10
- package/dist/engine/renderers/Indicator/macd.js.map +1 -1
- package/dist/engine/renderers/Indicator/mainIndicatorLegend.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/mainIndicatorLegend.js +38 -136
- package/dist/engine/renderers/Indicator/mainIndicatorLegend.js.map +1 -1
- package/dist/engine/renderers/Indicator/mfi.d.ts +4 -1
- package/dist/engine/renderers/Indicator/mfi.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/mfi.js +20 -7
- package/dist/engine/renderers/Indicator/mfi.js.map +1 -1
- package/dist/engine/renderers/Indicator/mom.d.ts +2 -1
- package/dist/engine/renderers/Indicator/mom.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/mom.js +12 -10
- package/dist/engine/renderers/Indicator/mom.js.map +1 -1
- package/dist/engine/renderers/Indicator/obv.d.ts +4 -1
- package/dist/engine/renderers/Indicator/obv.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/obv.js +19 -7
- package/dist/engine/renderers/Indicator/obv.js.map +1 -1
- package/dist/engine/renderers/Indicator/parkinson.d.ts +4 -1
- package/dist/engine/renderers/Indicator/parkinson.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/parkinson.js +18 -8
- package/dist/engine/renderers/Indicator/parkinson.js.map +1 -1
- package/dist/engine/renderers/Indicator/pivot.d.ts +2 -0
- package/dist/engine/renderers/Indicator/pivot.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/pivot.js +43 -8
- package/dist/engine/renderers/Indicator/pivot.js.map +1 -1
- package/dist/engine/renderers/Indicator/pvt.d.ts +4 -1
- package/dist/engine/renderers/Indicator/pvt.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/pvt.js +19 -7
- package/dist/engine/renderers/Indicator/pvt.js.map +1 -1
- package/dist/engine/renderers/Indicator/roc.d.ts +4 -1
- package/dist/engine/renderers/Indicator/roc.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/roc.js +20 -7
- package/dist/engine/renderers/Indicator/roc.js.map +1 -1
- package/dist/engine/renderers/Indicator/rsi.d.ts +2 -1
- package/dist/engine/renderers/Indicator/rsi.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/rsi.js +14 -11
- package/dist/engine/renderers/Indicator/rsi.js.map +1 -1
- package/dist/engine/renderers/Indicator/sar.d.ts +4 -1
- package/dist/engine/renderers/Indicator/sar.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/sar.js +18 -8
- package/dist/engine/renderers/Indicator/sar.js.map +1 -1
- package/dist/engine/renderers/Indicator/stoch.d.ts +2 -1
- package/dist/engine/renderers/Indicator/stoch.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/stoch.js +10 -11
- package/dist/engine/renderers/Indicator/stoch.js.map +1 -1
- package/dist/engine/renderers/Indicator/structure.d.ts +4 -1
- package/dist/engine/renderers/Indicator/structure.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/structure.js +21 -8
- package/dist/engine/renderers/Indicator/structure.js.map +1 -1
- package/dist/engine/renderers/Indicator/supertrend.d.ts +4 -1
- package/dist/engine/renderers/Indicator/supertrend.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/supertrend.js +18 -8
- package/dist/engine/renderers/Indicator/supertrend.js.map +1 -1
- package/dist/engine/renderers/Indicator/tema.d.ts +2 -0
- package/dist/engine/renderers/Indicator/tema.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/tema.js +19 -8
- package/dist/engine/renderers/Indicator/tema.js.map +1 -1
- package/dist/engine/renderers/Indicator/trix.d.ts +4 -1
- package/dist/engine/renderers/Indicator/trix.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/trix.js +30 -8
- package/dist/engine/renderers/Indicator/trix.js.map +1 -1
- package/dist/engine/renderers/Indicator/vma.d.ts +4 -1
- package/dist/engine/renderers/Indicator/vma.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/vma.js +20 -7
- package/dist/engine/renderers/Indicator/vma.js.map +1 -1
- package/dist/engine/renderers/Indicator/volumeProfile.d.ts +4 -1
- package/dist/engine/renderers/Indicator/volumeProfile.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/volumeProfile.js +28 -8
- package/dist/engine/renderers/Indicator/volumeProfile.js.map +1 -1
- package/dist/engine/renderers/Indicator/vwap.d.ts +4 -1
- package/dist/engine/renderers/Indicator/vwap.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/vwap.js +19 -7
- package/dist/engine/renderers/Indicator/vwap.js.map +1 -1
- package/dist/engine/renderers/Indicator/wma.d.ts +2 -0
- package/dist/engine/renderers/Indicator/wma.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/wma.js +19 -8
- package/dist/engine/renderers/Indicator/wma.js.map +1 -1
- package/dist/engine/renderers/Indicator/wmsr.d.ts +2 -1
- package/dist/engine/renderers/Indicator/wmsr.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/wmsr.js +12 -10
- package/dist/engine/renderers/Indicator/wmsr.js.map +1 -1
- package/dist/engine/renderers/Indicator/zones.d.ts +2 -0
- package/dist/engine/renderers/Indicator/zones.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/zones.js +24 -8
- package/dist/engine/renderers/Indicator/zones.js.map +1 -1
- package/dist/engine/renderers/paneTitle.d.ts +4 -10
- package/dist/engine/renderers/paneTitle.d.ts.map +1 -1
- package/dist/engine/renderers/paneTitle.js +28 -6
- package/dist/engine/renderers/paneTitle.js.map +1 -1
- package/dist/engine/renderers/subVolume.js +0 -3
- package/dist/engine/renderers/subVolume.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +4 -4
- package/src/controllers/__tests__/indicatorSelector.test.ts +4 -3
- package/src/controllers/createIndicatorSelectorController.ts +4 -6
- package/src/controllers/index.ts +0 -4
- package/src/engine/chart.ts +30 -22
- package/src/engine/indicators/__tests__/registerBuiltins.test.ts +14 -23
- package/src/engine/indicators/__tests__/scheduler.test.ts +14 -14
- package/src/engine/indicators/__tests__/stateComposer.test.ts +7 -4
- package/src/engine/indicators/indicator.worker.ts +36 -4
- package/src/engine/indicators/indicatorDefinitionRegistry.ts +38 -3
- package/src/engine/indicators/indicatorMetadata.ts +66 -1
- package/src/engine/indicators/indicatorRuntime.ts +121 -1447
- package/src/engine/indicators/scheduler.ts +85 -489
- package/src/engine/indicators/stateComposer.ts +16 -60
- package/src/engine/indicators/workerProtocol.ts +14 -36
- package/src/engine/renderers/Indicator/atr.ts +14 -11
- package/src/engine/renderers/Indicator/boll.ts +33 -10
- package/src/engine/renderers/Indicator/cci.ts +15 -14
- package/src/engine/renderers/Indicator/chaikinVol.ts +29 -7
- package/src/engine/renderers/Indicator/cmf.ts +28 -7
- package/src/engine/renderers/Indicator/dema.ts +28 -9
- package/src/engine/renderers/Indicator/donchian.ts +28 -9
- package/src/engine/renderers/Indicator/ene.ts +34 -11
- package/src/engine/renderers/Indicator/expma.ts +31 -9
- package/src/engine/renderers/Indicator/fastk.ts +15 -14
- package/src/engine/renderers/Indicator/fib.ts +29 -9
- package/src/engine/renderers/Indicator/hma.ts +28 -9
- package/src/engine/renderers/Indicator/hv.ts +27 -8
- package/src/engine/renderers/Indicator/ichimoku.ts +31 -9
- package/src/engine/renderers/Indicator/index.ts +27 -28
- package/src/engine/renderers/Indicator/kama.ts +28 -9
- package/src/engine/renderers/Indicator/keltner.ts +28 -9
- package/src/engine/renderers/Indicator/kst.ts +16 -19
- package/src/engine/renderers/Indicator/ma.ts +44 -9
- package/src/engine/renderers/Indicator/macd.ts +31 -36
- package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +43 -162
- package/src/engine/renderers/Indicator/mfi.ts +28 -7
- package/src/engine/renderers/Indicator/mom.ts +15 -14
- package/src/engine/renderers/Indicator/obv.ts +27 -7
- package/src/engine/renderers/Indicator/parkinson.ts +27 -8
- package/src/engine/renderers/Indicator/pivot.ts +47 -9
- package/src/engine/renderers/Indicator/pvt.ts +27 -7
- package/src/engine/renderers/Indicator/roc.ts +29 -7
- package/src/engine/renderers/Indicator/rsi.ts +14 -17
- package/src/engine/renderers/Indicator/sar.ts +26 -9
- package/src/engine/renderers/Indicator/stoch.ts +13 -16
- package/src/engine/renderers/Indicator/structure.ts +31 -8
- package/src/engine/renderers/Indicator/supertrend.ts +26 -9
- package/src/engine/renderers/Indicator/tema.ts +28 -9
- package/src/engine/renderers/Indicator/trix.ts +39 -8
- package/src/engine/renderers/Indicator/vma.ts +28 -7
- package/src/engine/renderers/Indicator/volumeProfile.ts +40 -8
- package/src/engine/renderers/Indicator/vwap.ts +27 -7
- package/src/engine/renderers/Indicator/wma.ts +28 -9
- package/src/engine/renderers/Indicator/wmsr.ts +15 -14
- package/src/engine/renderers/Indicator/zones.ts +29 -9
- package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +142 -79
- package/src/engine/renderers/paneTitle.ts +43 -18
- package/src/engine/renderers/subVolume.ts +0 -2
- package/src/version.ts +1 -1
- package/dist/engine/renderers/Indicator/macdLegend.d.ts +0 -13
- package/dist/engine/renderers/Indicator/macdLegend.d.ts.map +0 -1
- package/dist/engine/renderers/Indicator/macdLegend.js +0 -116
- package/dist/engine/renderers/Indicator/macdLegend.js.map +0 -1
- package/dist/engine/renderers/Indicator/subPaneConfig.d.ts +0 -16
- package/dist/engine/renderers/Indicator/subPaneConfig.d.ts.map +0 -1
- package/dist/engine/renderers/Indicator/subPaneConfig.js +0 -194
- package/dist/engine/renderers/Indicator/subPaneConfig.js.map +0 -1
- package/src/engine/renderers/Indicator/macdLegend.ts +0 -141
- package/src/engine/renderers/Indicator/subPaneConfig.ts +0 -265
|
@@ -8,11 +8,13 @@ import { ENE_STATE_KEY } from '@/core/indicators/eneState'
|
|
|
8
8
|
import type { PluginHost, RenderContext, RendererPluginWithHost } from '@/plugin'
|
|
9
9
|
import type { KLineData } from '@/types/price'
|
|
10
10
|
import type { Pane } from '@/core/layout/pane'
|
|
11
|
+
import type { GetTitleInfoFn, TitleInfo, TitleValueItem } from '@/engine/indicators/indicatorMetadata'
|
|
12
|
+
import type { IndicatorScheduler } from '@/engine/indicators/indicatorScheduler'
|
|
11
13
|
|
|
12
14
|
// Type helper for tests - we know these methods exist on the implementation
|
|
13
15
|
interface TestableLegendRenderer extends RendererPluginWithHost {
|
|
14
16
|
draw: (context: RenderContext) => void
|
|
15
|
-
getConfig: () => { yPaddingPx: number
|
|
17
|
+
getConfig: () => { yPaddingPx: number }
|
|
16
18
|
setConfig: (config: Record<string, unknown>) => void
|
|
17
19
|
}
|
|
18
20
|
|
|
@@ -33,10 +35,73 @@ function createMockCanvasContext(): CanvasRenderingContext2D {
|
|
|
33
35
|
} as unknown as CanvasRenderingContext2D
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Create a mock scheduler that returns indicator metadata with getTitleInfo.
|
|
40
|
+
* Keys are case-insensitive (matching real IndicatorScheduler behavior).
|
|
41
|
+
*/
|
|
42
|
+
function createMockScheduler(
|
|
43
|
+
metadataMap: Record<string, { getTitleInfo: GetTitleInfoFn }>,
|
|
44
|
+
activeMainIndicators?: string[]
|
|
45
|
+
): IndicatorScheduler {
|
|
46
|
+
const activeSet = new Set((activeMainIndicators ?? ['ma']).map((i: string) => i.toLowerCase()))
|
|
47
|
+
return {
|
|
48
|
+
getIndicatorMetadata: vi.fn((id: string) => metadataMap[id.toLowerCase()] ?? null),
|
|
49
|
+
getMainIndicators: vi.fn(() => Object.entries(metadataMap).map(([id, meta]) => ({
|
|
50
|
+
name: id,
|
|
51
|
+
getTitleInfo: meta.getTitleInfo,
|
|
52
|
+
category: 'main',
|
|
53
|
+
}))),
|
|
54
|
+
isMainIndicatorActive: vi.fn((id: string) => activeSet.has(id.toLowerCase())),
|
|
55
|
+
getMainIndicatorParams: vi.fn(() => ({})),
|
|
56
|
+
} as unknown as IndicatorScheduler
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create a mock getTitleInfo for MA that reads from shared state
|
|
61
|
+
*/
|
|
62
|
+
function createMAGetTitleInfo(): GetTitleInfoFn {
|
|
63
|
+
return (data, index, params, host, paneId): TitleInfo => {
|
|
64
|
+
const state = host.getSharedState<MARenderState>(MA_STATE_KEY)
|
|
65
|
+
if (!state) return null
|
|
66
|
+
|
|
67
|
+
const ci = index ?? (state.series[5]?.length ?? 100) - 1
|
|
68
|
+
const values: TitleValueItem[] = []
|
|
69
|
+
for (const period of state.enabledPeriods) {
|
|
70
|
+
const v = state.series[period]?.[ci]
|
|
71
|
+
if (v !== undefined) {
|
|
72
|
+
values.push({
|
|
73
|
+
label: `MA${period}`,
|
|
74
|
+
value: v,
|
|
75
|
+
color: '#888888',
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { name: 'MA', values: values.length > 0 ? values : undefined }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Create simple mock getTitleInfo for BOLL/EXPMA/ENE
|
|
85
|
+
*/
|
|
86
|
+
function createSimpleGetTitleInfo(name: string): GetTitleInfoFn {
|
|
87
|
+
return (): TitleInfo => ({
|
|
88
|
+
name,
|
|
89
|
+
params: [20, 2],
|
|
90
|
+
values: [
|
|
91
|
+
{ label: 'MID', value: 100.00, color: '#FF0000' },
|
|
92
|
+
{ label: 'UP', value: 120.00, color: '#00FF00' },
|
|
93
|
+
{ label: 'DN', value: 80.00, color: '#0000FF' },
|
|
94
|
+
],
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
36
98
|
/**
|
|
37
99
|
* 创建 mock PluginHost
|
|
38
100
|
*/
|
|
39
|
-
function createMockPluginHost(
|
|
101
|
+
function createMockPluginHost(
|
|
102
|
+
state?: MARenderState,
|
|
103
|
+
scheduler?: IndicatorScheduler
|
|
104
|
+
): PluginHost {
|
|
40
105
|
return {
|
|
41
106
|
setSharedState: vi.fn(),
|
|
42
107
|
getSharedState: vi.fn(<T>(key: string): T | undefined => {
|
|
@@ -59,6 +124,10 @@ function createMockPluginHost(state?: MARenderState): PluginHost {
|
|
|
59
124
|
off: vi.fn(),
|
|
60
125
|
once: vi.fn(),
|
|
61
126
|
emit: vi.fn(),
|
|
127
|
+
getService: vi.fn((name: string) => {
|
|
128
|
+
if (name === 'indicatorScheduler') return scheduler
|
|
129
|
+
return undefined
|
|
130
|
+
}),
|
|
62
131
|
} as unknown as PluginHost
|
|
63
132
|
}
|
|
64
133
|
|
|
@@ -149,14 +218,9 @@ describe('createMainIndicatorLegendRendererPlugin', () => {
|
|
|
149
218
|
expect(typeof plugin.onInstall).toBe('function')
|
|
150
219
|
})
|
|
151
220
|
|
|
152
|
-
it('should declare
|
|
221
|
+
it('should not declare any namespace keys (individual renderers own their state)', () => {
|
|
153
222
|
const plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 })
|
|
154
|
-
expect(plugin.getDeclaredNamespaces()).toEqual([
|
|
155
|
-
MA_STATE_KEY,
|
|
156
|
-
BOLL_STATE_KEY,
|
|
157
|
-
EXPMA_STATE_KEY,
|
|
158
|
-
ENE_STATE_KEY,
|
|
159
|
-
])
|
|
223
|
+
expect(plugin.getDeclaredNamespaces()).toEqual([])
|
|
160
224
|
})
|
|
161
225
|
})
|
|
162
226
|
|
|
@@ -168,17 +232,13 @@ describe('MainIndicatorLegend draw', () => {
|
|
|
168
232
|
ctx = createMockCanvasContext()
|
|
169
233
|
})
|
|
170
234
|
|
|
171
|
-
it('should not draw MA when MA is
|
|
235
|
+
it('should not draw MA when MA is not active', () => {
|
|
172
236
|
const state = createTestMARenderState()
|
|
173
|
-
const
|
|
237
|
+
const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, [])
|
|
238
|
+
const mockHost = createMockPluginHost(state, scheduler)
|
|
174
239
|
plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 }) as TestableLegendRenderer
|
|
175
240
|
plugin.onInstall(mockHost)
|
|
176
241
|
|
|
177
|
-
// Disable MA
|
|
178
|
-
plugin.setConfig({
|
|
179
|
-
indicators: { MA: { enabled: false, params: {} } },
|
|
180
|
-
})
|
|
181
|
-
|
|
182
242
|
const context = createMockRenderContext(ctx)
|
|
183
243
|
plugin.draw(context)
|
|
184
244
|
|
|
@@ -190,7 +250,8 @@ describe('MainIndicatorLegend draw', () => {
|
|
|
190
250
|
|
|
191
251
|
it('should draw MA values from StateStore', () => {
|
|
192
252
|
const state = createTestMARenderState()
|
|
193
|
-
const
|
|
253
|
+
const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, ['ma'])
|
|
254
|
+
const mockHost = createMockPluginHost(state, scheduler)
|
|
194
255
|
plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 }) as TestableLegendRenderer
|
|
195
256
|
plugin.onInstall(mockHost)
|
|
196
257
|
|
|
@@ -215,7 +276,8 @@ describe('MainIndicatorLegend draw', () => {
|
|
|
215
276
|
},
|
|
216
277
|
enabledPeriods: [5],
|
|
217
278
|
})
|
|
218
|
-
const
|
|
279
|
+
const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, ['ma'])
|
|
280
|
+
const mockHost = createMockPluginHost(state, scheduler)
|
|
219
281
|
plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 }) as TestableLegendRenderer
|
|
220
282
|
plugin.onInstall(mockHost)
|
|
221
283
|
|
|
@@ -227,7 +289,7 @@ describe('MainIndicatorLegend draw', () => {
|
|
|
227
289
|
|
|
228
290
|
// Should show value 150 at index 50 (100 + 50)
|
|
229
291
|
const maValueCalls = fillTextCalls.filter(call =>
|
|
230
|
-
String(call[0]).includes('150.
|
|
292
|
+
String(call[0]).includes('150.000')
|
|
231
293
|
)
|
|
232
294
|
expect(maValueCalls.length).toBeGreaterThan(0)
|
|
233
295
|
})
|
|
@@ -239,7 +301,8 @@ describe('MainIndicatorLegend draw', () => {
|
|
|
239
301
|
},
|
|
240
302
|
enabledPeriods: [5],
|
|
241
303
|
})
|
|
242
|
-
const
|
|
304
|
+
const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, ['ma'])
|
|
305
|
+
const mockHost = createMockPluginHost(state, scheduler)
|
|
243
306
|
plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 }) as TestableLegendRenderer
|
|
244
307
|
plugin.onInstall(mockHost)
|
|
245
308
|
|
|
@@ -261,7 +324,7 @@ describe('MainIndicatorLegend draw', () => {
|
|
|
261
324
|
|
|
262
325
|
// Should show last value (109) at index 9
|
|
263
326
|
const maValueCalls = fillTextCalls.filter(call =>
|
|
264
|
-
String(call[0]).includes('109.
|
|
327
|
+
String(call[0]).includes('109.000')
|
|
265
328
|
)
|
|
266
329
|
expect(maValueCalls.length).toBeGreaterThan(0)
|
|
267
330
|
})
|
|
@@ -283,27 +346,29 @@ describe('MainIndicatorLegend draw', () => {
|
|
|
283
346
|
visibleMax: -Infinity,
|
|
284
347
|
enabledPeriods: [],
|
|
285
348
|
})
|
|
286
|
-
const
|
|
349
|
+
const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, ['ma'])
|
|
350
|
+
const mockHost = createMockPluginHost(state, scheduler)
|
|
287
351
|
plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 }) as TestableLegendRenderer
|
|
288
352
|
plugin.onInstall(mockHost)
|
|
289
353
|
|
|
290
354
|
const context = createMockRenderContext(ctx)
|
|
291
355
|
plugin.draw(context)
|
|
292
356
|
|
|
293
|
-
// Should not draw any MA values
|
|
357
|
+
// Should not draw any MA period values (name 'MA' may still appear but no period texts)
|
|
294
358
|
const fillTextCalls = vi.mocked(ctx.fillText).mock.calls
|
|
295
359
|
const ma5Calls = fillTextCalls.filter(call => String(call[0]).includes('MA5'))
|
|
296
360
|
expect(ma5Calls).toHaveLength(0)
|
|
297
361
|
})
|
|
298
362
|
|
|
299
|
-
it('should display values with
|
|
363
|
+
it('should display values with 3 decimal places', () => {
|
|
300
364
|
const state = createTestMARenderState({
|
|
301
365
|
series: {
|
|
302
366
|
5: Array.from({ length: 100 }, () => 123.4567),
|
|
303
367
|
},
|
|
304
368
|
enabledPeriods: [5],
|
|
305
369
|
})
|
|
306
|
-
const
|
|
370
|
+
const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, ['ma'])
|
|
371
|
+
const mockHost = createMockPluginHost(state, scheduler)
|
|
307
372
|
plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 }) as TestableLegendRenderer
|
|
308
373
|
plugin.onInstall(mockHost)
|
|
309
374
|
|
|
@@ -314,29 +379,31 @@ describe('MainIndicatorLegend draw', () => {
|
|
|
314
379
|
|
|
315
380
|
// Should show formatted value
|
|
316
381
|
const formattedValueCalls = fillTextCalls.filter(call =>
|
|
317
|
-
String(call[0]).includes('123.
|
|
382
|
+
String(call[0]).includes('123.457')
|
|
318
383
|
)
|
|
319
384
|
expect(formattedValueCalls.length).toBeGreaterThan(0)
|
|
320
385
|
})
|
|
321
386
|
|
|
322
387
|
it('should use correct colors for each MA period', () => {
|
|
323
388
|
const state = createTestMARenderState()
|
|
324
|
-
const
|
|
389
|
+
const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, ['ma'])
|
|
390
|
+
const mockHost = createMockPluginHost(state, scheduler)
|
|
325
391
|
plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 }) as TestableLegendRenderer
|
|
326
392
|
plugin.onInstall(mockHost)
|
|
327
393
|
|
|
328
394
|
const context = createMockRenderContext(ctx)
|
|
329
395
|
plugin.draw(context)
|
|
330
396
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
expect(
|
|
397
|
+
// Should have drawn MA period values with proper colors
|
|
398
|
+
const fillTextCalls = vi.mocked(ctx.fillText).mock.calls
|
|
399
|
+
const ma5Calls = fillTextCalls.filter(call => String(call[0]).includes('MA5'))
|
|
400
|
+
expect(ma5Calls.length).toBeGreaterThan(0)
|
|
335
401
|
})
|
|
336
402
|
|
|
337
403
|
it('should save and restore context', () => {
|
|
338
404
|
const state = createTestMARenderState()
|
|
339
|
-
const
|
|
405
|
+
const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, ['ma'])
|
|
406
|
+
const mockHost = createMockPluginHost(state, scheduler)
|
|
340
407
|
plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 }) as TestableLegendRenderer
|
|
341
408
|
plugin.onInstall(mockHost)
|
|
342
409
|
|
|
@@ -357,10 +424,15 @@ describe('MainIndicatorLegend MA data source', () => {
|
|
|
357
424
|
enabledPeriods: [5],
|
|
358
425
|
})
|
|
359
426
|
const mockGetSharedState = vi.fn().mockReturnValue(state)
|
|
427
|
+
const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, ['ma'])
|
|
360
428
|
const mockHost = {
|
|
361
429
|
setSharedState: vi.fn(),
|
|
362
430
|
getSharedState: mockGetSharedState,
|
|
363
431
|
clearByOwner: vi.fn(),
|
|
432
|
+
getService: vi.fn((name: string) => {
|
|
433
|
+
if (name === 'indicatorScheduler') return scheduler
|
|
434
|
+
return undefined
|
|
435
|
+
}),
|
|
364
436
|
} as unknown as PluginHost
|
|
365
437
|
|
|
366
438
|
const plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 })
|
|
@@ -373,7 +445,7 @@ describe('MainIndicatorLegend MA data source', () => {
|
|
|
373
445
|
})
|
|
374
446
|
plugin.draw(context)
|
|
375
447
|
|
|
376
|
-
// Verify it read from StateStore
|
|
448
|
+
// Verify it read from StateStore (via getTitleInfo calling getSharedState)
|
|
377
449
|
expect(mockGetSharedState).toHaveBeenCalledWith(MA_STATE_KEY)
|
|
378
450
|
|
|
379
451
|
const fillTextCalls = vi.mocked(ctx.fillText).mock.calls
|
|
@@ -387,14 +459,12 @@ describe('MainIndicatorLegend MA data source', () => {
|
|
|
387
459
|
})
|
|
388
460
|
|
|
389
461
|
describe('MainIndicatorLegend config management', () => {
|
|
390
|
-
it('getConfig should return current
|
|
462
|
+
it('getConfig should return current yPaddingPx', () => {
|
|
391
463
|
const plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 })
|
|
392
464
|
|
|
393
465
|
const config = plugin.getConfig()
|
|
394
466
|
|
|
395
467
|
expect(config.yPaddingPx).toBe(20)
|
|
396
|
-
expect(config.indicators.MA.enabled).toBe(true)
|
|
397
|
-
expect(config.indicators.BOLL.enabled).toBe(false)
|
|
398
468
|
})
|
|
399
469
|
|
|
400
470
|
it('setConfig should update yPaddingPx', () => {
|
|
@@ -405,37 +475,18 @@ describe('MainIndicatorLegend config management', () => {
|
|
|
405
475
|
const config = plugin.getConfig()
|
|
406
476
|
expect(config.yPaddingPx).toBe(30)
|
|
407
477
|
})
|
|
408
|
-
|
|
409
|
-
it('setConfig should merge indicator config', () => {
|
|
410
|
-
const plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 })
|
|
411
|
-
|
|
412
|
-
plugin.setConfig({
|
|
413
|
-
indicators: {
|
|
414
|
-
MA: { enabled: false, params: {} },
|
|
415
|
-
BOLL: { enabled: true, params: { period: 26 } },
|
|
416
|
-
},
|
|
417
|
-
})
|
|
418
|
-
|
|
419
|
-
const config = plugin.getConfig()
|
|
420
|
-
expect(config.indicators.MA.enabled).toBe(false)
|
|
421
|
-
expect(config.indicators.BOLL.enabled).toBe(true)
|
|
422
|
-
expect(config.indicators.BOLL.params.period).toBe(26)
|
|
423
|
-
})
|
|
424
478
|
})
|
|
425
479
|
|
|
426
480
|
describe('MainIndicatorLegend with other indicators', () => {
|
|
427
|
-
it('should draw BOLL when
|
|
428
|
-
const
|
|
481
|
+
it('should draw BOLL when active', () => {
|
|
482
|
+
const scheduler = createMockScheduler(
|
|
483
|
+
{ boll: { getTitleInfo: createSimpleGetTitleInfo('BOLL') } },
|
|
484
|
+
['boll']
|
|
485
|
+
)
|
|
486
|
+
const mockHost = createMockPluginHost(undefined, scheduler)
|
|
429
487
|
const plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 })
|
|
430
488
|
plugin.onInstall(mockHost)
|
|
431
489
|
|
|
432
|
-
plugin.setConfig({
|
|
433
|
-
indicators: {
|
|
434
|
-
MA: { enabled: false, params: {} },
|
|
435
|
-
BOLL: { enabled: true, params: { period: 20, multiplier: 2 } },
|
|
436
|
-
},
|
|
437
|
-
})
|
|
438
|
-
|
|
439
490
|
const ctx = createMockCanvasContext()
|
|
440
491
|
const context = createMockRenderContext(ctx)
|
|
441
492
|
plugin.draw(context)
|
|
@@ -449,18 +500,15 @@ describe('MainIndicatorLegend with other indicators', () => {
|
|
|
449
500
|
expect(bollLabelCalls.length).toBeGreaterThan(0)
|
|
450
501
|
})
|
|
451
502
|
|
|
452
|
-
it('should draw EXPMA when
|
|
453
|
-
const
|
|
503
|
+
it('should draw EXPMA when active', () => {
|
|
504
|
+
const scheduler = createMockScheduler(
|
|
505
|
+
{ expma: { getTitleInfo: createSimpleGetTitleInfo('EXPMA') } },
|
|
506
|
+
['expma']
|
|
507
|
+
)
|
|
508
|
+
const mockHost = createMockPluginHost(undefined, scheduler)
|
|
454
509
|
const plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 })
|
|
455
510
|
plugin.onInstall(mockHost)
|
|
456
511
|
|
|
457
|
-
plugin.setConfig({
|
|
458
|
-
indicators: {
|
|
459
|
-
MA: { enabled: false, params: {} },
|
|
460
|
-
EXPMA: { enabled: true, params: { fastPeriod: 12, slowPeriod: 50 } },
|
|
461
|
-
},
|
|
462
|
-
})
|
|
463
|
-
|
|
464
512
|
const ctx = createMockCanvasContext()
|
|
465
513
|
const context = createMockRenderContext(ctx)
|
|
466
514
|
plugin.draw(context)
|
|
@@ -474,18 +522,15 @@ describe('MainIndicatorLegend with other indicators', () => {
|
|
|
474
522
|
expect(expmaLabelCalls.length).toBeGreaterThan(0)
|
|
475
523
|
})
|
|
476
524
|
|
|
477
|
-
it('should draw ENE when
|
|
478
|
-
const
|
|
525
|
+
it('should draw ENE when active', () => {
|
|
526
|
+
const scheduler = createMockScheduler(
|
|
527
|
+
{ ene: { getTitleInfo: createSimpleGetTitleInfo('ENE') } },
|
|
528
|
+
['ene']
|
|
529
|
+
)
|
|
530
|
+
const mockHost = createMockPluginHost(undefined, scheduler)
|
|
479
531
|
const plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 })
|
|
480
532
|
plugin.onInstall(mockHost)
|
|
481
533
|
|
|
482
|
-
plugin.setConfig({
|
|
483
|
-
indicators: {
|
|
484
|
-
MA: { enabled: false, params: {} },
|
|
485
|
-
ENE: { enabled: true, params: { period: 10, deviation: 11 } },
|
|
486
|
-
},
|
|
487
|
-
})
|
|
488
|
-
|
|
489
534
|
const ctx = createMockCanvasContext()
|
|
490
535
|
const context = createMockRenderContext(ctx)
|
|
491
536
|
plugin.draw(context)
|
|
@@ -498,4 +543,22 @@ describe('MainIndicatorLegend with other indicators', () => {
|
|
|
498
543
|
)
|
|
499
544
|
expect(eneLabelCalls.length).toBeGreaterThan(0)
|
|
500
545
|
})
|
|
546
|
+
|
|
547
|
+
it('should draw any registered main indicator when active (WMA example)', () => {
|
|
548
|
+
const scheduler = createMockScheduler(
|
|
549
|
+
{ wma: { getTitleInfo: createSimpleGetTitleInfo('WMA') } },
|
|
550
|
+
['wma']
|
|
551
|
+
)
|
|
552
|
+
const mockHost = createMockPluginHost(undefined, scheduler)
|
|
553
|
+
const plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 })
|
|
554
|
+
plugin.onInstall(mockHost)
|
|
555
|
+
|
|
556
|
+
const ctx = createMockCanvasContext()
|
|
557
|
+
const context = createMockRenderContext(ctx)
|
|
558
|
+
plugin.draw(context)
|
|
559
|
+
|
|
560
|
+
const fillTextCalls = vi.mocked(ctx.fillText).mock.calls
|
|
561
|
+
const wmaLabelCalls = fillTextCalls.filter(call => String(call[0]).includes('WMA'))
|
|
562
|
+
expect(wmaLabelCalls.length).toBeGreaterThan(0)
|
|
563
|
+
})
|
|
501
564
|
})
|
|
@@ -2,8 +2,33 @@ import type { RendererPluginWithHost, RenderContext, PluginHost } from '../../pl
|
|
|
2
2
|
import { RENDERER_PRIORITY } from '../../plugin'
|
|
3
3
|
import { resolveThemeColors } from '../../tokens'
|
|
4
4
|
import { getFont, setCanvasFont } from '../theme/fonts'
|
|
5
|
-
import { SUB_PANE_INDICATOR_CONFIGS } from './Indicator/subPaneConfig'
|
|
6
5
|
import type { SubIndicatorType } from './Indicator'
|
|
6
|
+
import type { KLineData } from '../../types/price'
|
|
7
|
+
import type { TitleInfo } from '../indicators/indicatorMetadata'
|
|
8
|
+
import type { IndicatorScheduler } from '../indicators/scheduler'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @deprecated 请从 indicatorMetadata 导入 TitleInfo
|
|
12
|
+
*/
|
|
13
|
+
export type { TitleInfo, TitleValueItem } from '../indicators/indicatorMetadata'
|
|
14
|
+
|
|
15
|
+
function getVolumeTitleInfo(
|
|
16
|
+
data: KLineData[],
|
|
17
|
+
index: number | null,
|
|
18
|
+
_params: Record<string, number | boolean | string>,
|
|
19
|
+
_host: PluginHost,
|
|
20
|
+
_paneId: string,
|
|
21
|
+
): TitleInfo | null {
|
|
22
|
+
if (index === null) return null
|
|
23
|
+
const kline = data[index]
|
|
24
|
+
if (!kline || kline.volume === undefined) return null
|
|
25
|
+
const color = kline.open < kline.close ? '#ef4444' : '#22c55e'
|
|
26
|
+
return {
|
|
27
|
+
name: 'VOL',
|
|
28
|
+
params: [],
|
|
29
|
+
values: [{ label: 'VOL', value: kline.volume, color }],
|
|
30
|
+
}
|
|
31
|
+
}
|
|
7
32
|
|
|
8
33
|
const textWidthCache = new Map<string, number>()
|
|
9
34
|
const TEXT_WIDTH_CACHE_LIMIT = 256
|
|
@@ -23,18 +48,6 @@ function measureTextWidth(ctx: CanvasRenderingContext2D, text: string): number {
|
|
|
23
48
|
return width
|
|
24
49
|
}
|
|
25
50
|
|
|
26
|
-
export interface TitleValueItem {
|
|
27
|
-
label: string
|
|
28
|
-
value: number
|
|
29
|
-
color: string
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface TitleInfo {
|
|
33
|
-
name: string
|
|
34
|
-
params?: number[]
|
|
35
|
-
values?: TitleValueItem[]
|
|
36
|
-
}
|
|
37
|
-
|
|
38
51
|
export interface PaneTitleOptions {
|
|
39
52
|
paneId: string
|
|
40
53
|
title: string
|
|
@@ -68,7 +81,7 @@ export function createPaneTitleRendererPlugin(options: PaneTitleOptions): Render
|
|
|
68
81
|
|
|
69
82
|
const fontSize = 12
|
|
70
83
|
const x = 12
|
|
71
|
-
|
|
84
|
+
let y = currentOptions.yOffset ?? fontSize
|
|
72
85
|
const gap = 8
|
|
73
86
|
|
|
74
87
|
overlayCtx.save()
|
|
@@ -76,11 +89,22 @@ export function createPaneTitleRendererPlugin(options: PaneTitleOptions): Render
|
|
|
76
89
|
overlayCtx.textAlign = 'left'
|
|
77
90
|
overlayCtx.textBaseline = 'top'
|
|
78
91
|
|
|
79
|
-
const config = SUB_PANE_INDICATOR_CONFIGS[currentOptions.indicatorId]
|
|
80
92
|
const crosshairIndex = context.crosshairIndex ?? null
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
93
|
+
const castParams = currentOptions.params as Record<string, number | boolean | string>
|
|
94
|
+
const klineData = context.data as KLineData[]
|
|
95
|
+
|
|
96
|
+
// 优先从 indicator metadata registry 获取 getTitleInfo
|
|
97
|
+
let titleInfo: TitleInfo | null = null
|
|
98
|
+
const scheduler = pluginHost?.getService<IndicatorScheduler>('indicatorScheduler')
|
|
99
|
+
const meta = scheduler?.getIndicatorMetadata(currentOptions.indicatorId)
|
|
100
|
+
if (meta?.getTitleInfo && pluginHost) {
|
|
101
|
+
titleInfo = meta.getTitleInfo(klineData, crosshairIndex, castParams, pluginHost, currentOptions.paneId)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// fallback: VOLUME 不是注册指标,内联处理
|
|
105
|
+
if (!titleInfo && pluginHost) {
|
|
106
|
+
titleInfo = getVolumeTitleInfo(klineData, crosshairIndex, castParams, pluginHost, currentOptions.paneId)
|
|
107
|
+
}
|
|
84
108
|
|
|
85
109
|
if (titleInfo) {
|
|
86
110
|
let currentX = x
|
|
@@ -99,6 +123,7 @@ export function createPaneTitleRendererPlugin(options: PaneTitleOptions): Render
|
|
|
99
123
|
}
|
|
100
124
|
|
|
101
125
|
if (titleInfo.values && titleInfo.values.length > 0) {
|
|
126
|
+
y += 1
|
|
102
127
|
for (const item of titleInfo.values) {
|
|
103
128
|
const valueText = `${item.label} ${item.value.toFixed(3)}`
|
|
104
129
|
overlayCtx.fillStyle = item.color
|
|
@@ -232,10 +232,8 @@ function judgeColor(dayData: KLineData, upColor: string, downColor: string, neut
|
|
|
232
232
|
name: 'volume',
|
|
233
233
|
displayName: 'VOL',
|
|
234
234
|
category: 'volume',
|
|
235
|
-
stateKey: (paneId: string) => createIndicatorStateKey('volume', paneId),
|
|
236
235
|
defaultPaneId: 'sub',
|
|
237
236
|
scaleRendererFactory: createVolumeScaleRendererPlugin,
|
|
238
|
-
updateConfig: () => {},
|
|
239
237
|
})
|
|
240
238
|
class VolumeIndicatorDefinition {
|
|
241
239
|
static rendererFactory = createVolumeRendererPlugin
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.7.
|
|
1
|
+
export const VERSION = "0.7.13"
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { RendererPluginWithHost } from '../../../plugin';
|
|
2
|
-
export interface MACDLegendOptions {
|
|
3
|
-
/** Y 轴内边距(与主图保持一致) */
|
|
4
|
-
yPaddingPx?: number;
|
|
5
|
-
/** 目标 pane ID(默认 'sub') */
|
|
6
|
-
paneId?: string;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* 创建 MACD 图例渲染器插件
|
|
10
|
-
* 从 StateStore 读取 MACD 状态,不再在 draw 时计算
|
|
11
|
-
*/
|
|
12
|
-
export declare function createMACDLegendRendererPlugin(options?: MACDLegendOptions): RendererPluginWithHost;
|
|
13
|
-
//# sourceMappingURL=macdLegend.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"macdLegend.d.ts","sourceRoot":"","sources":["../../../../src/engine/renderers/Indicator/macdLegend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAA6B,MAAM,iBAAiB,CAAA;AAyBxF,MAAM,WAAW,iBAAiB;IAC9B,sBAAsB;IACtB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,GAAE,iBAAsB,GAAG,sBAAsB,CAwGtG"}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { RENDERER_PRIORITY } from '../../../plugin';
|
|
2
|
-
import { resolveThemeColors } from '../../../tokens';
|
|
3
|
-
import { getFont, setCanvasFont } from '../../theme/fonts';
|
|
4
|
-
import { createMACDStateKey } from '../../indicators/macdState';
|
|
5
|
-
const textWidthCache = new Map();
|
|
6
|
-
const TEXT_WIDTH_CACHE_LIMIT = 256;
|
|
7
|
-
function measureTextWidth(ctx, text) {
|
|
8
|
-
const key = `${ctx.font}\n${text}`;
|
|
9
|
-
const cached = textWidthCache.get(key);
|
|
10
|
-
if (cached !== undefined) {
|
|
11
|
-
return cached;
|
|
12
|
-
}
|
|
13
|
-
const width = ctx.measureText(text).width;
|
|
14
|
-
if (textWidthCache.size >= TEXT_WIDTH_CACHE_LIMIT) {
|
|
15
|
-
textWidthCache.clear();
|
|
16
|
-
}
|
|
17
|
-
textWidthCache.set(key, width);
|
|
18
|
-
return width;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* 创建 MACD 图例渲染器插件
|
|
22
|
-
* 从 StateStore 读取 MACD 状态,不再在 draw 时计算
|
|
23
|
-
*/
|
|
24
|
-
export function createMACDLegendRendererPlugin(options = {}) {
|
|
25
|
-
const yPaddingPx = options.yPaddingPx ?? 0;
|
|
26
|
-
const paneId = options.paneId ?? 'sub';
|
|
27
|
-
let fastPeriod = 12;
|
|
28
|
-
let slowPeriod = 26;
|
|
29
|
-
let signalPeriod = 9;
|
|
30
|
-
let pluginHost = null;
|
|
31
|
-
const stateKey = createMACDStateKey(paneId);
|
|
32
|
-
return {
|
|
33
|
-
name: `macdLegend_${paneId}`,
|
|
34
|
-
version: '2.1.0',
|
|
35
|
-
description: 'MACD 图例渲染器(StateStore 版)',
|
|
36
|
-
debugName: 'MACD 图例',
|
|
37
|
-
paneId,
|
|
38
|
-
priority: RENDERER_PRIORITY.FOREGROUND,
|
|
39
|
-
onInstall(host) {
|
|
40
|
-
pluginHost = host;
|
|
41
|
-
},
|
|
42
|
-
draw(context) {
|
|
43
|
-
const { ctx, range } = context;
|
|
44
|
-
const colors = resolveThemeColors(context.theme, context.isAsiaMarket, context.colorPresetSettings);
|
|
45
|
-
// 从 StateStore 读取 MACD 状态
|
|
46
|
-
const state = pluginHost?.getSharedState(stateKey);
|
|
47
|
-
if (!state || state.visibleMin > state.visibleMax)
|
|
48
|
-
return;
|
|
49
|
-
// 获取最新值
|
|
50
|
-
let macdValue = null;
|
|
51
|
-
// 优先使用 latestValues(scheduler 已计算好)
|
|
52
|
-
if (state.latestValues) {
|
|
53
|
-
macdValue = state.latestValues;
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
// 回退:从 series 读取最后一个可见点
|
|
57
|
-
const lastIndex = Math.min(range.end - 1, state.series.length - 1);
|
|
58
|
-
const point = state.series[lastIndex];
|
|
59
|
-
if (point) {
|
|
60
|
-
macdValue = {
|
|
61
|
-
dif: point.dif,
|
|
62
|
-
dea: point.dea,
|
|
63
|
-
macd: point.macd,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
if (!macdValue)
|
|
68
|
-
return;
|
|
69
|
-
// 从 state.params 读取当前参数
|
|
70
|
-
const params = state.params;
|
|
71
|
-
fastPeriod = params.fastPeriod;
|
|
72
|
-
slowPeriod = params.slowPeriod;
|
|
73
|
-
signalPeriod = params.signalPeriod;
|
|
74
|
-
const fontSize = 11;
|
|
75
|
-
const gap = 12;
|
|
76
|
-
let x = 12;
|
|
77
|
-
const y = yPaddingPx + fontSize;
|
|
78
|
-
ctx.save();
|
|
79
|
-
setCanvasFont(ctx, getFont(fontSize));
|
|
80
|
-
ctx.textAlign = 'left';
|
|
81
|
-
ctx.textBaseline = 'top';
|
|
82
|
-
const paramText = `MACD(${fastPeriod},${slowPeriod},${signalPeriod})`;
|
|
83
|
-
ctx.fillStyle = colors.text.tertiary;
|
|
84
|
-
ctx.fillText(paramText, x, y);
|
|
85
|
-
x += measureTextWidth(ctx, paramText) + gap;
|
|
86
|
-
const difText = `DIF:${macdValue.dif.toFixed(2)}`;
|
|
87
|
-
ctx.fillStyle = colors.macd.dif;
|
|
88
|
-
ctx.fillText(difText, x, y);
|
|
89
|
-
x += measureTextWidth(ctx, difText) + gap;
|
|
90
|
-
const deaText = `DEA:${macdValue.dea.toFixed(2)}`;
|
|
91
|
-
ctx.fillStyle = colors.macd.dea;
|
|
92
|
-
ctx.fillText(deaText, x, y);
|
|
93
|
-
x += measureTextWidth(ctx, deaText) + gap;
|
|
94
|
-
const macdText = `MACD:${macdValue.macd.toFixed(2)}`;
|
|
95
|
-
ctx.fillStyle = macdValue.macd >= 0 ? colors.macd.barUp : colors.macd.barDown;
|
|
96
|
-
ctx.fillText(macdText, x, y);
|
|
97
|
-
ctx.restore();
|
|
98
|
-
},
|
|
99
|
-
getConfig() {
|
|
100
|
-
return {
|
|
101
|
-
fastPeriod,
|
|
102
|
-
slowPeriod,
|
|
103
|
-
signalPeriod,
|
|
104
|
-
};
|
|
105
|
-
},
|
|
106
|
-
setConfig(newConfig) {
|
|
107
|
-
if (typeof newConfig.fastPeriod === 'number')
|
|
108
|
-
fastPeriod = newConfig.fastPeriod;
|
|
109
|
-
if (typeof newConfig.slowPeriod === 'number')
|
|
110
|
-
slowPeriod = newConfig.slowPeriod;
|
|
111
|
-
if (typeof newConfig.signalPeriod === 'number')
|
|
112
|
-
signalPeriod = newConfig.signalPeriod;
|
|
113
|
-
},
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
//# sourceMappingURL=macdLegend.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"macdLegend.js","sourceRoot":"","sources":["../../../../src/engine/renderers/Indicator/macdLegend.ts"],"names":[],"mappings":"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"}
|