@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.
Files changed (234) hide show
  1. package/dist/engine/chart.d.ts.map +1 -1
  2. package/dist/engine/chart.js +39 -229
  3. package/dist/engine/chart.js.map +1 -1
  4. package/dist/engine/indicators/indicatorDefinitionRegistry.d.ts +10 -3
  5. package/dist/engine/indicators/indicatorDefinitionRegistry.d.ts.map +1 -1
  6. package/dist/engine/indicators/indicatorDefinitionRegistry.js +29 -2
  7. package/dist/engine/indicators/indicatorDefinitionRegistry.js.map +1 -1
  8. package/dist/engine/indicators/indicatorMetadata.d.ts +79 -2
  9. package/dist/engine/indicators/indicatorMetadata.d.ts.map +1 -1
  10. package/dist/engine/indicators/indicatorMetadata.js.map +1 -1
  11. package/dist/engine/indicators/indicatorRegistry.d.ts +5 -0
  12. package/dist/engine/indicators/indicatorRegistry.d.ts.map +1 -1
  13. package/dist/engine/indicators/indicatorRegistry.js +49 -5
  14. package/dist/engine/indicators/indicatorRegistry.js.map +1 -1
  15. package/dist/engine/indicators/registerBuiltins.d.ts +42 -0
  16. package/dist/engine/indicators/registerBuiltins.d.ts.map +1 -0
  17. package/dist/engine/indicators/registerBuiltins.js +46 -0
  18. package/dist/engine/indicators/registerBuiltins.js.map +1 -0
  19. package/dist/engine/indicators/scheduler.d.ts +2 -2
  20. package/dist/engine/indicators/scheduler.d.ts.map +1 -1
  21. package/dist/engine/indicators/scheduler.js +41 -11
  22. package/dist/engine/indicators/scheduler.js.map +1 -1
  23. package/dist/engine/indicators/stateComposer.d.ts +6 -4
  24. package/dist/engine/indicators/stateComposer.d.ts.map +1 -1
  25. package/dist/engine/indicators/stateComposer.js +80 -984
  26. package/dist/engine/indicators/stateComposer.js.map +1 -1
  27. package/dist/engine/indicators/visibleStateComposers.d.ts +145 -0
  28. package/dist/engine/indicators/visibleStateComposers.d.ts.map +1 -0
  29. package/dist/engine/indicators/visibleStateComposers.js +469 -0
  30. package/dist/engine/indicators/visibleStateComposers.js.map +1 -0
  31. package/dist/engine/renderers/Indicator/atr.d.ts.map +1 -1
  32. package/dist/engine/renderers/Indicator/atr.js +8 -0
  33. package/dist/engine/renderers/Indicator/atr.js.map +1 -1
  34. package/dist/engine/renderers/Indicator/boll.d.ts.map +1 -1
  35. package/dist/engine/renderers/Indicator/boll.js +47 -0
  36. package/dist/engine/renderers/Indicator/boll.js.map +1 -1
  37. package/dist/engine/renderers/Indicator/cci.d.ts.map +1 -1
  38. package/dist/engine/renderers/Indicator/cci.js +8 -1
  39. package/dist/engine/renderers/Indicator/cci.js.map +1 -1
  40. package/dist/engine/renderers/Indicator/chaikinVol.d.ts.map +1 -1
  41. package/dist/engine/renderers/Indicator/chaikinVol.js +7 -1
  42. package/dist/engine/renderers/Indicator/chaikinVol.js.map +1 -1
  43. package/dist/engine/renderers/Indicator/cmf.d.ts.map +1 -1
  44. package/dist/engine/renderers/Indicator/cmf.js +7 -1
  45. package/dist/engine/renderers/Indicator/cmf.js.map +1 -1
  46. package/dist/engine/renderers/Indicator/dema.d.ts.map +1 -1
  47. package/dist/engine/renderers/Indicator/dema.js +8 -1
  48. package/dist/engine/renderers/Indicator/dema.js.map +1 -1
  49. package/dist/engine/renderers/Indicator/donchian.d.ts.map +1 -1
  50. package/dist/engine/renderers/Indicator/donchian.js +8 -1
  51. package/dist/engine/renderers/Indicator/donchian.js.map +1 -1
  52. package/dist/engine/renderers/Indicator/ene.d.ts.map +1 -1
  53. package/dist/engine/renderers/Indicator/ene.js +45 -0
  54. package/dist/engine/renderers/Indicator/ene.js.map +1 -1
  55. package/dist/engine/renderers/Indicator/expma.d.ts.map +1 -1
  56. package/dist/engine/renderers/Indicator/expma.js +45 -0
  57. package/dist/engine/renderers/Indicator/expma.js.map +1 -1
  58. package/dist/engine/renderers/Indicator/fastk.d.ts.map +1 -1
  59. package/dist/engine/renderers/Indicator/fastk.js +8 -1
  60. package/dist/engine/renderers/Indicator/fastk.js.map +1 -1
  61. package/dist/engine/renderers/Indicator/fib.d.ts.map +1 -1
  62. package/dist/engine/renderers/Indicator/fib.js +8 -1
  63. package/dist/engine/renderers/Indicator/fib.js.map +1 -1
  64. package/dist/engine/renderers/Indicator/hma.d.ts.map +1 -1
  65. package/dist/engine/renderers/Indicator/hma.js +8 -1
  66. package/dist/engine/renderers/Indicator/hma.js.map +1 -1
  67. package/dist/engine/renderers/Indicator/hv.d.ts.map +1 -1
  68. package/dist/engine/renderers/Indicator/hv.js +7 -0
  69. package/dist/engine/renderers/Indicator/hv.js.map +1 -1
  70. package/dist/engine/renderers/Indicator/ichimoku.d.ts.map +1 -1
  71. package/dist/engine/renderers/Indicator/ichimoku.js +8 -1
  72. package/dist/engine/renderers/Indicator/ichimoku.js.map +1 -1
  73. package/dist/engine/renderers/Indicator/index.d.ts +7 -2
  74. package/dist/engine/renderers/Indicator/index.d.ts.map +1 -1
  75. package/dist/engine/renderers/Indicator/index.js +2 -114
  76. package/dist/engine/renderers/Indicator/index.js.map +1 -1
  77. package/dist/engine/renderers/Indicator/kama.d.ts.map +1 -1
  78. package/dist/engine/renderers/Indicator/kama.js +8 -1
  79. package/dist/engine/renderers/Indicator/kama.js.map +1 -1
  80. package/dist/engine/renderers/Indicator/keltner.d.ts.map +1 -1
  81. package/dist/engine/renderers/Indicator/keltner.js +8 -1
  82. package/dist/engine/renderers/Indicator/keltner.js.map +1 -1
  83. package/dist/engine/renderers/Indicator/kst.d.ts.map +1 -1
  84. package/dist/engine/renderers/Indicator/kst.js +8 -0
  85. package/dist/engine/renderers/Indicator/kst.js.map +1 -1
  86. package/dist/engine/renderers/Indicator/ma.d.ts.map +1 -1
  87. package/dist/engine/renderers/Indicator/ma.js +63 -0
  88. package/dist/engine/renderers/Indicator/ma.js.map +1 -1
  89. package/dist/engine/renderers/Indicator/macd.d.ts.map +1 -1
  90. package/dist/engine/renderers/Indicator/macd.js +8 -1
  91. package/dist/engine/renderers/Indicator/macd.js.map +1 -1
  92. package/dist/engine/renderers/Indicator/mfi.d.ts.map +1 -1
  93. package/dist/engine/renderers/Indicator/mfi.js +7 -1
  94. package/dist/engine/renderers/Indicator/mfi.js.map +1 -1
  95. package/dist/engine/renderers/Indicator/mom.d.ts.map +1 -1
  96. package/dist/engine/renderers/Indicator/mom.js +8 -0
  97. package/dist/engine/renderers/Indicator/mom.js.map +1 -1
  98. package/dist/engine/renderers/Indicator/obv.d.ts.map +1 -1
  99. package/dist/engine/renderers/Indicator/obv.js +7 -1
  100. package/dist/engine/renderers/Indicator/obv.js.map +1 -1
  101. package/dist/engine/renderers/Indicator/parkinson.d.ts.map +1 -1
  102. package/dist/engine/renderers/Indicator/parkinson.js +7 -0
  103. package/dist/engine/renderers/Indicator/parkinson.js.map +1 -1
  104. package/dist/engine/renderers/Indicator/pivot.d.ts.map +1 -1
  105. package/dist/engine/renderers/Indicator/pivot.js +8 -1
  106. package/dist/engine/renderers/Indicator/pivot.js.map +1 -1
  107. package/dist/engine/renderers/Indicator/pvt.d.ts.map +1 -1
  108. package/dist/engine/renderers/Indicator/pvt.js +7 -1
  109. package/dist/engine/renderers/Indicator/pvt.js.map +1 -1
  110. package/dist/engine/renderers/Indicator/roc.d.ts.map +1 -1
  111. package/dist/engine/renderers/Indicator/roc.js +7 -1
  112. package/dist/engine/renderers/Indicator/roc.js.map +1 -1
  113. package/dist/engine/renderers/Indicator/rsi.d.ts.map +1 -1
  114. package/dist/engine/renderers/Indicator/rsi.js +8 -1
  115. package/dist/engine/renderers/Indicator/rsi.js.map +1 -1
  116. package/dist/engine/renderers/Indicator/sar.d.ts.map +1 -1
  117. package/dist/engine/renderers/Indicator/sar.js +8 -1
  118. package/dist/engine/renderers/Indicator/sar.js.map +1 -1
  119. package/dist/engine/renderers/Indicator/stoch.d.ts.map +1 -1
  120. package/dist/engine/renderers/Indicator/stoch.js +8 -1
  121. package/dist/engine/renderers/Indicator/stoch.js.map +1 -1
  122. package/dist/engine/renderers/Indicator/structure.d.ts.map +1 -1
  123. package/dist/engine/renderers/Indicator/structure.js +8 -1
  124. package/dist/engine/renderers/Indicator/structure.js.map +1 -1
  125. package/dist/engine/renderers/Indicator/subPaneConfig.d.ts +1 -1
  126. package/dist/engine/renderers/Indicator/subPaneConfig.d.ts.map +1 -1
  127. package/dist/engine/renderers/Indicator/subPaneConfig.js.map +1 -1
  128. package/dist/engine/renderers/Indicator/supertrend.d.ts.map +1 -1
  129. package/dist/engine/renderers/Indicator/supertrend.js +8 -1
  130. package/dist/engine/renderers/Indicator/supertrend.js.map +1 -1
  131. package/dist/engine/renderers/Indicator/tema.d.ts.map +1 -1
  132. package/dist/engine/renderers/Indicator/tema.js +8 -1
  133. package/dist/engine/renderers/Indicator/tema.js.map +1 -1
  134. package/dist/engine/renderers/Indicator/trix.d.ts.map +1 -1
  135. package/dist/engine/renderers/Indicator/trix.js +7 -0
  136. package/dist/engine/renderers/Indicator/trix.js.map +1 -1
  137. package/dist/engine/renderers/Indicator/vma.d.ts.map +1 -1
  138. package/dist/engine/renderers/Indicator/vma.js +7 -0
  139. package/dist/engine/renderers/Indicator/vma.js.map +1 -1
  140. package/dist/engine/renderers/Indicator/volumeProfile.d.ts.map +1 -1
  141. package/dist/engine/renderers/Indicator/volumeProfile.js +7 -1
  142. package/dist/engine/renderers/Indicator/volumeProfile.js.map +1 -1
  143. package/dist/engine/renderers/Indicator/vwap.d.ts.map +1 -1
  144. package/dist/engine/renderers/Indicator/vwap.js +7 -1
  145. package/dist/engine/renderers/Indicator/vwap.js.map +1 -1
  146. package/dist/engine/renderers/Indicator/wma.d.ts.map +1 -1
  147. package/dist/engine/renderers/Indicator/wma.js +8 -1
  148. package/dist/engine/renderers/Indicator/wma.js.map +1 -1
  149. package/dist/engine/renderers/Indicator/wmsr.d.ts.map +1 -1
  150. package/dist/engine/renderers/Indicator/wmsr.js +8 -1
  151. package/dist/engine/renderers/Indicator/wmsr.js.map +1 -1
  152. package/dist/engine/renderers/Indicator/zones.d.ts.map +1 -1
  153. package/dist/engine/renderers/Indicator/zones.js +8 -1
  154. package/dist/engine/renderers/Indicator/zones.js.map +1 -1
  155. package/dist/engine/renderers/extremaMarkers.d.ts.map +1 -1
  156. package/dist/engine/renderers/extremaMarkers.js +0 -22
  157. package/dist/engine/renderers/extremaMarkers.js.map +1 -1
  158. package/dist/engine/renderers/subVolume.d.ts.map +1 -1
  159. package/dist/engine/renderers/subVolume.js +3 -0
  160. package/dist/engine/renderers/subVolume.js.map +1 -1
  161. package/dist/engine/subPaneManager.d.ts +1 -0
  162. package/dist/engine/subPaneManager.d.ts.map +1 -1
  163. package/dist/engine/subPaneManager.js +30 -237
  164. package/dist/engine/subPaneManager.js.map +1 -1
  165. package/dist/semantic/controller.d.ts +9 -4
  166. package/dist/semantic/controller.d.ts.map +1 -1
  167. package/dist/semantic/controller.js +57 -53
  168. package/dist/semantic/controller.js.map +1 -1
  169. package/dist/version.d.ts +1 -1
  170. package/dist/version.d.ts.map +1 -1
  171. package/dist/version.js +1 -1
  172. package/dist/version.js.map +1 -1
  173. package/package.json +1 -1
  174. package/src/engine/chart.ts +49 -229
  175. package/src/engine/indicators/__tests__/indicatorDefinitionRegistry.test.ts +59 -0
  176. package/src/engine/indicators/__tests__/indicatorRegistry.test.ts +62 -0
  177. package/src/engine/indicators/__tests__/registerBuiltins.test.ts +380 -0
  178. package/src/engine/indicators/__tests__/scheduler.test.ts +62 -32
  179. package/src/engine/indicators/__tests__/stateComposer.test.ts +444 -0
  180. package/src/engine/indicators/indicatorDefinitionRegistry.ts +49 -4
  181. package/src/engine/indicators/indicatorMetadata.ts +107 -2
  182. package/src/engine/indicators/indicatorRegistry.ts +54 -5
  183. package/src/engine/indicators/registerBuiltins.ts +47 -0
  184. package/src/engine/indicators/scheduler.ts +61 -13
  185. package/src/engine/indicators/stateComposer.ts +110 -1040
  186. package/src/engine/indicators/visibleStateComposers.ts +734 -0
  187. package/src/engine/renderers/Indicator/__tests__/createSubIndicatorRenderer.test.ts +61 -0
  188. package/src/engine/renderers/Indicator/atr.ts +9 -1
  189. package/src/engine/renderers/Indicator/boll.ts +53 -1
  190. package/src/engine/renderers/Indicator/cci.ts +9 -2
  191. package/src/engine/renderers/Indicator/chaikinVol.ts +8 -2
  192. package/src/engine/renderers/Indicator/cmf.ts +8 -2
  193. package/src/engine/renderers/Indicator/dema.ts +9 -2
  194. package/src/engine/renderers/Indicator/donchian.ts +9 -2
  195. package/src/engine/renderers/Indicator/ene.ts +51 -1
  196. package/src/engine/renderers/Indicator/expma.ts +51 -1
  197. package/src/engine/renderers/Indicator/fastk.ts +9 -2
  198. package/src/engine/renderers/Indicator/fib.ts +9 -2
  199. package/src/engine/renderers/Indicator/hma.ts +9 -2
  200. package/src/engine/renderers/Indicator/hv.ts +8 -1
  201. package/src/engine/renderers/Indicator/ichimoku.ts +9 -2
  202. package/src/engine/renderers/Indicator/index.ts +9 -121
  203. package/src/engine/renderers/Indicator/kama.ts +9 -2
  204. package/src/engine/renderers/Indicator/keltner.ts +9 -2
  205. package/src/engine/renderers/Indicator/kst.ts +9 -1
  206. package/src/engine/renderers/Indicator/ma.ts +69 -0
  207. package/src/engine/renderers/Indicator/macd.ts +9 -2
  208. package/src/engine/renderers/Indicator/mfi.ts +8 -2
  209. package/src/engine/renderers/Indicator/mom.ts +9 -1
  210. package/src/engine/renderers/Indicator/obv.ts +8 -2
  211. package/src/engine/renderers/Indicator/parkinson.ts +8 -1
  212. package/src/engine/renderers/Indicator/pivot.ts +9 -2
  213. package/src/engine/renderers/Indicator/pvt.ts +8 -2
  214. package/src/engine/renderers/Indicator/roc.ts +8 -2
  215. package/src/engine/renderers/Indicator/rsi.ts +9 -2
  216. package/src/engine/renderers/Indicator/sar.ts +9 -2
  217. package/src/engine/renderers/Indicator/stoch.ts +9 -2
  218. package/src/engine/renderers/Indicator/structure.ts +9 -2
  219. package/src/engine/renderers/Indicator/subPaneConfig.ts +1 -1
  220. package/src/engine/renderers/Indicator/supertrend.ts +9 -2
  221. package/src/engine/renderers/Indicator/tema.ts +9 -2
  222. package/src/engine/renderers/Indicator/trix.ts +8 -1
  223. package/src/engine/renderers/Indicator/vma.ts +8 -1
  224. package/src/engine/renderers/Indicator/volumeProfile.ts +8 -2
  225. package/src/engine/renderers/Indicator/vwap.ts +8 -2
  226. package/src/engine/renderers/Indicator/wma.ts +9 -2
  227. package/src/engine/renderers/Indicator/wmsr.ts +9 -2
  228. package/src/engine/renderers/Indicator/zones.ts +9 -2
  229. package/src/engine/renderers/extremaMarkers.ts +0 -24
  230. package/src/engine/renderers/subVolume.ts +3 -0
  231. package/src/engine/subPaneManager.ts +34 -274
  232. package/src/semantic/__tests__/controller.test.ts +84 -0
  233. package/src/semantic/controller.ts +69 -65
  234. package/src/version.ts +1 -1
