@363045841yyt/klinechart-core 0.7.9 → 0.7.11
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/engine/chart.d.ts.map +1 -1
- package/dist/engine/chart.js +39 -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/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/engine/chart.ts +49 -229
- 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 +62 -32
- 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/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/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,5 +1,6 @@
|
|
|
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'
|
|
@@ -15,40 +16,45 @@ function applyMainResult(key: string): NonNullable<IndicatorMetadata['applyResul
|
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
20
27
|
}
|
|
21
28
|
|
|
22
29
|
function registerTestIndicators(scheduler: IndicatorScheduler): void {
|
|
23
30
|
const indicators: Array<IndicatorMetadata> = [
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
getBuiltinTestIndicator('ma'),
|
|
32
|
+
getBuiltinTestIndicator('boll'),
|
|
33
|
+
getBuiltinTestIndicator('expma'),
|
|
34
|
+
getBuiltinTestIndicator('ene'),
|
|
35
|
+
getBuiltinTestIndicator('rsi'),
|
|
36
|
+
getBuiltinTestIndicator('macd'),
|
|
30
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') },
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
{ name: 'keltner', displayName: 'Keltner', category: 'main' as const, stateKey: 'indicator:keltner:main', defaultPaneId: 'main', rendererFactory: vi.fn() as any, applyResult: applyMainResult('indicator:keltner:main') },
|
|
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'),
|
|
52
58
|
]
|
|
53
59
|
for (const meta of indicators) {
|
|
54
60
|
scheduler.registerIndicator(meta)
|
|
@@ -203,6 +209,28 @@ describe('IndicatorScheduler', () => {
|
|
|
203
209
|
expect(state!.visibleMin).toBe(Infinity)
|
|
204
210
|
expect(state!.visibleMax).toBe(-Infinity)
|
|
205
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
|
+
})
|
|
206
234
|
})
|
|
207
235
|
|
|
208
236
|
describe('MA config update', () => {
|
|
@@ -279,9 +307,11 @@ describe('IndicatorScheduler', () => {
|
|
|
279
307
|
// Update only viewport
|
|
280
308
|
scheduler.updateVisibleRange({ start: 50, end: 60 })
|
|
281
309
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
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)
|
|
285
315
|
|
|
286
316
|
// Inspect a sub-indicator (RSI) since main indicators are not rewritten on viewport-only updates
|
|
287
317
|
const rsiKey = createRSIStateKey('sub_RSI')
|