@363045841yyt/klinechart-core 0.7.8 → 0.7.10
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/index.d.ts +1 -1
- package/dist/controllers/index.d.ts.map +1 -1
- package/dist/controllers/index.js.map +1 -1
- package/dist/engine/chart.d.ts.map +1 -1
- package/dist/engine/chart.js +41 -229
- package/dist/engine/chart.js.map +1 -1
- package/dist/engine/indicators/indicatorDefinitionRegistry.d.ts +10 -3
- package/dist/engine/indicators/indicatorDefinitionRegistry.d.ts.map +1 -1
- package/dist/engine/indicators/indicatorDefinitionRegistry.js +29 -2
- package/dist/engine/indicators/indicatorDefinitionRegistry.js.map +1 -1
- package/dist/engine/indicators/indicatorMetadata.d.ts +79 -2
- package/dist/engine/indicators/indicatorMetadata.d.ts.map +1 -1
- package/dist/engine/indicators/indicatorMetadata.js.map +1 -1
- package/dist/engine/indicators/indicatorRegistry.d.ts +5 -0
- package/dist/engine/indicators/indicatorRegistry.d.ts.map +1 -1
- package/dist/engine/indicators/indicatorRegistry.js +49 -5
- package/dist/engine/indicators/indicatorRegistry.js.map +1 -1
- package/dist/engine/indicators/registerBuiltins.d.ts +42 -0
- package/dist/engine/indicators/registerBuiltins.d.ts.map +1 -0
- package/dist/engine/indicators/registerBuiltins.js +46 -0
- package/dist/engine/indicators/registerBuiltins.js.map +1 -0
- package/dist/engine/indicators/scheduler.d.ts +2 -2
- package/dist/engine/indicators/scheduler.d.ts.map +1 -1
- package/dist/engine/indicators/scheduler.js +41 -11
- package/dist/engine/indicators/scheduler.js.map +1 -1
- package/dist/engine/indicators/stateComposer.d.ts +6 -4
- package/dist/engine/indicators/stateComposer.d.ts.map +1 -1
- package/dist/engine/indicators/stateComposer.js +80 -984
- package/dist/engine/indicators/stateComposer.js.map +1 -1
- package/dist/engine/indicators/visibleStateComposers.d.ts +145 -0
- package/dist/engine/indicators/visibleStateComposers.d.ts.map +1 -0
- package/dist/engine/indicators/visibleStateComposers.js +469 -0
- package/dist/engine/indicators/visibleStateComposers.js.map +1 -0
- package/dist/engine/renderers/Indicator/atr.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/atr.js +8 -0
- package/dist/engine/renderers/Indicator/atr.js.map +1 -1
- package/dist/engine/renderers/Indicator/boll.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/boll.js +47 -0
- package/dist/engine/renderers/Indicator/boll.js.map +1 -1
- package/dist/engine/renderers/Indicator/cci.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/cci.js +8 -1
- package/dist/engine/renderers/Indicator/cci.js.map +1 -1
- package/dist/engine/renderers/Indicator/chaikinVol.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/chaikinVol.js +7 -1
- package/dist/engine/renderers/Indicator/chaikinVol.js.map +1 -1
- package/dist/engine/renderers/Indicator/cmf.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/cmf.js +7 -1
- package/dist/engine/renderers/Indicator/cmf.js.map +1 -1
- package/dist/engine/renderers/Indicator/dema.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/dema.js +8 -1
- package/dist/engine/renderers/Indicator/dema.js.map +1 -1
- package/dist/engine/renderers/Indicator/donchian.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/donchian.js +8 -1
- package/dist/engine/renderers/Indicator/donchian.js.map +1 -1
- package/dist/engine/renderers/Indicator/ene.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/ene.js +45 -0
- package/dist/engine/renderers/Indicator/ene.js.map +1 -1
- package/dist/engine/renderers/Indicator/expma.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/expma.js +45 -0
- package/dist/engine/renderers/Indicator/expma.js.map +1 -1
- package/dist/engine/renderers/Indicator/fastk.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/fastk.js +8 -1
- package/dist/engine/renderers/Indicator/fastk.js.map +1 -1
- package/dist/engine/renderers/Indicator/fib.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/fib.js +8 -1
- package/dist/engine/renderers/Indicator/fib.js.map +1 -1
- package/dist/engine/renderers/Indicator/hma.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/hma.js +8 -1
- package/dist/engine/renderers/Indicator/hma.js.map +1 -1
- package/dist/engine/renderers/Indicator/hv.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/hv.js +7 -0
- package/dist/engine/renderers/Indicator/hv.js.map +1 -1
- package/dist/engine/renderers/Indicator/ichimoku.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/ichimoku.js +8 -1
- package/dist/engine/renderers/Indicator/ichimoku.js.map +1 -1
- package/dist/engine/renderers/Indicator/index.d.ts +7 -2
- package/dist/engine/renderers/Indicator/index.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/index.js +2 -114
- package/dist/engine/renderers/Indicator/index.js.map +1 -1
- package/dist/engine/renderers/Indicator/kama.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/kama.js +8 -1
- package/dist/engine/renderers/Indicator/kama.js.map +1 -1
- package/dist/engine/renderers/Indicator/keltner.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/keltner.js +8 -1
- package/dist/engine/renderers/Indicator/keltner.js.map +1 -1
- package/dist/engine/renderers/Indicator/kst.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/kst.js +8 -0
- package/dist/engine/renderers/Indicator/kst.js.map +1 -1
- package/dist/engine/renderers/Indicator/ma.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/ma.js +63 -0
- package/dist/engine/renderers/Indicator/ma.js.map +1 -1
- package/dist/engine/renderers/Indicator/macd.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/macd.js +8 -1
- package/dist/engine/renderers/Indicator/macd.js.map +1 -1
- package/dist/engine/renderers/Indicator/mfi.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/mfi.js +7 -1
- package/dist/engine/renderers/Indicator/mfi.js.map +1 -1
- package/dist/engine/renderers/Indicator/mom.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/mom.js +8 -0
- package/dist/engine/renderers/Indicator/mom.js.map +1 -1
- package/dist/engine/renderers/Indicator/obv.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/obv.js +7 -1
- package/dist/engine/renderers/Indicator/obv.js.map +1 -1
- package/dist/engine/renderers/Indicator/parkinson.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/parkinson.js +7 -0
- package/dist/engine/renderers/Indicator/parkinson.js.map +1 -1
- package/dist/engine/renderers/Indicator/pivot.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/pivot.js +8 -1
- package/dist/engine/renderers/Indicator/pivot.js.map +1 -1
- package/dist/engine/renderers/Indicator/pvt.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/pvt.js +7 -1
- package/dist/engine/renderers/Indicator/pvt.js.map +1 -1
- package/dist/engine/renderers/Indicator/roc.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/roc.js +7 -1
- package/dist/engine/renderers/Indicator/roc.js.map +1 -1
- package/dist/engine/renderers/Indicator/rsi.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/rsi.js +8 -1
- package/dist/engine/renderers/Indicator/rsi.js.map +1 -1
- package/dist/engine/renderers/Indicator/sar.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/sar.js +8 -1
- package/dist/engine/renderers/Indicator/sar.js.map +1 -1
- package/dist/engine/renderers/Indicator/stoch.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/stoch.js +8 -1
- package/dist/engine/renderers/Indicator/stoch.js.map +1 -1
- package/dist/engine/renderers/Indicator/structure.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/structure.js +8 -1
- package/dist/engine/renderers/Indicator/structure.js.map +1 -1
- package/dist/engine/renderers/Indicator/subPaneConfig.d.ts +1 -1
- package/dist/engine/renderers/Indicator/subPaneConfig.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/subPaneConfig.js.map +1 -1
- package/dist/engine/renderers/Indicator/supertrend.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/supertrend.js +8 -1
- package/dist/engine/renderers/Indicator/supertrend.js.map +1 -1
- package/dist/engine/renderers/Indicator/tema.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/tema.js +8 -1
- package/dist/engine/renderers/Indicator/tema.js.map +1 -1
- package/dist/engine/renderers/Indicator/trix.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/trix.js +7 -0
- package/dist/engine/renderers/Indicator/trix.js.map +1 -1
- package/dist/engine/renderers/Indicator/vma.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/vma.js +7 -0
- package/dist/engine/renderers/Indicator/vma.js.map +1 -1
- package/dist/engine/renderers/Indicator/volumeProfile.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/volumeProfile.js +7 -1
- package/dist/engine/renderers/Indicator/volumeProfile.js.map +1 -1
- package/dist/engine/renderers/Indicator/vwap.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/vwap.js +7 -1
- package/dist/engine/renderers/Indicator/vwap.js.map +1 -1
- package/dist/engine/renderers/Indicator/wma.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/wma.js +8 -1
- package/dist/engine/renderers/Indicator/wma.js.map +1 -1
- package/dist/engine/renderers/Indicator/wmsr.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/wmsr.js +8 -1
- package/dist/engine/renderers/Indicator/wmsr.js.map +1 -1
- package/dist/engine/renderers/Indicator/zones.d.ts.map +1 -1
- package/dist/engine/renderers/Indicator/zones.js +8 -1
- package/dist/engine/renderers/Indicator/zones.js.map +1 -1
- package/dist/engine/renderers/extremaMarkers.d.ts.map +1 -1
- package/dist/engine/renderers/extremaMarkers.js +0 -22
- package/dist/engine/renderers/extremaMarkers.js.map +1 -1
- package/dist/engine/renderers/subVolume.d.ts.map +1 -1
- package/dist/engine/renderers/subVolume.js +3 -0
- package/dist/engine/renderers/subVolume.js.map +1 -1
- package/dist/engine/subPaneManager.d.ts +1 -0
- package/dist/engine/subPaneManager.d.ts.map +1 -1
- package/dist/engine/subPaneManager.js +30 -237
- package/dist/engine/subPaneManager.js.map +1 -1
- package/dist/semantic/controller.d.ts +9 -4
- package/dist/semantic/controller.d.ts.map +1 -1
- package/dist/semantic/controller.js +57 -53
- package/dist/semantic/controller.js.map +1 -1
- package/dist/tokens/theme-dark.js +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 +1 -1
- package/src/controllers/index.ts +7 -0
- package/src/engine/__tests__/chart.dpr.test.ts +2 -1
- package/src/engine/__tests__/paneRenderer.resize.test.ts +1 -0
- package/src/engine/chart.ts +51 -229
- package/src/engine/controller/__tests__/interaction.dpr.test.ts +1 -0
- package/src/engine/indicators/__tests__/indicatorDefinitionRegistry.test.ts +59 -0
- package/src/engine/indicators/__tests__/indicatorRegistry.test.ts +62 -0
- package/src/engine/indicators/__tests__/registerBuiltins.test.ts +380 -0
- package/src/engine/indicators/__tests__/scheduler.test.ts +92 -8
- package/src/engine/indicators/__tests__/stateComposer.test.ts +444 -0
- package/src/engine/indicators/indicatorDefinitionRegistry.ts +49 -4
- package/src/engine/indicators/indicatorMetadata.ts +107 -2
- package/src/engine/indicators/indicatorRegistry.ts +54 -5
- package/src/engine/indicators/registerBuiltins.ts +47 -0
- package/src/engine/indicators/scheduler.ts +61 -13
- package/src/engine/indicators/stateComposer.ts +110 -1040
- package/src/engine/indicators/visibleStateComposers.ts +734 -0
- package/src/engine/renderers/Indicator/__tests__/createSubIndicatorRenderer.test.ts +61 -0
- package/src/engine/renderers/Indicator/atr.ts +9 -1
- package/src/engine/renderers/Indicator/boll.ts +53 -1
- package/src/engine/renderers/Indicator/cci.ts +9 -2
- package/src/engine/renderers/Indicator/chaikinVol.ts +8 -2
- package/src/engine/renderers/Indicator/cmf.ts +8 -2
- package/src/engine/renderers/Indicator/dema.ts +9 -2
- package/src/engine/renderers/Indicator/donchian.ts +9 -2
- package/src/engine/renderers/Indicator/ene.ts +51 -1
- package/src/engine/renderers/Indicator/expma.ts +51 -1
- package/src/engine/renderers/Indicator/fastk.ts +9 -2
- package/src/engine/renderers/Indicator/fib.ts +9 -2
- package/src/engine/renderers/Indicator/hma.ts +9 -2
- package/src/engine/renderers/Indicator/hv.ts +8 -1
- package/src/engine/renderers/Indicator/ichimoku.ts +9 -2
- package/src/engine/renderers/Indicator/index.ts +9 -121
- package/src/engine/renderers/Indicator/kama.ts +9 -2
- package/src/engine/renderers/Indicator/keltner.ts +9 -2
- package/src/engine/renderers/Indicator/kst.ts +9 -1
- package/src/engine/renderers/Indicator/ma.ts +69 -0
- package/src/engine/renderers/Indicator/macd.ts +9 -2
- package/src/engine/renderers/Indicator/mfi.ts +8 -2
- package/src/engine/renderers/Indicator/mom.ts +9 -1
- package/src/engine/renderers/Indicator/obv.ts +8 -2
- package/src/engine/renderers/Indicator/parkinson.ts +8 -1
- package/src/engine/renderers/Indicator/pivot.ts +9 -2
- package/src/engine/renderers/Indicator/pvt.ts +8 -2
- package/src/engine/renderers/Indicator/roc.ts +8 -2
- package/src/engine/renderers/Indicator/rsi.ts +9 -2
- package/src/engine/renderers/Indicator/sar.ts +9 -2
- package/src/engine/renderers/Indicator/stoch.ts +9 -2
- package/src/engine/renderers/Indicator/structure.ts +9 -2
- package/src/engine/renderers/Indicator/subPaneConfig.ts +1 -1
- package/src/engine/renderers/Indicator/supertrend.ts +9 -2
- package/src/engine/renderers/Indicator/tema.ts +9 -2
- package/src/engine/renderers/Indicator/trix.ts +8 -1
- package/src/engine/renderers/Indicator/vma.ts +8 -1
- package/src/engine/renderers/Indicator/volumeProfile.ts +8 -2
- package/src/engine/renderers/Indicator/vwap.ts +8 -2
- package/src/engine/renderers/Indicator/wma.ts +9 -2
- package/src/engine/renderers/Indicator/wmsr.ts +9 -2
- package/src/engine/renderers/Indicator/zones.ts +9 -2
- package/src/engine/renderers/__tests__/boll.renderer.test.ts +9 -1
- package/src/engine/renderers/__tests__/ene.renderer.test.ts +2 -1
- package/src/engine/renderers/__tests__/expma.renderer.test.ts +0 -1
- package/src/engine/renderers/__tests__/ma.renderer.test.ts +0 -1
- package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +0 -1
- package/src/engine/renderers/__tests__/yAxis.renderer.test.ts +3 -0
- package/src/engine/renderers/extremaMarkers.ts +0 -24
- package/src/engine/renderers/subVolume.ts +3 -0
- package/src/engine/subPaneManager.ts +34 -274
- package/src/semantic/__tests__/controller.test.ts +84 -0
- package/src/semantic/controller.ts +69 -65
- package/src/tokens/__tests__/__snapshots__/baseline.test.ts.snap +4 -2
- package/src/tokens/__tests__/tokens.test.ts +1 -0
- package/src/tokens/theme-dark.ts +1 -1
- package/src/version.ts +1 -1
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { getRegisteredIndicatorDefinition } from '../indicatorDefinitionRegistry'
|
|
3
|
+
import { getBuiltinIndicatorDefinitions } from '../registerBuiltins'
|
|
4
|
+
|
|
5
|
+
describe('builtin indicator registration', () => {
|
|
6
|
+
it('loads all builtin indicator definitions through decorators', () => {
|
|
7
|
+
const definitions = getBuiltinIndicatorDefinitions()
|
|
8
|
+
|
|
9
|
+
expect(definitions).toHaveLength(40)
|
|
10
|
+
expect(definitions.map((definition) => definition.name)).toEqual(
|
|
11
|
+
expect.arrayContaining([
|
|
12
|
+
'ma',
|
|
13
|
+
'boll',
|
|
14
|
+
'rsi',
|
|
15
|
+
'macd',
|
|
16
|
+
'volume',
|
|
17
|
+
'volumeProfile',
|
|
18
|
+
'zones',
|
|
19
|
+
]),
|
|
20
|
+
)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('allows builtin definitions to be queried by display name', () => {
|
|
24
|
+
expect(getRegisteredIndicatorDefinition('RSI')?.name).toBe('rsi')
|
|
25
|
+
expect(getRegisteredIndicatorDefinition('MACD')?.name).toBe('macd')
|
|
26
|
+
expect(getRegisteredIndicatorDefinition('VOL')?.name).toBe('volume')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('registers metadata config updaters for stage 4A indicators', () => {
|
|
30
|
+
expect(getRegisteredIndicatorDefinition('RSI')?.updateConfig).toBeTypeOf('function')
|
|
31
|
+
expect(getRegisteredIndicatorDefinition('MACD')?.updateConfig).toBeTypeOf('function')
|
|
32
|
+
expect(getRegisteredIndicatorDefinition('VOL')?.updateConfig).toBeTypeOf('function')
|
|
33
|
+
expect(getRegisteredIndicatorDefinition('VOLUME_PROFILE')?.updateConfig).toBeTypeOf('function')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('registers metadata config updaters for stage 4B indicators', () => {
|
|
37
|
+
const expectedIndicators = [
|
|
38
|
+
'CCI', 'STOCH', 'MOM', 'WMSR', 'KST', 'FASTK', 'ATR',
|
|
39
|
+
'WMA', 'DEMA', 'TEMA', 'HMA', 'KAMA', 'SAR',
|
|
40
|
+
'SUPERTREND', 'KELTNER', 'DONCHIAN', 'ICHIMOKU',
|
|
41
|
+
'ROC', 'TRIX', 'HV', 'PARKINSON', 'CHAIKIN_VOL',
|
|
42
|
+
'VMA', 'OBV', 'PVT', 'VWAP', 'CMF', 'MFI',
|
|
43
|
+
'PIVOT', 'FIB', 'STRUCTURE', 'ZONES',
|
|
44
|
+
]
|
|
45
|
+
for (const id of expectedIndicators) {
|
|
46
|
+
expect(getRegisteredIndicatorDefinition(id)?.updateConfig).toBeTypeOf('function')
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('routes stage 4A metadata config updates to scheduler methods', () => {
|
|
51
|
+
const scheduler = {
|
|
52
|
+
updateRSIConfig: vi.fn(),
|
|
53
|
+
updateMACDConfig: vi.fn(),
|
|
54
|
+
updateVolumeProfileConfig: vi.fn(),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
getRegisteredIndicatorDefinition('RSI')?.updateConfig?.(scheduler, { period1: 7 }, 'RSI_0')
|
|
58
|
+
getRegisteredIndicatorDefinition('MACD')?.updateConfig?.(scheduler, { fastPeriod: 8 }, 'MACD_0')
|
|
59
|
+
getRegisteredIndicatorDefinition('VOLUME_PROFILE')?.updateConfig?.(scheduler, { bins: 32 }, 'VP_0')
|
|
60
|
+
getRegisteredIndicatorDefinition('VOL')?.updateConfig?.(scheduler, {}, 'VOL_0')
|
|
61
|
+
|
|
62
|
+
expect(scheduler.updateRSIConfig).toHaveBeenCalledWith({ period1: 7 }, 'RSI_0')
|
|
63
|
+
expect(scheduler.updateMACDConfig).toHaveBeenCalledWith({ fastPeriod: 8 }, 'MACD_0')
|
|
64
|
+
expect(scheduler.updateVolumeProfileConfig).toHaveBeenCalledWith({ bins: 32 }, 'VP_0')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('routes stage 4B metadata config updates to scheduler methods', () => {
|
|
68
|
+
const scheduler = {
|
|
69
|
+
updateCCIConfig: vi.fn(),
|
|
70
|
+
updateATRConfig: vi.fn(),
|
|
71
|
+
updateChaikinVolConfig: vi.fn(),
|
|
72
|
+
updateZonesConfig: vi.fn(),
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
getRegisteredIndicatorDefinition('CCI')?.updateConfig?.(scheduler, { period: 14 }, 'CCI_0')
|
|
76
|
+
getRegisteredIndicatorDefinition('ATR')?.updateConfig?.(scheduler, { period: 10 }, 'ATR_0')
|
|
77
|
+
getRegisteredIndicatorDefinition('CHAIKIN_VOL')?.updateConfig?.(scheduler, { emaPeriod: 10 }, 'CV_0')
|
|
78
|
+
getRegisteredIndicatorDefinition('ZONES')?.updateConfig?.(scheduler, { showFVG: true }, 'Z_0')
|
|
79
|
+
|
|
80
|
+
expect(scheduler.updateCCIConfig).toHaveBeenCalledWith({ period: 14 }, 'CCI_0')
|
|
81
|
+
expect(scheduler.updateATRConfig).toHaveBeenCalledWith({ period: 10 }, 'ATR_0')
|
|
82
|
+
expect(scheduler.updateChaikinVolConfig).toHaveBeenCalledWith({ emaPeriod: 10 }, 'CV_0')
|
|
83
|
+
expect(scheduler.updateZonesConfig).toHaveBeenCalledWith({ showFVG: true }, 'Z_0')
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it('registers dedicated scale renderer factories for stage 5A indicators', () => {
|
|
87
|
+
const expectedIndicators = [
|
|
88
|
+
'VOL', 'MACD', 'RSI', 'CCI', 'STOCH', 'MOM', 'WMSR', 'KST', 'FASTK', 'ATR',
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
for (const id of expectedIndicators) {
|
|
92
|
+
expect(getRegisteredIndicatorDefinition(id)?.scaleRendererFactory).toBeTypeOf('function')
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('creates scale renderers through stage 5A metadata factories', () => {
|
|
97
|
+
const rsiScaleRenderer = getRegisteredIndicatorDefinition('RSI')?.scaleRendererFactory?.({
|
|
98
|
+
indicatorId: 'RSI',
|
|
99
|
+
paneId: 'RSI_0',
|
|
100
|
+
axisWidth: 80,
|
|
101
|
+
yPaddingPx: 4,
|
|
102
|
+
getCrosshair: () => null,
|
|
103
|
+
})
|
|
104
|
+
const volumeScaleRenderer = getRegisteredIndicatorDefinition('VOL')?.scaleRendererFactory?.({
|
|
105
|
+
indicatorId: 'VOLUME',
|
|
106
|
+
paneId: 'VOLUME_0',
|
|
107
|
+
axisWidth: 80,
|
|
108
|
+
yPaddingPx: 4,
|
|
109
|
+
getCrosshair: () => null,
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
expect(rsiScaleRenderer?.name).toBe('rsiScale_RSI_0')
|
|
113
|
+
expect(volumeScaleRenderer?.name).toBe('volumeScale_VOLUME_0')
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('registers generic scale metadata for stage 5B indicators', () => {
|
|
117
|
+
const expected: Record<string, { indicatorKey: string; label: string; decimals: number }> = {
|
|
118
|
+
WMA: { indicatorKey: 'wma', label: 'WMA', decimals: 2 },
|
|
119
|
+
DEMA: { indicatorKey: 'dema', label: 'DEMA', decimals: 2 },
|
|
120
|
+
TEMA: { indicatorKey: 'tema', label: 'TEMA', decimals: 2 },
|
|
121
|
+
HMA: { indicatorKey: 'hma', label: 'HMA', decimals: 2 },
|
|
122
|
+
KAMA: { indicatorKey: 'kama', label: 'KAMA', decimals: 2 },
|
|
123
|
+
SAR: { indicatorKey: 'sar', label: 'SAR', decimals: 4 },
|
|
124
|
+
SUPERTREND: { indicatorKey: 'supertrend', label: 'SuperTrend', decimals: 2 },
|
|
125
|
+
KELTNER: { indicatorKey: 'keltner', label: 'Keltner', decimals: 2 },
|
|
126
|
+
DONCHIAN: { indicatorKey: 'donchian', label: 'Donchian', decimals: 2 },
|
|
127
|
+
ICHIMOKU: { indicatorKey: 'ichimoku', label: 'Ichimoku', decimals: 2 },
|
|
128
|
+
ROC: { indicatorKey: 'roc', label: 'ROC', decimals: 2 },
|
|
129
|
+
TRIX: { indicatorKey: 'trix', label: 'TRIX', decimals: 6 },
|
|
130
|
+
HV: { indicatorKey: 'hv', label: 'HV', decimals: 2 },
|
|
131
|
+
PARKINSON: { indicatorKey: 'parkinson', label: 'Parkinson', decimals: 2 },
|
|
132
|
+
CHAIKIN_VOL: { indicatorKey: 'chaikinVol', label: 'ChaikinVol', decimals: 2 },
|
|
133
|
+
VMA: { indicatorKey: 'vma', label: 'VMA', decimals: 0 },
|
|
134
|
+
OBV: { indicatorKey: 'obv', label: 'OBV', decimals: 0 },
|
|
135
|
+
PVT: { indicatorKey: 'pvt', label: 'PVT', decimals: 0 },
|
|
136
|
+
VWAP: { indicatorKey: 'vwap', label: 'VWAP', decimals: 2 },
|
|
137
|
+
CMF: { indicatorKey: 'cmf', label: 'CMF', decimals: 4 },
|
|
138
|
+
MFI: { indicatorKey: 'mfi', label: 'MFI', decimals: 2 },
|
|
139
|
+
PIVOT: { indicatorKey: 'pivot', label: 'Pivot', decimals: 2 },
|
|
140
|
+
FIB: { indicatorKey: 'fib', label: 'Fib', decimals: 4 },
|
|
141
|
+
STRUCTURE: { indicatorKey: 'structure', label: 'Structure', decimals: 2 },
|
|
142
|
+
ZONES: { indicatorKey: 'zones', label: 'Zones', decimals: 2 },
|
|
143
|
+
VOLUME_PROFILE: { indicatorKey: 'volumeProfile', label: 'VP', decimals: 0 },
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
for (const [id, scale] of Object.entries(expected)) {
|
|
147
|
+
expect(getRegisteredIndicatorDefinition(id)?.scale).toEqual(scale)
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('creates generic scale renderers through stage 5B metadata', () => {
|
|
152
|
+
const definition = getRegisteredIndicatorDefinition('WMA')
|
|
153
|
+
expect(definition?.scale).toBeDefined()
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('registers main pane renderer metadata for stage 6A-1 indicators', () => {
|
|
157
|
+
const expected: Record<string, string> = {
|
|
158
|
+
MA: 'ma',
|
|
159
|
+
BOLL: 'boll',
|
|
160
|
+
EXPMA: 'expma',
|
|
161
|
+
ENE: 'ene',
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (const [id, rendererName] of Object.entries(expected)) {
|
|
165
|
+
expect(getRegisteredIndicatorDefinition(id)?.mainPane?.rendererName).toBe(rendererName)
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('creates main pane renderers through stage 6A-1 metadata factories', () => {
|
|
170
|
+
const maRenderer = getRegisteredIndicatorDefinition('MA')?.rendererFactory({ paneId: 'main', indicatorId: 'MA' })
|
|
171
|
+
const bollRenderer = getRegisteredIndicatorDefinition('BOLL')?.rendererFactory({ paneId: 'main', indicatorId: 'BOLL' })
|
|
172
|
+
|
|
173
|
+
expect(maRenderer?.name).toBe('ma')
|
|
174
|
+
expect(bollRenderer?.name).toBe('boll')
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('registers main pane renderer metadata for stage 6A-2 indicators', () => {
|
|
178
|
+
const expected: Record<string, string> = {
|
|
179
|
+
WMA: 'wma_main',
|
|
180
|
+
DEMA: 'dema_main',
|
|
181
|
+
TEMA: 'tema_main',
|
|
182
|
+
HMA: 'hma_main',
|
|
183
|
+
KAMA: 'kama_main',
|
|
184
|
+
SAR: 'sar_main',
|
|
185
|
+
SUPERTREND: 'supertrend_main',
|
|
186
|
+
KELTNER: 'keltner_main',
|
|
187
|
+
DONCHIAN: 'donchian_main',
|
|
188
|
+
ICHIMOKU: 'ichimoku_main',
|
|
189
|
+
PIVOT: 'pivot_main',
|
|
190
|
+
FIB: 'fib_main',
|
|
191
|
+
STRUCTURE: 'structure_main',
|
|
192
|
+
ZONES: 'zones_main',
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
for (const [id, rendererName] of Object.entries(expected)) {
|
|
196
|
+
expect(getRegisteredIndicatorDefinition(id)?.mainPane?.rendererName).toBe(rendererName)
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('creates overlay main pane renderers through stage 6A-2 metadata factories', () => {
|
|
201
|
+
const wma = getRegisteredIndicatorDefinition('WMA')?.rendererFactory({ paneId: 'main', indicatorId: 'WMA' })
|
|
202
|
+
const zones = getRegisteredIndicatorDefinition('ZONES')?.rendererFactory({ paneId: 'main', indicatorId: 'ZONES' })
|
|
203
|
+
|
|
204
|
+
expect(wma?.name).toBe('wma_main')
|
|
205
|
+
expect(zones?.name).toBe('zones_main')
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('registers overlay main config metadata for stage 6B-2 indicators', () => {
|
|
209
|
+
const ids = [
|
|
210
|
+
'WMA', 'DEMA', 'TEMA', 'HMA', 'KAMA',
|
|
211
|
+
'SAR', 'SUPERTREND', 'KELTNER', 'DONCHIAN',
|
|
212
|
+
'ICHIMOKU', 'PIVOT', 'FIB', 'STRUCTURE', 'ZONES',
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
for (const id of ids) {
|
|
216
|
+
expect(getRegisteredIndicatorDefinition(id)?.mainPane?.toActiveConfig).toBeTypeOf('function')
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it('builds overlay main active configs through stage 6B-2 metadata', () => {
|
|
221
|
+
expect(getRegisteredIndicatorDefinition('WMA')?.mainPane?.toActiveConfig?.({ period: 10 }, false)).toEqual({
|
|
222
|
+
period: 10, showWMA: false,
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
expect(getRegisteredIndicatorDefinition('KELTNER')?.mainPane?.toActiveConfig?.({ emaPeriod: 20 }, false)).toEqual({
|
|
226
|
+
emaPeriod: 20, showUpper: false, showMiddle: false, showLower: false,
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
expect(getRegisteredIndicatorDefinition('ICHIMOKU')?.mainPane?.toActiveConfig?.({}, false)).toEqual({
|
|
230
|
+
showTenkan: false, showKijun: false, showSpanA: false,
|
|
231
|
+
showSpanB: false, showChikou: false, showCloud: false,
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
expect(getRegisteredIndicatorDefinition('ZONES')?.mainPane?.toActiveConfig?.({}, false)).toEqual({
|
|
235
|
+
showFVG: false, showOB: false, showFilledZones: false,
|
|
236
|
+
})
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
it('registers base main config metadata for stage 6B-1 indicators', () => {
|
|
240
|
+
for (const id of ['MA', 'BOLL', 'EXPMA', 'ENE']) {
|
|
241
|
+
const definition = getRegisteredIndicatorDefinition(id)
|
|
242
|
+
expect(definition?.updateConfig).toBeTypeOf('function')
|
|
243
|
+
expect(definition?.mainPane?.toActiveConfig).toBeTypeOf('function')
|
|
244
|
+
}
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
it('builds base main active configs through stage 6B-1 metadata', () => {
|
|
248
|
+
expect(getRegisteredIndicatorDefinition('MA')?.mainPane?.toActiveConfig?.({}, false)).toEqual({
|
|
249
|
+
ma5: false,
|
|
250
|
+
ma10: false,
|
|
251
|
+
ma20: false,
|
|
252
|
+
ma30: false,
|
|
253
|
+
ma60: false,
|
|
254
|
+
})
|
|
255
|
+
expect(getRegisteredIndicatorDefinition('BOLL')?.mainPane?.toActiveConfig?.({ period: 20 }, false)).toEqual({
|
|
256
|
+
period: 20,
|
|
257
|
+
showUpper: false,
|
|
258
|
+
showMiddle: false,
|
|
259
|
+
showLower: false,
|
|
260
|
+
showBand: false,
|
|
261
|
+
})
|
|
262
|
+
expect(getRegisteredIndicatorDefinition('EXPMA')?.mainPane?.toActiveConfig?.({}, false)).toBeNull()
|
|
263
|
+
expect(getRegisteredIndicatorDefinition('ENE')?.mainPane?.toActiveConfig?.({}, false)).toBeNull()
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
it('routes base main metadata config updates to scheduler methods', () => {
|
|
267
|
+
const scheduler = {
|
|
268
|
+
updateMAConfig: vi.fn(),
|
|
269
|
+
updateBOLLConfig: vi.fn(),
|
|
270
|
+
updateEXPMAConfig: vi.fn(),
|
|
271
|
+
updateENEConfig: vi.fn(),
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
getRegisteredIndicatorDefinition('MA')?.updateConfig?.(scheduler, { ma5: true }, 'main')
|
|
275
|
+
getRegisteredIndicatorDefinition('BOLL')?.updateConfig?.(scheduler, { period: 20 }, 'main')
|
|
276
|
+
getRegisteredIndicatorDefinition('EXPMA')?.updateConfig?.(scheduler, { fastPeriod: 12 }, 'main')
|
|
277
|
+
getRegisteredIndicatorDefinition('ENE')?.updateConfig?.(scheduler, { period: 10 }, 'main')
|
|
278
|
+
|
|
279
|
+
expect(scheduler.updateMAConfig).toHaveBeenCalledWith({ ma5: true })
|
|
280
|
+
expect(scheduler.updateBOLLConfig).toHaveBeenCalledWith({ period: 20 })
|
|
281
|
+
expect(scheduler.updateEXPMAConfig).toHaveBeenCalledWith({ fastPeriod: 12 })
|
|
282
|
+
expect(scheduler.updateENEConfig).toHaveBeenCalledWith({ period: 10 })
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
it('registers semantic apply metadata for stage 7A main indicators', () => {
|
|
286
|
+
for (const id of ['MA', 'BOLL', 'EXPMA', 'ENE']) {
|
|
287
|
+
expect(getRegisteredIndicatorDefinition(id)?.semantic?.apply).toBeTypeOf('function')
|
|
288
|
+
}
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
it('routes semantic main indicator configs through stage 7A metadata', () => {
|
|
292
|
+
const chart = { updateRendererConfig: vi.fn() }
|
|
293
|
+
|
|
294
|
+
getRegisteredIndicatorDefinition('MA')?.semantic?.apply?.(chart, {
|
|
295
|
+
type: 'MA',
|
|
296
|
+
enabled: true,
|
|
297
|
+
params: { periods: [5, 20, 99] },
|
|
298
|
+
})
|
|
299
|
+
getRegisteredIndicatorDefinition('BOLL')?.semantic?.apply?.(chart, {
|
|
300
|
+
type: 'BOLL',
|
|
301
|
+
enabled: true,
|
|
302
|
+
params: { period: 21, multiplier: 2.5 },
|
|
303
|
+
})
|
|
304
|
+
getRegisteredIndicatorDefinition('EXPMA')?.semantic?.apply?.(chart, {
|
|
305
|
+
type: 'EXPMA',
|
|
306
|
+
enabled: true,
|
|
307
|
+
params: {},
|
|
308
|
+
})
|
|
309
|
+
getRegisteredIndicatorDefinition('ENE')?.semantic?.apply?.(chart, {
|
|
310
|
+
type: 'ENE',
|
|
311
|
+
enabled: true,
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
expect(chart.updateRendererConfig).toHaveBeenCalledWith('ma', { ma5: true, ma20: true })
|
|
315
|
+
expect(chart.updateRendererConfig).toHaveBeenCalledWith('boll', { period: 21, multiplier: 2.5 })
|
|
316
|
+
expect(chart.updateRendererConfig).toHaveBeenCalledWith('expma', { fastPeriod: 12, slowPeriod: 50 })
|
|
317
|
+
expect(chart.updateRendererConfig).toHaveBeenCalledWith('ene', { period: 10, deviation: 11 })
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
it('registers main pane price range metadata for stage 8A indicators', () => {
|
|
321
|
+
for (const id of ['MA', 'BOLL', 'EXPMA', 'ENE']) {
|
|
322
|
+
expect(getRegisteredIndicatorDefinition(id)?.mainPane?.computePriceRange).toBeTypeOf('function')
|
|
323
|
+
}
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
it('registers main pane render state composer metadata for stage 8B indicators', () => {
|
|
327
|
+
for (const id of ['MA', 'BOLL', 'EXPMA', 'ENE']) {
|
|
328
|
+
expect(getRegisteredIndicatorDefinition(id)?.mainPane?.composeRenderState).toBeTypeOf('function')
|
|
329
|
+
}
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
it('registers visible state composer metadata for stage 8C-A indicators', () => {
|
|
333
|
+
for (const id of ['WMA', 'DEMA', 'TEMA', 'HMA', 'KAMA', 'ROC', 'ChaikinVol', 'OBV', 'PVT', 'VWAP']) {
|
|
334
|
+
expect(getRegisteredIndicatorDefinition(id)?.visibleState?.compose).toBeTypeOf('function')
|
|
335
|
+
}
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
it('registers visible state composer metadata for stage 8C-B indicators', () => {
|
|
339
|
+
for (const id of ['RSI', 'STOCH', 'FASTK', 'MFI', 'WMSR', 'CMF']) {
|
|
340
|
+
expect(getRegisteredIndicatorDefinition(id)?.visibleState?.compose).toBeTypeOf('function')
|
|
341
|
+
}
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
it('registers visible state composer metadata for stage 8C-C indicators', () => {
|
|
345
|
+
for (const id of ['MOM', 'KST', 'ATR', 'HV', 'PARKINSON', 'VMA', 'TRIX']) {
|
|
346
|
+
expect(getRegisteredIndicatorDefinition(id)?.visibleState?.compose).toBeTypeOf('function')
|
|
347
|
+
}
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
it('registers visible state composer metadata for stage 8C-D1 MACD indicator', () => {
|
|
351
|
+
expect(getRegisteredIndicatorDefinition('MACD')?.visibleState?.compose).toBeTypeOf('function')
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
it('registers visible state composer metadata for stage 8C-D2 overlay indicators', () => {
|
|
355
|
+
for (const id of ['SAR', 'SUPERTREND', 'KELTNER', 'DONCHIAN', 'ICHIMOKU', 'PIVOT', 'FIB']) {
|
|
356
|
+
expect(getRegisteredIndicatorDefinition(id)?.visibleState?.compose).toBeTypeOf('function')
|
|
357
|
+
}
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
it('registers visible state composer metadata for stage 8C-D2-B structure indicators', () => {
|
|
361
|
+
for (const id of ['STRUCTURE', 'ZONES', 'VOLUME_PROFILE']) {
|
|
362
|
+
expect(getRegisteredIndicatorDefinition(id)?.visibleState?.compose).toBeTypeOf('function')
|
|
363
|
+
}
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
it('registers visible state composer metadata for all composed visible sub indicators', () => {
|
|
367
|
+
const ids = [
|
|
368
|
+
'RSI', 'CCI', 'STOCH', 'MOM', 'WMSR', 'FASTK', 'MACD',
|
|
369
|
+
'ATR', 'WMA', 'DEMA', 'TEMA', 'HMA', 'KAMA',
|
|
370
|
+
'SAR', 'SUPERTREND', 'KELTNER', 'DONCHIAN', 'ICHIMOKU',
|
|
371
|
+
'ROC', 'TRIX', 'HV', 'PARKINSON', 'CHAIKIN_VOL',
|
|
372
|
+
'VMA', 'OBV', 'PVT', 'VWAP', 'CMF', 'MFI',
|
|
373
|
+
'KST', 'PIVOT', 'FIB', 'STRUCTURE', 'ZONES', 'VOLUME_PROFILE',
|
|
374
|
+
]
|
|
375
|
+
|
|
376
|
+
for (const id of ids) {
|
|
377
|
+
expect(getRegisteredIndicatorDefinition(id)?.visibleState?.compose).toBeTypeOf('function')
|
|
378
|
+
}
|
|
379
|
+
})
|
|
380
|
+
})
|
|
@@ -1,13 +1,66 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
2
2
|
import { IndicatorScheduler } from '../scheduler'
|
|
3
|
+
import { getBuiltinIndicatorDefinitions } from '../registerBuiltins'
|
|
3
4
|
import { MA_STATE_KEY, EMPTY_MA_STATE, type MARenderState } from '../maState'
|
|
4
5
|
import { BOLL_STATE_KEY, EMPTY_BOLL_STATE, type BOLLRenderState } from '../bollState'
|
|
5
6
|
import { EXPMA_STATE_KEY, EMPTY_EXPMA_STATE, type EXPMARenderState } from '../expmaState'
|
|
6
7
|
import { ENE_STATE_KEY, EMPTY_ENE_STATE, type ENERenderState } from '../eneState'
|
|
7
8
|
import { createRSIStateKey, EMPTY_RSI_STATE, type RSIRenderState } from '../rsiState'
|
|
9
|
+
import type { IndicatorMetadata } from '../indicatorMetadata'
|
|
8
10
|
import type { KLineData } from '@/types/price'
|
|
9
11
|
import type { PluginHost } from '@/plugin'
|
|
10
12
|
|
|
13
|
+
function applyMainResult(key: string): NonNullable<IndicatorMetadata['applyResult']> {
|
|
14
|
+
return (host, state, _paneId) => {
|
|
15
|
+
;(host as PluginHost).setSharedState(key, state as any, 'indicator_scheduler')
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const builtinDefinitionsByName = new Map(getBuiltinIndicatorDefinitions().map((definition) => [definition.name, definition]))
|
|
20
|
+
|
|
21
|
+
function getBuiltinTestIndicator(name: string): IndicatorMetadata {
|
|
22
|
+
const definition = builtinDefinitionsByName.get(name)
|
|
23
|
+
if (!definition) {
|
|
24
|
+
throw new Error(`Missing builtin test indicator: ${name}`)
|
|
25
|
+
}
|
|
26
|
+
return definition
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function registerTestIndicators(scheduler: IndicatorScheduler): void {
|
|
30
|
+
const indicators: Array<IndicatorMetadata> = [
|
|
31
|
+
getBuiltinTestIndicator('ma'),
|
|
32
|
+
getBuiltinTestIndicator('boll'),
|
|
33
|
+
getBuiltinTestIndicator('expma'),
|
|
34
|
+
getBuiltinTestIndicator('ene'),
|
|
35
|
+
getBuiltinTestIndicator('rsi'),
|
|
36
|
+
getBuiltinTestIndicator('macd'),
|
|
37
|
+
{ name: 'volume', displayName: 'Volume', category: 'sub' as const, stateKey: 'indicator:volume:sub_Volume', defaultPaneId: 'sub_Volume', rendererFactory: vi.fn() as any, paneIdField: 'volumePaneId' as any, applyResult: applyMainResult('indicator:volume:sub_Volume') },
|
|
38
|
+
getBuiltinTestIndicator('stoch'),
|
|
39
|
+
getBuiltinTestIndicator('wmsr'),
|
|
40
|
+
getBuiltinTestIndicator('mom'),
|
|
41
|
+
getBuiltinTestIndicator('fastk'),
|
|
42
|
+
getBuiltinTestIndicator('cci'),
|
|
43
|
+
getBuiltinTestIndicator('atr'),
|
|
44
|
+
getBuiltinTestIndicator('kst'),
|
|
45
|
+
getBuiltinTestIndicator('roc'),
|
|
46
|
+
getBuiltinTestIndicator('trix'),
|
|
47
|
+
getBuiltinTestIndicator('hv'),
|
|
48
|
+
getBuiltinTestIndicator('parkinson'),
|
|
49
|
+
getBuiltinTestIndicator('chaikinVol'),
|
|
50
|
+
getBuiltinTestIndicator('vma'),
|
|
51
|
+
getBuiltinTestIndicator('obv'),
|
|
52
|
+
getBuiltinTestIndicator('pvt'),
|
|
53
|
+
getBuiltinTestIndicator('vwap'),
|
|
54
|
+
getBuiltinTestIndicator('cmf'),
|
|
55
|
+
getBuiltinTestIndicator('mfi'),
|
|
56
|
+
getBuiltinTestIndicator('sar'),
|
|
57
|
+
getBuiltinTestIndicator('keltner'),
|
|
58
|
+
]
|
|
59
|
+
for (const meta of indicators) {
|
|
60
|
+
scheduler.registerIndicator(meta)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
11
64
|
/**
|
|
12
65
|
* 创建测试用的 K 线数据
|
|
13
66
|
*/
|
|
@@ -76,6 +129,8 @@ describe('IndicatorScheduler', () => {
|
|
|
76
129
|
scheduler = new IndicatorScheduler()
|
|
77
130
|
mockHost = createMockPluginHost()
|
|
78
131
|
scheduler.setPluginHost(mockHost)
|
|
132
|
+
registerTestIndicators(scheduler)
|
|
133
|
+
vi.mocked(mockHost.setSharedState).mockClear()
|
|
79
134
|
})
|
|
80
135
|
|
|
81
136
|
describe('initialization', () => {
|
|
@@ -107,7 +162,7 @@ describe('IndicatorScheduler', () => {
|
|
|
107
162
|
visibleMin: expect.any(Number),
|
|
108
163
|
visibleMax: expect.any(Number),
|
|
109
164
|
}),
|
|
110
|
-
|
|
165
|
+
expect.any(String)
|
|
111
166
|
)
|
|
112
167
|
})
|
|
113
168
|
|
|
@@ -154,6 +209,28 @@ describe('IndicatorScheduler', () => {
|
|
|
154
209
|
expect(state!.visibleMin).toBe(Infinity)
|
|
155
210
|
expect(state!.visibleMax).toBe(-Infinity)
|
|
156
211
|
})
|
|
212
|
+
|
|
213
|
+
it('should refresh main indicator visible extremes when visible range changes', () => {
|
|
214
|
+
const data = createTestData(100, 100)
|
|
215
|
+
const invalidate = vi.fn()
|
|
216
|
+
scheduler.setInvalidateCallback(invalidate)
|
|
217
|
+
|
|
218
|
+
scheduler.update(data, { start: 0, end: 0 })
|
|
219
|
+
invalidate.mockClear()
|
|
220
|
+
const initialState = getStateFromMockCalls<BOLLRenderState>(mockHost, BOLL_STATE_KEY)
|
|
221
|
+
expect(initialState).toBeDefined()
|
|
222
|
+
expect(initialState!.visibleMin).toBe(Infinity)
|
|
223
|
+
expect(initialState!.visibleMax).toBe(-Infinity)
|
|
224
|
+
|
|
225
|
+
scheduler.updateVisibleRange({ start: 30, end: 60 })
|
|
226
|
+
|
|
227
|
+
const updatedState = getStateFromMockCalls<BOLLRenderState>(mockHost, BOLL_STATE_KEY)
|
|
228
|
+
expect(updatedState).toBeDefined()
|
|
229
|
+
expect(updatedState!.visibleMin).toBeLessThan(updatedState!.visibleMax)
|
|
230
|
+
expect(Number.isFinite(updatedState!.visibleMin)).toBe(true)
|
|
231
|
+
expect(Number.isFinite(updatedState!.visibleMax)).toBe(true)
|
|
232
|
+
expect(invalidate).toHaveBeenCalledTimes(1)
|
|
233
|
+
})
|
|
157
234
|
})
|
|
158
235
|
|
|
159
236
|
describe('MA config update', () => {
|
|
@@ -230,9 +307,11 @@ describe('IndicatorScheduler', () => {
|
|
|
230
307
|
// Update only viewport
|
|
231
308
|
scheduler.updateVisibleRange({ start: 50, end: 60 })
|
|
232
309
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
expect(
|
|
310
|
+
const calls = vi.mocked(mockHost.setSharedState).mock.calls
|
|
311
|
+
expect(calls.some((call) => call[0] === MA_STATE_KEY)).toBe(false)
|
|
312
|
+
expect(calls.some((call) => call[0] === BOLL_STATE_KEY)).toBe(false)
|
|
313
|
+
expect(calls.some((call) => call[0] === EXPMA_STATE_KEY)).toBe(false)
|
|
314
|
+
expect(calls.some((call) => call[0] === ENE_STATE_KEY)).toBe(false)
|
|
236
315
|
|
|
237
316
|
// Inspect a sub-indicator (RSI) since main indicators are not rewritten on viewport-only updates
|
|
238
317
|
const rsiKey = createRSIStateKey('sub_RSI')
|
|
@@ -252,8 +331,8 @@ describe('IndicatorScheduler', () => {
|
|
|
252
331
|
const data2 = createTestData(100, 200)
|
|
253
332
|
scheduler.update(data2, { start: 0, end: 100 })
|
|
254
333
|
|
|
255
|
-
//
|
|
256
|
-
expect(mockHost.setSharedState).toHaveBeenCalledTimes(
|
|
334
|
+
// 26 registered indicators produce states × 2 data updates (1st triggers compute, 2nd retriggers with different data)
|
|
335
|
+
expect(mockHost.setSharedState).toHaveBeenCalledTimes(52)
|
|
257
336
|
})
|
|
258
337
|
})
|
|
259
338
|
|
|
@@ -266,8 +345,8 @@ describe('IndicatorScheduler', () => {
|
|
|
266
345
|
|
|
267
346
|
scheduler.recompute()
|
|
268
347
|
|
|
269
|
-
// Should write all
|
|
270
|
-
expect(mockHost.setSharedState).toHaveBeenCalledTimes(
|
|
348
|
+
// Should write all 26 indicator states that have registered + known to composeRenderStates
|
|
349
|
+
expect(mockHost.setSharedState).toHaveBeenCalledTimes(26)
|
|
271
350
|
})
|
|
272
351
|
|
|
273
352
|
it('should recalculate with same data and range', () => {
|
|
@@ -345,6 +424,7 @@ describe('BOLL State in scheduler', () => {
|
|
|
345
424
|
scheduler = new IndicatorScheduler()
|
|
346
425
|
mockHost = createMockPluginHost()
|
|
347
426
|
scheduler.setPluginHost(mockHost)
|
|
427
|
+
registerTestIndicators(scheduler)
|
|
348
428
|
})
|
|
349
429
|
|
|
350
430
|
it('should write BOLLRenderState to StateStore after update', () => {
|
|
@@ -427,6 +507,7 @@ describe('EXPMA State in scheduler', () => {
|
|
|
427
507
|
scheduler = new IndicatorScheduler()
|
|
428
508
|
mockHost = createMockPluginHost()
|
|
429
509
|
scheduler.setPluginHost(mockHost)
|
|
510
|
+
registerTestIndicators(scheduler)
|
|
430
511
|
})
|
|
431
512
|
|
|
432
513
|
it('should write EXPMARenderState to StateStore after update', () => {
|
|
@@ -480,6 +561,7 @@ describe('ENE State in scheduler', () => {
|
|
|
480
561
|
scheduler = new IndicatorScheduler()
|
|
481
562
|
mockHost = createMockPluginHost()
|
|
482
563
|
scheduler.setPluginHost(mockHost)
|
|
564
|
+
registerTestIndicators(scheduler)
|
|
483
565
|
})
|
|
484
566
|
|
|
485
567
|
it('should write ENERenderState to StateStore after update', () => {
|
|
@@ -536,6 +618,7 @@ describe('Per-indicator dirty flags', () => {
|
|
|
536
618
|
scheduler = new IndicatorScheduler()
|
|
537
619
|
mockHost = createMockPluginHost()
|
|
538
620
|
scheduler.setPluginHost(mockHost)
|
|
621
|
+
registerTestIndicators(scheduler)
|
|
539
622
|
})
|
|
540
623
|
|
|
541
624
|
it('updateBOLLConfig should not recalculate MA series', () => {
|
|
@@ -684,6 +767,7 @@ describe('RSI State in scheduler', () => {
|
|
|
684
767
|
scheduler = new IndicatorScheduler()
|
|
685
768
|
mockHost = createMockPluginHost()
|
|
686
769
|
scheduler.setPluginHost(mockHost)
|
|
770
|
+
registerTestIndicators(scheduler)
|
|
687
771
|
})
|
|
688
772
|
|
|
689
773
|
it('should write RSIRenderState to StateStore after update', () => {
|