@@ -2,1013 +2,109 @@
2
2
  * State Composer
3
3
  * 把 Worker/Runtime 返回的 series bundle 组装成与现有兼容的 render states
4
4
  */
5
- import { EMPTY_RSI_STATE } from './rsiState';
6
- import { EMPTY_CCI_STATE } from './cciState';
7
- import { EMPTY_STOCH_STATE } from './stochState';
8
- import { EMPTY_MOM_STATE } from './momState';
9
- import { EMPTY_WMSR_STATE } from './wmsrState';
10
- import { EMPTY_KST_STATE } from './kstState';
11
- import { EMPTY_FASTK_STATE } from './fastkState';
12
- import { EMPTY_MACD_STATE } from './macdState';
13
- import { EMPTY_ATR_STATE } from './atrState';
14
- import { EMPTY_WMA_STATE } from './wmaState';
15
- import { EMPTY_DEMA_STATE } from './demaState';
16
- import { EMPTY_TEMA_STATE } from './temaState';
17
- import { EMPTY_HMA_STATE } from './hmaState';
18
- import { EMPTY_KAMA_STATE } from './kamaState';
19
- import { EMPTY_SAR_STATE } from './sarState';
20
- import { EMPTY_SUPERTREND_STATE } from './supertrendState';
21
- import { EMPTY_KELTNER_STATE } from './keltnerState';
22
- import { EMPTY_DONCHIAN_STATE } from './donchianState';
23
- import { EMPTY_ICHIMOKU_STATE } from './ichimokuState';
24
- import { EMPTY_ROC_STATE } from './rocState';
25
- import { EMPTY_TRIX_STATE } from './trixState';
26
- import { EMPTY_HV_STATE } from './hvState';
27
- import { EMPTY_PARKINSON_STATE } from './parkinsonState';
28
- import { EMPTY_CHAIKIN_VOL_STATE } from './chaikinVolState';
29
- import { EMPTY_VMA_STATE } from './vmaState';
30
- import { EMPTY_OBV_STATE } from './obvState';
31
- import { EMPTY_PVT_STATE } from './pvtState';
32
- import { EMPTY_VWAP_STATE } from './vwapState';
33
- import { EMPTY_CMF_STATE } from './cmfState';
34
- import { EMPTY_MFI_STATE } from './mfiState';
35
- import { EMPTY_PIVOT_STATE } from './pivotState';
36
- import { EMPTY_FIB_STATE } from './fibState';
37
- import { EMPTY_STRUCTURE_STATE } from './structureState';
38
- import { EMPTY_ZONES_STATE } from './zonesState';
39
- import { EMPTY_VOLUME_PROFILE_STATE } from './volumeProfileState';
40
- function getLatestMACDPoint(bundle, visibleRange) {
41
- const latestIndex = visibleRange.end - 1;
42
- return latestIndex >= 0 && latestIndex < bundle.macd.series.length
43
- ? bundle.macd.series[latestIndex]
44
- : null;
45
- }
46
- function mergeEmptyState(state, timestamp, overrides) {
47
- return {
48
- ...state,
49
- ...overrides,
50
- timestamp,
51
- };
52
- }
5
+ const MAIN_RENDER_STATE_INDICATOR_IDS = ['ma', 'boll', 'expma', 'ene'];
6
+ const METADATA_VISIBLE_STATE_INDICATOR_IDS = [
7
+ 'cci',
8
+ 'wma',
9
+ 'dema',
10
+ 'tema',
11
+ 'hma',
12
+ 'kama',
13
+ 'roc',
14
+ 'chaikinVol',
15
+ 'obv',
16
+ 'pvt',
17
+ 'vwap',
18
+ 'rsi',
19
+ 'stoch',
20
+ 'fastk',
21
+ 'mfi',
22
+ 'wmsr',
23
+ 'cmf',
24
+ 'atr',
25
+ 'hv',
26
+ 'kst',
27
+ 'mom',
28
+ 'parkinson',
29
+ 'trix',
30
+ 'vma',
31
+ 'macd',
32
+ 'sar',
33
+ 'supertrend',
34
+ 'keltner',
35
+ 'donchian',
36
+ 'ichimoku',
37
+ 'pivot',
38
+ 'fib',
39
+ 'structure',
40
+ 'zones',
41
+ 'volumeProfile',
42
+ ];
53
43
  /**
54
44
  * 仅计算副图指标的 visible-only states
55
45
  * 用于滚动时的轻量更新,避免重复计算主图指标
56
46
  */
57
- export function composeVisibleSubIndicatorStates(bundle, visibleRange, timestamp, activeMask = {}) {
58
- const rsiActive = activeMask.rsi ?? true;
59
- const cciActive = activeMask.cci ?? true;
60
- const stochActive = activeMask.stoch ?? true;
61
- const momActive = activeMask.mom ?? true;
62
- const wmsrActive = activeMask.wmsr ?? true;
63
- const kstActive = activeMask.kst ?? true;
64
- const fastkActive = activeMask.fastk ?? true;
65
- const macdActive = activeMask.macd ?? true;
66
- const atrActive = activeMask.atr ?? true;
67
- const wmaActive = activeMask.wma ?? true;
68
- const demaActive = activeMask.dema ?? true;
69
- const temaActive = activeMask.tema ?? true;
70
- const hmaActive = activeMask.hma ?? true;
71
- const kamaActive = activeMask.kama ?? true;
72
- const sarActive = activeMask.sar ?? true;
73
- const supertrendActive = activeMask.supertrend ?? true;
74
- const keltnerActive = activeMask.keltner ?? true;
75
- const donchianActive = activeMask.donchian ?? true;
76
- const ichimokuActive = activeMask.ichimoku ?? true;
77
- const rocActive = activeMask.roc ?? true;
78
- const trixActive = activeMask.trix ?? true;
79
- const hvActive = activeMask.hv ?? true;
80
- const parkinsonActive = activeMask.parkinson ?? true;
81
- const chaikinVolActive = activeMask.chaikinVol ?? true;
82
- const vmaActive = activeMask.vma ?? true;
83
- const obvActive = activeMask.obv ?? true;
84
- const pvtActive = activeMask.pvt ?? true;
85
- const vwapActive = activeMask.vwap ?? true;
86
- const cmfActive = activeMask.cmf ?? true;
87
- const mfiActive = activeMask.mfi ?? true;
88
- const pivotActive = activeMask.pivot ?? true;
89
- const fibActive = activeMask.fib ?? true;
90
- const structureActive = activeMask.structure ?? true;
91
- const zonesActive = activeMask.zones ?? true;
92
- const vpActive = activeMask.volumeProfile ?? true;
93
- const rsiExtremes = rsiActive ? calcRSIExtremes(bundle.rsi.series, visibleRange) : null;
94
- const cciExtremes = cciActive ? calcCCIExtremes(bundle.cci.series, visibleRange) : null;
95
- const stochExtremes = stochActive ? calcSTOCHExtremes(bundle.stoch.series, visibleRange) : null;
96
- const momExtremes = momActive ? calcMOMExtremes(bundle.mom.series, visibleRange) : null;
97
- const wmsrExtremes = wmsrActive ? calcWMSRExtremes(bundle.wmsr.series, visibleRange) : null;
98
- const kstExtremes = kstActive ? calcKSTExtremes(bundle.kst.series, visibleRange) : null;
99
- const fastkExtremes = fastkActive ? calcFASTKExtremes(bundle.fastk.series, visibleRange) : null;
100
- const macdExtremes = macdActive ? calcMACDExtremes(bundle.macd.series, visibleRange) : null;
101
- const atrExtremes = atrActive ? calcATRExtremes(bundle.atr.series, visibleRange) : null;
102
- const wmaExtremes = wmaActive ? calcSparseExtremes(bundle.wma.series, visibleRange) : null;
103
- const demaExtremes = demaActive ? calcSparseExtremes(bundle.dema.series, visibleRange) : null;
104
- const temaExtremes = temaActive ? calcSparseExtremes(bundle.tema.series, visibleRange) : null;
105
- const hmaExtremes = hmaActive ? calcSparseExtremes(bundle.hma.series, visibleRange) : null;
106
- const kamaExtremes = kamaActive ? calcSparseExtremes(bundle.kama.series, visibleRange) : null;
107
- const sarExtremes = sarActive ? calcSARExtremes(bundle.sar.series, visibleRange) : null;
108
- const supertrendExtremes = supertrendActive ? calcSARExtremes(bundle.supertrend.series, visibleRange) : null;
109
- const keltnerExtremes = keltnerActive ? calcBandExtremes(bundle.keltner.series, visibleRange) : null;
110
- const donchianExtremes = donchianActive ? calcBandExtremes(bundle.donchian.series, visibleRange) : null;
111
- const ichimokuExtremes = ichimokuActive ? calcIchimokuExtremes(bundle.ichimoku.series, visibleRange) : null;
112
- const rocExtremes = rocActive ? calcSparseExtremes(bundle.roc.series, visibleRange) : null;
113
- const trixSeriesExtremes = trixActive ? calcSparseExtremes(bundle.trix.series, visibleRange) : null;
114
- const trixSignalExtremes = trixActive ? calcSparseExtremes(bundle.trix.signalSeries, visibleRange) : null;
115
- const hvExtremes = hvActive ? calcSparseExtremes(bundle.hv.series, visibleRange) : null;
116
- const parkinsonExtremes = parkinsonActive ? calcSparseExtremes(bundle.parkinson.series, visibleRange) : null;
117
- const chaikinVolExtremes = chaikinVolActive ? calcSparseExtremes(bundle.chaikinVol.series, visibleRange) : null;
118
- const vmaExtremes = vmaActive ? calcSparseExtremes(bundle.vma.series, visibleRange) : null;
119
- const obvExtremes = obvActive ? calcSparseExtremes(bundle.obv.series, visibleRange) : null;
120
- const pvtExtremes = pvtActive ? calcSparseExtremes(bundle.pvt.series, visibleRange) : null;
121
- const vwapExtremes = vwapActive ? calcSparseExtremes(bundle.vwap.series, visibleRange) : null;
122
- const cmfExtremes = cmfActive ? calcSparseExtremes(bundle.cmf.series, visibleRange) : null;
123
- const mfiExtremes = mfiActive ? calcSparseExtremes(bundle.mfi.series, visibleRange) : null;
124
- const pivotExtremes = pivotActive ? calcPivotExtremes(bundle.pivot.series, visibleRange) : null;
125
- const fibExtremes = fibActive ? calcFibExtremes(bundle.fib.series, visibleRange) : null;
126
- const latestPoint = macdActive ? getLatestMACDPoint(bundle, visibleRange) : null;
127
- const macdPadding = macdExtremes ? Math.max(Math.abs(macdExtremes.max), Math.abs(macdExtremes.min)) * 0.1 : 0;
128
- const macdValueMin = macdExtremes && Number.isFinite(macdExtremes.min) ? macdExtremes.min - macdPadding : EMPTY_MACD_STATE.valueMin;
129
- const macdValueMax = macdExtremes && Number.isFinite(macdExtremes.max) ? macdExtremes.max + macdPadding : EMPTY_MACD_STATE.valueMax;
130
- const cciValueMin = cciExtremes ? Math.min(cciExtremes.min, -150) : EMPTY_CCI_STATE.valueMin;
131
- const cciValueMax = cciExtremes ? Math.max(cciExtremes.max, 150) : EMPTY_CCI_STATE.valueMax;
132
- const momPadding = momExtremes ? Math.max(Math.abs(momExtremes.max), Math.abs(momExtremes.min)) * 0.1 : 0;
133
- const momValueMin = momExtremes ? momExtremes.min - momPadding : EMPTY_MOM_STATE.valueMin;
134
- const momValueMax = momExtremes ? momExtremes.max + momPadding : EMPTY_MOM_STATE.valueMax;
135
- const kstRange = kstExtremes ? kstExtremes.max - kstExtremes.min : 0;
136
- const kstPadding = kstRange * 0.1;
137
- const kstValueMin = kstExtremes ? kstExtremes.min - kstPadding : EMPTY_KST_STATE.valueMin;
138
- const kstValueMax = kstExtremes ? kstExtremes.max + kstPadding : EMPTY_KST_STATE.valueMax;
139
- const atrValueMax = atrExtremes && Number.isFinite(atrExtremes.max)
140
- ? atrExtremes.max * 1.1
141
- : EMPTY_ATR_STATE.valueMax;
142
- const maFamilyBounds = (ext, empty) => {
143
- if (!ext || !Number.isFinite(ext.min) || !Number.isFinite(ext.max)) {
144
- return { valueMin: empty.valueMin, valueMax: empty.valueMax };
145
- }
146
- const range = ext.max - ext.min;
147
- const padding = range > 0 ? range * 0.05 : Math.max(1, Math.abs(ext.max) * 0.05);
148
- return { valueMin: ext.min - padding, valueMax: ext.max + padding };
149
- };
150
- const wmaBounds = maFamilyBounds(wmaExtremes, EMPTY_WMA_STATE);
151
- const demaBounds = maFamilyBounds(demaExtremes, EMPTY_DEMA_STATE);
152
- const temaBounds = maFamilyBounds(temaExtremes, EMPTY_TEMA_STATE);
153
- const hmaBounds = maFamilyBounds(hmaExtremes, EMPTY_HMA_STATE);
154
- const kamaBounds = maFamilyBounds(kamaExtremes, EMPTY_KAMA_STATE);
155
- const sarBounds = maFamilyBounds(sarExtremes, EMPTY_SAR_STATE);
156
- const supertrendBounds = maFamilyBounds(supertrendExtremes, EMPTY_SUPERTREND_STATE);
157
- const keltnerBounds = maFamilyBounds(keltnerExtremes, EMPTY_KELTNER_STATE);
158
- const donchianBounds = maFamilyBounds(donchianExtremes, EMPTY_DONCHIAN_STATE);
159
- const ichimokuBounds = maFamilyBounds(ichimokuExtremes, EMPTY_ICHIMOKU_STATE);
160
- const rocBounds = maFamilyBounds(rocExtremes, EMPTY_ROC_STATE);
161
- const trixCombinedMin = Math.min(trixSeriesExtremes?.min ?? Infinity, trixSignalExtremes?.min ?? Infinity);
162
- const trixCombinedMax = Math.max(trixSeriesExtremes?.max ?? -Infinity, trixSignalExtremes?.max ?? -Infinity);
163
- const trixBounds = maFamilyBounds(Number.isFinite(trixCombinedMin) && Number.isFinite(trixCombinedMax)
164
- ? { min: trixCombinedMin, max: trixCombinedMax }
165
- : null, EMPTY_TRIX_STATE);
166
- // HV and Parkinson are non-negative volatility series; pad upward only
167
- const hvValueMax = hvExtremes && Number.isFinite(hvExtremes.max) ? hvExtremes.max * 1.1 : EMPTY_HV_STATE.valueMax;
168
- const parkinsonValueMax = parkinsonExtremes && Number.isFinite(parkinsonExtremes.max)
169
- ? parkinsonExtremes.max * 1.1
170
- : EMPTY_PARKINSON_STATE.valueMax;
171
- const chaikinVolBounds = maFamilyBounds(chaikinVolExtremes, EMPTY_CHAIKIN_VOL_STATE);
172
- const vmaValueMax = vmaExtremes && Number.isFinite(vmaExtremes.max) ? vmaExtremes.max * 1.1 : EMPTY_VMA_STATE.valueMax;
173
- const obvBounds = maFamilyBounds(obvExtremes, EMPTY_OBV_STATE);
174
- const pvtBounds = maFamilyBounds(pvtExtremes, EMPTY_PVT_STATE);
175
- const vwapBounds = maFamilyBounds(vwapExtremes, EMPTY_VWAP_STATE);
176
- return {
177
- rsi: rsiActive ? {
178
- timestamp,
179
- series: bundle.rsi.series,
180
- enabledPeriods: bundle.rsi.enabledPeriods,
181
- params: bundle.rsi.params,
182
- valueMin: 0,
183
- valueMax: 100,
184
- visibleMin: rsiExtremes.min,
185
- visibleMax: rsiExtremes.max,
186
- } : mergeEmptyState(EMPTY_RSI_STATE, timestamp, {
187
- series: bundle.rsi.series,
188
- enabledPeriods: bundle.rsi.enabledPeriods,
189
- params: bundle.rsi.params,
190
- }),
191
- cci: cciActive ? {
192
- timestamp,
193
- series: bundle.cci.series,
194
- params: bundle.cci.params,
195
- valueMin: cciValueMin,
196
- valueMax: cciValueMax,
197
- visibleMin: cciExtremes.min,
198
- visibleMax: cciExtremes.max,
199
- } : mergeEmptyState(EMPTY_CCI_STATE, timestamp, {
200
- series: bundle.cci.series,
201
- params: bundle.cci.params,
202
- }),
203
- stoch: stochActive ? {
204
- timestamp,
205
- series: bundle.stoch.series,
206
- params: bundle.stoch.params,
207
- valueMin: 0,
208
- valueMax: 100,
209
- visibleMin: stochExtremes.min,
210
- visibleMax: stochExtremes.max,
211
- } : mergeEmptyState(EMPTY_STOCH_STATE, timestamp, {
212
- series: bundle.stoch.series,
213
- params: bundle.stoch.params,
214
- }),
215
- mom: momActive ? {
216
- timestamp,
217
- series: bundle.mom.series,
218
- params: bundle.mom.params,
219
- valueMin: momValueMin,
220
- valueMax: momValueMax,
221
- visibleMin: momExtremes.min,
222
- visibleMax: momExtremes.max,
223
- } : mergeEmptyState(EMPTY_MOM_STATE, timestamp, {
224
- series: bundle.mom.series,
225
- params: bundle.mom.params,
226
- }),
227
- wmsr: wmsrActive ? {
228
- timestamp,
229
- series: bundle.wmsr.series,
230
- params: bundle.wmsr.params,
231
- valueMin: -100,
232
- valueMax: 0,
233
- visibleMin: wmsrExtremes.min,
234
- visibleMax: wmsrExtremes.max,
235
- } : mergeEmptyState(EMPTY_WMSR_STATE, timestamp, {
236
- series: bundle.wmsr.series,
237
- params: bundle.wmsr.params,
238
- }),
239
- kst: kstActive ? {
240
- timestamp,
241
- series: bundle.kst.series,
242
- params: bundle.kst.params,
243
- valueMin: kstValueMin,
244
- valueMax: kstValueMax,
245
- visibleMin: kstExtremes.min,
246
- visibleMax: kstExtremes.max,
247
- } : mergeEmptyState(EMPTY_KST_STATE, timestamp, {
248
- series: bundle.kst.series,
249
- params: bundle.kst.params,
250
- }),
251
- fastk: fastkActive ? {
252
- timestamp,
253
- series: bundle.fastk.series,
254
- params: bundle.fastk.params,
255
- valueMin: 0,
256
- valueMax: 100,
257
- visibleMin: fastkExtremes.min,
258
- visibleMax: fastkExtremes.max,
259
- } : mergeEmptyState(EMPTY_FASTK_STATE, timestamp, {
260
- series: bundle.fastk.series,
261
- params: bundle.fastk.params,
262
- }),
263
- macd: macdActive ? {
264
- timestamp,
265
- series: bundle.macd.series,
266
- params: bundle.macd.params,
267
- valueMin: macdValueMin,
268
- valueMax: macdValueMax,
269
- visibleMin: macdExtremes.min,
270
- visibleMax: macdExtremes.max,
271
- latestValues: latestPoint ? {
272
- dif: latestPoint.dif,
273
- dea: latestPoint.dea,
274
- macd: latestPoint.macd,
275
- } : undefined,
276
- } : mergeEmptyState(EMPTY_MACD_STATE, timestamp, {
277
- series: bundle.macd.series,
278
- params: bundle.macd.params,
279
- }),
280
- atr: atrActive ? {
281
- timestamp,
282
- series: bundle.atr.series,
283
- params: bundle.atr.params,
284
- valueMin: 0,
285
- valueMax: atrValueMax,
286
- visibleMin: atrExtremes.min,
287
- visibleMax: atrExtremes.max,
288
- } : mergeEmptyState(EMPTY_ATR_STATE, timestamp, {
289
- series: bundle.atr.series,
290
- params: bundle.atr.params,
291
- }),
292
- wma: wmaActive ? {
293
- timestamp,
294
- series: bundle.wma.series,
295
- params: bundle.wma.params,
296
- valueMin: wmaBounds.valueMin,
297
- valueMax: wmaBounds.valueMax,
298
- visibleMin: wmaExtremes.min,
299
- visibleMax: wmaExtremes.max,
300
- } : mergeEmptyState(EMPTY_WMA_STATE, timestamp, {
301
- series: bundle.wma.series,
302
- params: bundle.wma.params,
303
- }),
304
- dema: demaActive ? {
305
- timestamp,
306
- series: bundle.dema.series,
307
- params: bundle.dema.params,
308
- valueMin: demaBounds.valueMin,
309
- valueMax: demaBounds.valueMax,
310
- visibleMin: demaExtremes.min,
311
- visibleMax: demaExtremes.max,
312
- } : mergeEmptyState(EMPTY_DEMA_STATE, timestamp, {
313
- series: bundle.dema.series,
314
- params: bundle.dema.params,
315
- }),
316
- tema: temaActive ? {
317
- timestamp,
318
- series: bundle.tema.series,
319
- params: bundle.tema.params,
320
- valueMin: temaBounds.valueMin,
321
- valueMax: temaBounds.valueMax,
322
- visibleMin: temaExtremes.min,
323
- visibleMax: temaExtremes.max,
324
- } : mergeEmptyState(EMPTY_TEMA_STATE, timestamp, {
325
- series: bundle.tema.series,
326
- params: bundle.tema.params,
327
- }),
328
- hma: hmaActive ? {
329
- timestamp,
330
- series: bundle.hma.series,
331
- params: bundle.hma.params,
332
- valueMin: hmaBounds.valueMin,
333
- valueMax: hmaBounds.valueMax,
334
- visibleMin: hmaExtremes.min,
335
- visibleMax: hmaExtremes.max,
336
- } : mergeEmptyState(EMPTY_HMA_STATE, timestamp, {
337
- series: bundle.hma.series,
338
- params: bundle.hma.params,
339
- }),
340
- kama: kamaActive ? {
341
- timestamp,
342
- series: bundle.kama.series,
343
- params: bundle.kama.params,
344
- valueMin: kamaBounds.valueMin,
345
- valueMax: kamaBounds.valueMax,
346
- visibleMin: kamaExtremes.min,
347
- visibleMax: kamaExtremes.max,
348
- } : mergeEmptyState(EMPTY_KAMA_STATE, timestamp, {
349
- series: bundle.kama.series,
350
- params: bundle.kama.params,
351
- }),
352
- sar: sarActive ? {
353
- timestamp,
354
- series: bundle.sar.series,
355
- params: bundle.sar.params,
356
- valueMin: sarBounds.valueMin,
357
- valueMax: sarBounds.valueMax,
358
- visibleMin: sarExtremes.min,
359
- visibleMax: sarExtremes.max,
360
- } : mergeEmptyState(EMPTY_SAR_STATE, timestamp, {
361
- series: bundle.sar.series,
362
- params: bundle.sar.params,
363
- }),
364
- supertrend: supertrendActive ? {
365
- timestamp,
366
- series: bundle.supertrend.series,
367
- params: bundle.supertrend.params,
368
- valueMin: supertrendBounds.valueMin,
369
- valueMax: supertrendBounds.valueMax,
370
- visibleMin: supertrendExtremes.min,
371
- visibleMax: supertrendExtremes.max,
372
- } : mergeEmptyState(EMPTY_SUPERTREND_STATE, timestamp, {
373
- series: bundle.supertrend.series,
374
- params: bundle.supertrend.params,
375
- }),
376
- keltner: keltnerActive ? {
377
- timestamp,
378
- series: bundle.keltner.series,
379
- params: bundle.keltner.params,
380
- valueMin: keltnerBounds.valueMin,
381
- valueMax: keltnerBounds.valueMax,
382
- visibleMin: keltnerExtremes.min,
383
- visibleMax: keltnerExtremes.max,
384
- } : mergeEmptyState(EMPTY_KELTNER_STATE, timestamp, {
385
- series: bundle.keltner.series,
386
- params: bundle.keltner.params,
387
- }),
388
- donchian: donchianActive ? {
389
- timestamp,
390
- series: bundle.donchian.series,
391
- params: bundle.donchian.params,
392
- valueMin: donchianBounds.valueMin,
393
- valueMax: donchianBounds.valueMax,
394
- visibleMin: donchianExtremes.min,
395
- visibleMax: donchianExtremes.max,
396
- } : mergeEmptyState(EMPTY_DONCHIAN_STATE, timestamp, {
397
- series: bundle.donchian.series,
398
- params: bundle.donchian.params,
399
- }),
400
- ichimoku: ichimokuActive ? {
401
- timestamp,
402
- series: bundle.ichimoku.series,
403
- params: bundle.ichimoku.params,
404
- valueMin: ichimokuBounds.valueMin,
405
- valueMax: ichimokuBounds.valueMax,
406
- visibleMin: ichimokuExtremes.min,
407
- visibleMax: ichimokuExtremes.max,
408
- } : mergeEmptyState(EMPTY_ICHIMOKU_STATE, timestamp, {
409
- series: bundle.ichimoku.series,
410
- params: bundle.ichimoku.params,
411
- }),
412
- roc: rocActive ? {
413
- timestamp,
414
- series: bundle.roc.series,
415
- params: bundle.roc.params,
416
- valueMin: rocBounds.valueMin,
417
- valueMax: rocBounds.valueMax,
418
- visibleMin: rocExtremes.min,
419
- visibleMax: rocExtremes.max,
420
- } : mergeEmptyState(EMPTY_ROC_STATE, timestamp, {
421
- series: bundle.roc.series,
422
- params: bundle.roc.params,
423
- }),
424
- trix: trixActive ? {
425
- timestamp,
426
- series: bundle.trix.series,
427
- signalSeries: bundle.trix.signalSeries,
428
- params: bundle.trix.params,
429
- valueMin: trixBounds.valueMin,
430
- valueMax: trixBounds.valueMax,
431
- visibleMin: Math.min(trixSeriesExtremes?.min ?? Infinity, trixSignalExtremes?.min ?? Infinity),
432
- visibleMax: Math.max(trixSeriesExtremes?.max ?? -Infinity, trixSignalExtremes?.max ?? -Infinity),
433
- } : mergeEmptyState(EMPTY_TRIX_STATE, timestamp, {
434
- series: bundle.trix.series,
435
- signalSeries: bundle.trix.signalSeries,
436
- params: bundle.trix.params,
437
- }),
438
- hv: hvActive ? {
439
- timestamp,
440
- series: bundle.hv.series,
441
- params: bundle.hv.params,
442
- valueMin: 0,
443
- valueMax: hvValueMax,
444
- visibleMin: hvExtremes.min,
445
- visibleMax: hvExtremes.max,
446
- } : mergeEmptyState(EMPTY_HV_STATE, timestamp, {
447
- series: bundle.hv.series,
448
- params: bundle.hv.params,
449
- }),
450
- parkinson: parkinsonActive ? {
451
- timestamp,
452
- series: bundle.parkinson.series,
453
- params: bundle.parkinson.params,
454
- valueMin: 0,
455
- valueMax: parkinsonValueMax,
456
- visibleMin: parkinsonExtremes.min,
457
- visibleMax: parkinsonExtremes.max,
458
- } : mergeEmptyState(EMPTY_PARKINSON_STATE, timestamp, {
459
- series: bundle.parkinson.series,
460
- params: bundle.parkinson.params,
461
- }),
462
- chaikinVol: chaikinVolActive ? {
463
- timestamp,
464
- series: bundle.chaikinVol.series,
465
- params: bundle.chaikinVol.params,
466
- valueMin: chaikinVolBounds.valueMin,
467
- valueMax: chaikinVolBounds.valueMax,
468
- visibleMin: chaikinVolExtremes.min,
469
- visibleMax: chaikinVolExtremes.max,
470
- } : mergeEmptyState(EMPTY_CHAIKIN_VOL_STATE, timestamp, {
471
- series: bundle.chaikinVol.series,
472
- params: bundle.chaikinVol.params,
473
- }),
474
- vma: vmaActive ? {
475
- timestamp,
476
- series: bundle.vma.series,
477
- params: bundle.vma.params,
478
- valueMin: 0,
479
- valueMax: vmaValueMax,
480
- visibleMin: vmaExtremes.min,
481
- visibleMax: vmaExtremes.max,
482
- } : mergeEmptyState(EMPTY_VMA_STATE, timestamp, {
483
- series: bundle.vma.series,
484
- params: bundle.vma.params,
485
- }),
486
- obv: obvActive ? {
487
- timestamp,
488
- series: bundle.obv.series,
489
- params: bundle.obv.params,
490
- valueMin: obvBounds.valueMin,
491
- valueMax: obvBounds.valueMax,
492
- visibleMin: obvExtremes.min,
493
- visibleMax: obvExtremes.max,
494
- } : mergeEmptyState(EMPTY_OBV_STATE, timestamp, {
495
- series: bundle.obv.series,
496
- params: bundle.obv.params,
497
- }),
498
- pvt: pvtActive ? {
499
- timestamp,
500
- series: bundle.pvt.series,
501
- params: bundle.pvt.params,
502
- valueMin: pvtBounds.valueMin,
503
- valueMax: pvtBounds.valueMax,
504
- visibleMin: pvtExtremes.min,
505
- visibleMax: pvtExtremes.max,
506
- } : mergeEmptyState(EMPTY_PVT_STATE, timestamp, {
507
- series: bundle.pvt.series,
508
- params: bundle.pvt.params,
509
- }),
510
- vwap: vwapActive ? {
511
- timestamp,
512
- series: bundle.vwap.series,
513
- params: bundle.vwap.params,
514
- valueMin: vwapBounds.valueMin,
515
- valueMax: vwapBounds.valueMax,
516
- visibleMin: vwapExtremes.min,
517
- visibleMax: vwapExtremes.max,
518
- } : mergeEmptyState(EMPTY_VWAP_STATE, timestamp, {
519
- series: bundle.vwap.series,
520
- params: bundle.vwap.params,
521
- }),
522
- cmf: cmfActive ? {
523
- timestamp,
524
- series: bundle.cmf.series,
525
- params: bundle.cmf.params,
526
- valueMin: -1,
527
- valueMax: 1,
528
- visibleMin: cmfExtremes.min,
529
- visibleMax: cmfExtremes.max,
530
- } : mergeEmptyState(EMPTY_CMF_STATE, timestamp, {
531
- series: bundle.cmf.series,
532
- params: bundle.cmf.params,
533
- }),
534
- mfi: mfiActive ? {
535
- timestamp,
536
- series: bundle.mfi.series,
537
- params: bundle.mfi.params,
538
- valueMin: 0,
539
- valueMax: 100,
540
- visibleMin: mfiExtremes.min,
541
- visibleMax: mfiExtremes.max,
542
- } : mergeEmptyState(EMPTY_MFI_STATE, timestamp, {
543
- series: bundle.mfi.series,
544
- params: bundle.mfi.params,
545
- }),
546
- pivot: pivotActive ? {
547
- timestamp,
548
- series: bundle.pivot.series,
549
- params: bundle.pivot.params,
550
- valueMin: pivotExtremes.min,
551
- valueMax: pivotExtremes.max,
552
- visibleMin: pivotExtremes.min,
553
- visibleMax: pivotExtremes.max,
554
- } : mergeEmptyState(EMPTY_PIVOT_STATE, timestamp, {
555
- series: bundle.pivot.series,
556
- params: bundle.pivot.params,
557
- }),
558
- fib: fibActive ? {
559
- timestamp,
560
- series: bundle.fib.series,
561
- params: bundle.fib.params,
562
- valueMin: fibExtremes.min,
563
- valueMax: fibExtremes.max,
564
- visibleMin: fibExtremes.min,
565
- visibleMax: fibExtremes.max,
566
- } : mergeEmptyState(EMPTY_FIB_STATE, timestamp, {
567
- series: bundle.fib.series,
568
- params: bundle.fib.params,
569
- }),
570
- structure: structureActive ? {
571
- timestamp,
572
- series: bundle.structure.series,
573
- params: bundle.structure.params,
574
- valueMin: 0,
575
- valueMax: 1,
576
- visibleMin: 0,
577
- visibleMax: 1,
578
- } : mergeEmptyState(EMPTY_STRUCTURE_STATE, timestamp, {
579
- series: bundle.structure.series,
580
- params: bundle.structure.params,
581
- }),
582
- zones: zonesActive ? {
583
- timestamp,
584
- series: bundle.zones.series,
585
- params: bundle.zones.params,
586
- valueMin: 0,
587
- valueMax: 1,
588
- visibleMin: 0,
589
- visibleMax: 1,
590
- } : mergeEmptyState(EMPTY_ZONES_STATE, timestamp, {
591
- series: bundle.zones.series,
592
- params: bundle.zones.params,
593
- }),
594
- volumeProfile: vpActive ? {
595
- timestamp,
596
- series: bundle.volumeProfile.series,
597
- params: bundle.volumeProfile.params,
598
- valueMin: bundle.volumeProfile.series.bins[0]?.priceLow ?? 0,
599
- valueMax: bundle.volumeProfile.series.bins[bundle.volumeProfile.series.bins.length - 1]?.priceHigh ?? 1,
600
- visibleMin: bundle.volumeProfile.series.val,
601
- visibleMax: bundle.volumeProfile.series.vah,
602
- } : mergeEmptyState(EMPTY_VOLUME_PROFILE_STATE, timestamp, {
603
- series: bundle.volumeProfile.series,
604
- params: bundle.volumeProfile.params,
605
- }),
606
- };
47
+ export function composeVisibleSubIndicatorStates(bundle, visibleRange, timestamp, activeMask = {}, getIndicatorMetadata) {
48
+ const states = {};
49
+ for (const indicatorId of METADATA_VISIBLE_STATE_INDICATOR_IDS) {
50
+ states[indicatorId] = composeRequiredMetadataVisibleState(indicatorId, bundle, visibleRange, timestamp, activeMask, getIndicatorMetadata);
51
+ }
52
+ return states;
607
53
  }
608
54
  /**
609
55
  * 从 series bundle 组装所有 render states
610
56
  * 同时计算 visibleMin/visibleMax 等派生字段
611
57
  */
612
- export function composeRenderStates(bundle, visibleRange, timestamp) {
613
- const maExtremes = calcMAExtremes(bundle.ma.series, visibleRange);
614
- const bollExtremes = calcBOLLExtremes(bundle.boll.series, visibleRange);
615
- const expmaExtremes = calcEXPMAExtremes(bundle.expma.series, visibleRange);
616
- const eneExtremes = calcENEExtremes(bundle.ene.series, visibleRange);
617
- const subStates = composeVisibleSubIndicatorStates(bundle, visibleRange, timestamp);
58
+ export function composeRenderStates(bundle, visibleRange, timestamp, getIndicatorMetadata) {
59
+ const mainStates = composeMainRenderStates(bundle, visibleRange, timestamp, getIndicatorMetadata);
60
+ const subStates = composeVisibleSubIndicatorStates(bundle, visibleRange, timestamp, {}, getIndicatorMetadata);
618
61
  return {
619
- ma: {
620
- timestamp,
621
- series: bundle.ma.series,
622
- enabledPeriods: bundle.ma.enabledPeriods,
623
- visibleMin: maExtremes.min,
624
- visibleMax: maExtremes.max,
625
- },
626
- boll: {
627
- timestamp,
628
- series: bundle.boll.series,
629
- params: bundle.boll.params,
630
- visibleMin: bollExtremes.min,
631
- visibleMax: bollExtremes.max,
632
- },
633
- expma: {
634
- timestamp,
635
- series: bundle.expma.series,
636
- params: bundle.expma.params,
637
- visibleMin: expmaExtremes.min,
638
- visibleMax: expmaExtremes.max,
639
- },
640
- ene: {
641
- timestamp,
642
- series: bundle.ene.series,
643
- params: bundle.ene.params,
644
- visibleMin: eneExtremes.min,
645
- visibleMax: eneExtremes.max,
646
- },
62
+ ...mainStates,
647
63
  ...subStates,
648
64
  };
649
65
  }
650
- // ============================================================================
651
- // 极值计算辅助函数
652
- // ============================================================================
653
- function calcMAExtremes(series, range) {
654
- const seriesList = Object.values(series);
655
- if (seriesList.length === 0 || range.start >= seriesList[0].length) {
656
- return { min: Infinity, max: -Infinity };
657
- }
658
- let min = Infinity;
659
- let max = -Infinity;
660
- for (const values of seriesList) {
661
- const end = Math.min(range.end, values.length);
662
- for (let i = range.start; i < end; i++) {
663
- const v = values[i];
664
- if (v !== undefined) {
665
- min = Math.min(min, v);
666
- max = Math.max(max, v);
667
- }
668
- }
669
- }
670
- return { min, max };
671
- }
672
- function calcBOLLExtremes(series, range) {
673
- if (series.length === 0 || range.start >= series.length) {
674
- return { min: Infinity, max: -Infinity };
675
- }
676
- let min = Infinity;
677
- let max = -Infinity;
678
- const end = Math.min(range.end, series.length);
679
- for (let i = range.start; i < end; i++) {
680
- const p = series[i];
681
- if (p) {
682
- min = Math.min(min, p.upper, p.middle, p.lower);
683
- max = Math.max(max, p.upper, p.middle, p.lower);
684
- }
685
- }
686
- return { min, max };
687
- }
688
- function calcEXPMAExtremes(series, range) {
689
- if (series.length === 0 || range.start >= series.length) {
690
- return { min: Infinity, max: -Infinity };
691
- }
692
- let min = Infinity;
693
- let max = -Infinity;
694
- const end = Math.min(range.end, series.length);
695
- for (let i = range.start; i < end; i++) {
696
- const p = series[i];
697
- if (p) {
698
- min = Math.min(min, p.fast, p.slow);
699
- max = Math.max(max, p.fast, p.slow);
700
- }
701
- }
702
- return { min, max };
703
- }
704
- function calcENEExtremes(series, range) {
705
- if (series.length === 0 || range.start >= series.length) {
706
- return { min: Infinity, max: -Infinity };
707
- }
708
- let min = Infinity;
709
- let max = -Infinity;
710
- const end = Math.min(range.end, series.length);
711
- for (let i = range.start; i < end; i++) {
712
- const p = series[i];
713
- if (p) {
714
- min = Math.min(min, p.upper, p.middle, p.lower);
715
- max = Math.max(max, p.upper, p.middle, p.lower);
716
- }
717
- }
718
- return { min, max };
719
- }
720
- function calcRSIExtremes(series, range) {
721
- const seriesList = Object.values(series);
722
- if (seriesList.length === 0 || range.start >= seriesList[0].length) {
723
- return { min: Infinity, max: -Infinity };
724
- }
725
- let min = Infinity;
726
- let max = -Infinity;
727
- for (const values of seriesList) {
728
- const end = Math.min(range.end, values.length);
729
- for (let i = range.start; i < end; i++) {
730
- const v = values[i];
731
- if (v !== undefined) {
732
- min = Math.min(min, v);
733
- max = Math.max(max, v);
734
- }
735
- }
736
- }
737
- return { min, max };
738
- }
739
- function calcCCIExtremes(series, range) {
740
- if (series.length === 0 || range.start >= series.length) {
741
- return { min: Infinity, max: -Infinity };
742
- }
743
- let min = Infinity;
744
- let max = -Infinity;
745
- const end = Math.min(range.end, series.length);
746
- for (let i = range.start; i < end; i++) {
747
- const v = series[i];
748
- if (v !== undefined) {
749
- min = Math.min(min, v);
750
- max = Math.max(max, v);
751
- }
752
- }
753
- return { min, max };
754
- }
755
- function calcSTOCHExtremes(series, range) {
756
- if (series.length === 0 || range.start >= series.length) {
757
- return { min: Infinity, max: -Infinity };
758
- }
759
- let min = Infinity;
760
- let max = -Infinity;
761
- const end = Math.min(range.end, series.length);
762
- for (let i = range.start; i < end; i++) {
763
- const p = series[i];
764
- if (p) {
765
- min = Math.min(min, p.k, p.d);
766
- max = Math.max(max, p.k, p.d);
767
- }
768
- }
769
- return { min, max };
770
- }
771
- function calcMOMExtremes(series, range) {
772
- if (series.length === 0 || range.start >= series.length) {
773
- return { min: Infinity, max: -Infinity };
774
- }
775
- let min = Infinity;
776
- let max = -Infinity;
777
- const end = Math.min(range.end, series.length);
778
- for (let i = range.start; i < end; i++) {
779
- const v = series[i];
780
- if (v !== undefined) {
781
- min = Math.min(min, v);
782
- max = Math.max(max, v);
783
- }
784
- }
785
- return { min, max };
786
- }
787
- function calcWMSRExtremes(series, range) {
788
- if (series.length === 0 || range.start >= series.length) {
789
- return { min: Infinity, max: -Infinity };
790
- }
791
- let min = Infinity;
792
- let max = -Infinity;
793
- const end = Math.min(range.end, series.length);
794
- for (let i = range.start; i < end; i++) {
795
- const v = series[i];
796
- if (v !== undefined) {
797
- min = Math.min(min, v);
798
- max = Math.max(max, v);
799
- }
800
- }
801
- return { min, max };
802
- }
803
- function calcKSTExtremes(series, range) {
804
- // 快速检查:空数据直接返回
805
- if (series.length === 0 || range.start >= series.length) {
806
- return { min: Infinity, max: -Infinity };
807
- }
808
- let min = Infinity;
809
- let max = -Infinity;
810
- const end = Math.min(range.end, series.length);
811
- for (let i = range.start; i < end; i++) {
812
- const p = series[i];
813
- if (p) {
814
- min = Math.min(min, p.kst, p.signal);
815
- max = Math.max(max, p.kst, p.signal);
816
- }
817
- }
818
- return { min, max };
819
- }
820
- function calcFASTKExtremes(series, range) {
821
- if (series.length === 0 || range.start >= series.length) {
822
- return { min: Infinity, max: -Infinity };
823
- }
824
- let min = Infinity;
825
- let max = -Infinity;
826
- const end = Math.min(range.end, series.length);
827
- for (let i = range.start; i < end; i++) {
828
- const v = series[i];
829
- if (v !== undefined) {
830
- min = Math.min(min, v);
831
- max = Math.max(max, v);
832
- }
833
- }
834
- return { min, max };
835
- }
836
- function calcMACDExtremes(series, range) {
837
- if (series.length === 0 || range.start >= series.length) {
838
- return { min: Infinity, max: -Infinity };
839
- }
840
- let min = Infinity;
841
- let max = -Infinity;
842
- const end = Math.min(range.end, series.length);
843
- for (let i = range.start; i < end; i++) {
844
- const p = series[i];
845
- if (p) {
846
- min = Math.min(min, p.dif, p.dea, p.macd);
847
- max = Math.max(max, p.dif, p.dea, p.macd);
848
- }
849
- }
850
- return { min, max };
851
- }
852
- function calcATRExtremes(series, range) {
853
- return calcSparseExtremes(series, range);
854
- }
855
- function calcPivotExtremes(series, range) {
856
- if (series.length === 0 || range.start >= series.length) {
857
- return { min: Infinity, max: -Infinity };
858
- }
859
- let min = Infinity;
860
- let max = -Infinity;
861
- const end = Math.min(range.end, series.length);
862
- for (let i = range.start; i < end; i++) {
863
- const p = series[i];
864
- if (!p)
865
- continue;
866
- for (const v of [p.pp, p.r1, p.r2, p.r3, p.s1, p.s2, p.s3]) {
867
- if (v < min)
868
- min = v;
869
- if (v > max)
870
- max = v;
871
- }
872
- }
873
- return { min, max };
874
- }
875
- function calcFibExtremes(series, range) {
876
- if (series.length === 0 || range.start >= series.length) {
877
- return { min: Infinity, max: -Infinity };
878
- }
879
- let min = Infinity;
880
- let max = -Infinity;
881
- const end = Math.min(range.end, series.length);
882
- for (let i = range.start; i < end; i++) {
883
- const p = series[i];
884
- if (!p)
885
- continue;
886
- if (p.low < min)
887
- min = p.low;
888
- if (p.high > max)
889
- max = p.high;
890
- }
891
- return { min, max };
892
- }
893
- function calcIchimokuExtremes(series, range) {
894
- if (series.length === 0 || range.start >= series.length) {
895
- return { min: Infinity, max: -Infinity };
896
- }
897
- let min = Infinity;
898
- let max = -Infinity;
899
- const end = Math.min(range.end, series.length);
900
- for (let i = range.start; i < end; i++) {
901
- const p = series[i];
902
- if (!p)
66
+ function composeRequiredMetadataVisibleState(indicatorId, bundle, visibleRange, timestamp, activeMask, getIndicatorMetadata) {
67
+ const meta = getIndicatorMetadata(indicatorId);
68
+ if (!meta)
69
+ return undefined;
70
+ const compose = meta.visibleState?.compose;
71
+ if (!compose) {
72
+ throw new Error(`[StateComposer] Missing visibleState.compose for ${indicatorId}`);
73
+ }
74
+ return compose({
75
+ bundle,
76
+ visibleRange,
77
+ timestamp,
78
+ active: activeMask[indicatorId] ?? true,
79
+ });
80
+ }
81
+ function composeMainRenderStates(bundle, visibleRange, timestamp, getIndicatorMetadata) {
82
+ const states = {};
83
+ for (const indicatorId of MAIN_RENDER_STATE_INDICATOR_IDS) {
84
+ const definition = getIndicatorMetadata(indicatorId);
85
+ if (!definition)
903
86
  continue;
904
- for (const field of [p.tenkan, p.kijun, p.spanA, p.spanB, p.chikou]) {
905
- if (field !== undefined) {
906
- if (field < min)
907
- min = field;
908
- if (field > max)
909
- max = field;
910
- }
911
- }
912
- }
913
- return { min, max };
914
- }
915
- function calcBandExtremes(series, range) {
916
- if (series.length === 0 || range.start >= series.length) {
917
- return { min: Infinity, max: -Infinity };
918
- }
919
- let min = Infinity;
920
- let max = -Infinity;
921
- const end = Math.min(range.end, series.length);
922
- for (let i = range.start; i < end; i++) {
923
- const p = series[i];
924
- if (p) {
925
- min = Math.min(min, p.lower);
926
- max = Math.max(max, p.upper);
927
- }
928
- }
929
- return { min, max };
930
- }
931
- function calcSARExtremes(series, range) {
932
- if (series.length === 0 || range.start >= series.length) {
933
- return { min: Infinity, max: -Infinity };
934
- }
935
- let min = Infinity;
936
- let max = -Infinity;
937
- const end = Math.min(range.end, series.length);
938
- for (let i = range.start; i < end; i++) {
939
- const p = series[i];
940
- if (p) {
941
- min = Math.min(min, p.value);
942
- max = Math.max(max, p.value);
943
- }
944
- }
945
- return { min, max };
946
- }
947
- function calcSparseExtremes(series, range) {
948
- if (series.length === 0 || range.start >= series.length) {
949
- return { min: Infinity, max: -Infinity };
950
- }
951
- let min = Infinity;
952
- let max = -Infinity;
953
- const end = Math.min(range.end, series.length);
954
- for (let i = range.start; i < end; i++) {
955
- const v = series[i];
956
- if (v !== undefined) {
957
- min = Math.min(min, v);
958
- max = Math.max(max, v);
87
+ const composeRenderState = definition.mainPane?.composeRenderState;
88
+ if (!composeRenderState) {
89
+ throw new Error(`[StateComposer] Missing mainPane.composeRenderState for ${indicatorId}`);
959
90
  }
91
+ states[indicatorId] = composeRenderState(bundle, visibleRange, timestamp);
960
92
  }
961
- return { min, max };
93
+ return states;
962
94
  }
963
95
  /**
964
96
  * 计算主图指标价格范围
965
97
  * 用于 Chart.draw() 中的 pane.updateRange
966
98
  */
967
- export function computeMainIndicatorPriceRange(bundle, visibleRange, activeMainIndicators) {
99
+ export function computeMainIndicatorPriceRange(bundle, visibleRange, activeMainIndicators, getIndicatorMetadata) {
968
100
  let min = Infinity;
969
101
  let max = -Infinity;
970
- const { start, end } = visibleRange;
971
- // MA
972
- if (activeMainIndicators.has('ma') && Object.keys(bundle.ma.series).length > 0) {
973
- for (const values of Object.values(bundle.ma.series)) {
974
- for (let i = start; i < end && i < values.length; i++) {
975
- const v = values[i];
976
- if (v !== undefined) {
977
- min = Math.min(min, v);
978
- max = Math.max(max, v);
979
- }
980
- }
981
- }
982
- }
983
- // BOLL
984
- if (activeMainIndicators.has('boll') && bundle.boll.series.length > 0) {
985
- for (let i = start; i < end && i < bundle.boll.series.length; i++) {
986
- const p = bundle.boll.series[i];
987
- if (p) {
988
- min = Math.min(min, p.upper, p.middle, p.lower);
989
- max = Math.max(max, p.upper, p.middle, p.lower);
990
- }
991
- }
992
- }
993
- // EXPMA
994
- if (activeMainIndicators.has('expma') && bundle.expma.series.length > 0) {
995
- for (let i = start; i < end && i < bundle.expma.series.length; i++) {
996
- const p = bundle.expma.series[i];
997
- if (p) {
998
- min = Math.min(min, p.fast, p.slow);
999
- max = Math.max(max, p.fast, p.slow);
1000
- }
1001
- }
1002
- }
1003
- // ENE
1004
- if (activeMainIndicators.has('ene') && bundle.ene.series.length > 0) {
1005
- for (let i = start; i < end && i < bundle.ene.series.length; i++) {
1006
- const p = bundle.ene.series[i];
1007
- if (p) {
1008
- min = Math.min(min, p.upper, p.middle, p.lower);
1009
- max = Math.max(max, p.upper, p.middle, p.lower);
1010
- }
1011
- }
102
+ for (const indicatorId of activeMainIndicators) {
103
+ const range = getIndicatorMetadata(indicatorId)?.mainPane?.computePriceRange?.(bundle, visibleRange);
104
+ if (!range)
105
+ continue;
106
+ min = Math.min(min, range.min);
107
+ max = Math.max(max, range.max);
1012
108
  }
1013
109
  if (!Number.isFinite(min) || !Number.isFinite(max)) {
1014
110
  return null;