@363045841yyt/klinechart-core 0.8.7 → 0.8.9

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 (590) hide show
  1. package/dist/config/chartSettings.d.ts +2 -5
  2. package/dist/config/chartSettings.d.ts.map +1 -1
  3. package/dist/config/chartSettings.js +1 -2
  4. package/dist/config/chartSettings.js.map +1 -1
  5. package/dist/controllers/createChartController.d.ts.map +1 -1
  6. package/dist/controllers/createChartController.js +7 -0
  7. package/dist/controllers/createChartController.js.map +1 -1
  8. package/dist/controllers/index.d.ts +1 -1
  9. package/dist/controllers/index.d.ts.map +1 -1
  10. package/dist/controllers/types.d.ts +4 -2
  11. package/dist/controllers/types.d.ts.map +1 -1
  12. package/dist/data-fetchers/dataBuffer.d.ts +6 -0
  13. package/dist/data-fetchers/dataBuffer.d.ts.map +1 -1
  14. package/dist/data-fetchers/dataBuffer.js +50 -3
  15. package/dist/data-fetchers/dataBuffer.js.map +1 -1
  16. package/dist/engine/chart.d.ts +3 -1
  17. package/dist/engine/chart.d.ts.map +1 -1
  18. package/dist/engine/chart.js +9 -45
  19. package/dist/engine/chart.js.map +1 -1
  20. package/dist/engine/controller/interaction.d.ts +62 -0
  21. package/dist/engine/controller/interaction.d.ts.map +1 -1
  22. package/dist/engine/controller/interaction.js +159 -75
  23. package/dist/engine/controller/interaction.js.map +1 -1
  24. package/dist/engine/data/chartDataManager.d.ts +4 -3
  25. package/dist/engine/data/chartDataManager.d.ts.map +1 -1
  26. package/dist/engine/data/chartDataManager.js +39 -41
  27. package/dist/engine/data/chartDataManager.js.map +1 -1
  28. package/dist/engine/drawing/index.d.ts +2 -5
  29. package/dist/engine/drawing/index.d.ts.map +1 -1
  30. package/dist/engine/drawing/index.js +2 -33
  31. package/dist/engine/drawing/index.js.map +1 -1
  32. package/dist/engine/drawing/interaction.js +1 -1
  33. package/dist/engine/drawing/interaction.js.map +1 -1
  34. package/dist/engine/drawing/linearRegression.d.ts +6 -0
  35. package/dist/engine/drawing/linearRegression.d.ts.map +1 -0
  36. package/dist/engine/drawing/linearRegression.js +34 -0
  37. package/dist/engine/drawing/linearRegression.js.map +1 -0
  38. package/dist/engine/drawing/plugin.d.ts.map +1 -1
  39. package/dist/engine/drawing/plugin.js +104 -94
  40. package/dist/engine/drawing/plugin.js.map +1 -1
  41. package/dist/engine/indicators/calculators/_shared.d.ts +3 -0
  42. package/dist/engine/indicators/calculators/_shared.d.ts.map +1 -0
  43. package/dist/engine/indicators/calculators/_shared.js +45 -0
  44. package/dist/engine/indicators/calculators/_shared.js.map +1 -0
  45. package/dist/engine/indicators/calculators/bands.d.ts +44 -0
  46. package/dist/engine/indicators/calculators/bands.d.ts.map +1 -0
  47. package/dist/engine/indicators/calculators/bands.js +268 -0
  48. package/dist/engine/indicators/calculators/bands.js.map +1 -0
  49. package/dist/engine/indicators/calculators/index.d.ts +33 -0
  50. package/dist/engine/indicators/calculators/index.d.ts.map +1 -0
  51. package/dist/engine/indicators/calculators/index.js +19 -0
  52. package/dist/engine/indicators/calculators/index.js.map +1 -0
  53. package/dist/engine/indicators/calculators/movingAverages.d.ts +21 -0
  54. package/dist/engine/indicators/calculators/movingAverages.d.ts.map +1 -0
  55. package/dist/engine/indicators/calculators/movingAverages.js +143 -0
  56. package/dist/engine/indicators/calculators/movingAverages.js.map +1 -0
  57. package/dist/engine/indicators/calculators/oscillators.d.ts +29 -0
  58. package/dist/engine/indicators/calculators/oscillators.d.ts.map +1 -0
  59. package/dist/engine/indicators/calculators/oscillators.js +349 -0
  60. package/dist/engine/indicators/calculators/oscillators.js.map +1 -0
  61. package/dist/engine/indicators/calculators/patterns.d.ts +54 -0
  62. package/dist/engine/indicators/calculators/patterns.d.ts.map +1 -0
  63. package/dist/engine/indicators/calculators/patterns.js +214 -0
  64. package/dist/engine/indicators/calculators/patterns.js.map +1 -0
  65. package/dist/engine/indicators/calculators/volatility.d.ts +6 -0
  66. package/dist/engine/indicators/calculators/volatility.d.ts.map +1 -0
  67. package/dist/engine/indicators/calculators/volatility.js +114 -0
  68. package/dist/engine/indicators/calculators/volatility.js.map +1 -0
  69. package/dist/engine/indicators/calculators/volume.d.ts +21 -0
  70. package/dist/engine/indicators/calculators/volume.d.ts.map +1 -0
  71. package/dist/engine/indicators/calculators/volume.js +218 -0
  72. package/dist/engine/indicators/calculators/volume.js.map +1 -0
  73. package/dist/engine/indicators/calculators.d.ts +1 -361
  74. package/dist/engine/indicators/calculators.d.ts.map +1 -1
  75. package/dist/engine/indicators/calculators.js +1 -1787
  76. package/dist/engine/indicators/calculators.js.map +1 -1
  77. package/dist/engine/indicators/chartIndicatorManager.d.ts.map +1 -1
  78. package/dist/engine/indicators/chartIndicatorManager.js +3 -44
  79. package/dist/engine/indicators/chartIndicatorManager.js.map +1 -1
  80. package/dist/engine/indicators/indicatorRegistry.d.ts +8 -0
  81. package/dist/engine/indicators/indicatorRegistry.d.ts.map +1 -1
  82. package/dist/engine/indicators/indicatorRegistry.js +16 -0
  83. package/dist/engine/indicators/indicatorRegistry.js.map +1 -1
  84. package/dist/engine/indicators/indicatorRuntime.js +2 -2
  85. package/dist/engine/indicators/indicatorRuntime.js.map +1 -1
  86. package/dist/engine/indicators/scheduler.d.ts +2 -2
  87. package/dist/engine/indicators/scheduler.d.ts.map +1 -1
  88. package/dist/engine/indicators/scheduler.js +15 -155
  89. package/dist/engine/indicators/scheduler.js.map +1 -1
  90. package/dist/engine/indicators/{atrState.d.ts → state/atrState.d.ts} +1 -1
  91. package/dist/engine/indicators/state/atrState.d.ts.map +1 -0
  92. package/dist/engine/indicators/{atrState.js → state/atrState.js} +1 -1
  93. package/dist/engine/indicators/state/atrState.js.map +1 -0
  94. package/dist/engine/indicators/{bollState.d.ts → state/bollState.d.ts} +4 -2
  95. package/dist/engine/indicators/state/bollState.d.ts.map +1 -0
  96. package/dist/engine/indicators/{bollState.js → state/bollState.js} +5 -3
  97. package/dist/engine/indicators/state/bollState.js.map +1 -0
  98. package/dist/engine/indicators/{cciState.d.ts → state/cciState.d.ts} +1 -1
  99. package/dist/engine/indicators/state/cciState.d.ts.map +1 -0
  100. package/dist/engine/indicators/{cciState.js → state/cciState.js} +1 -1
  101. package/dist/engine/indicators/state/cciState.js.map +1 -0
  102. package/dist/engine/indicators/{chaikinVolState.d.ts → state/chaikinVolState.d.ts} +1 -1
  103. package/dist/engine/indicators/state/chaikinVolState.d.ts.map +1 -0
  104. package/dist/engine/indicators/{chaikinVolState.js → state/chaikinVolState.js} +1 -1
  105. package/dist/engine/indicators/state/chaikinVolState.js.map +1 -0
  106. package/dist/engine/indicators/{cmfState.d.ts → state/cmfState.d.ts} +1 -1
  107. package/dist/engine/indicators/state/cmfState.d.ts.map +1 -0
  108. package/dist/engine/indicators/{cmfState.js → state/cmfState.js} +1 -1
  109. package/dist/engine/indicators/state/cmfState.js.map +1 -0
  110. package/dist/engine/indicators/{demaState.d.ts → state/demaState.d.ts} +1 -1
  111. package/dist/engine/indicators/state/demaState.d.ts.map +1 -0
  112. package/dist/engine/indicators/{demaState.js → state/demaState.js} +1 -1
  113. package/dist/engine/indicators/state/demaState.js.map +1 -0
  114. package/dist/engine/indicators/{donchianState.d.ts → state/donchianState.d.ts} +1 -1
  115. package/dist/engine/indicators/state/donchianState.d.ts.map +1 -0
  116. package/dist/engine/indicators/{donchianState.js → state/donchianState.js} +1 -1
  117. package/dist/engine/indicators/state/donchianState.js.map +1 -0
  118. package/dist/engine/indicators/{eneState.d.ts → state/eneState.d.ts} +4 -2
  119. package/dist/engine/indicators/state/eneState.d.ts.map +1 -0
  120. package/dist/engine/indicators/{eneState.js → state/eneState.js} +5 -3
  121. package/dist/engine/indicators/state/eneState.js.map +1 -0
  122. package/dist/engine/indicators/{expmaState.d.ts → state/expmaState.d.ts} +4 -2
  123. package/dist/engine/indicators/state/expmaState.d.ts.map +1 -0
  124. package/dist/engine/indicators/{expmaState.js → state/expmaState.js} +5 -3
  125. package/dist/engine/indicators/state/expmaState.js.map +1 -0
  126. package/dist/engine/indicators/{fastkState.d.ts → state/fastkState.d.ts} +1 -1
  127. package/dist/engine/indicators/state/fastkState.d.ts.map +1 -0
  128. package/dist/engine/indicators/{fastkState.js → state/fastkState.js} +1 -1
  129. package/dist/engine/indicators/state/fastkState.js.map +1 -0
  130. package/dist/engine/indicators/{fibState.d.ts → state/fibState.d.ts} +1 -1
  131. package/dist/engine/indicators/state/fibState.d.ts.map +1 -0
  132. package/dist/engine/indicators/{fibState.js → state/fibState.js} +1 -1
  133. package/dist/engine/indicators/state/fibState.js.map +1 -0
  134. package/dist/engine/indicators/{hmaState.d.ts → state/hmaState.d.ts} +1 -1
  135. package/dist/engine/indicators/state/hmaState.d.ts.map +1 -0
  136. package/dist/engine/indicators/{hmaState.js → state/hmaState.js} +1 -1
  137. package/dist/engine/indicators/state/hmaState.js.map +1 -0
  138. package/dist/engine/indicators/{hvState.d.ts → state/hvState.d.ts} +1 -1
  139. package/dist/engine/indicators/state/hvState.d.ts.map +1 -0
  140. package/dist/engine/indicators/{hvState.js → state/hvState.js} +1 -1
  141. package/dist/engine/indicators/state/hvState.js.map +1 -0
  142. package/dist/engine/indicators/{ichimokuState.d.ts → state/ichimokuState.d.ts} +1 -1
  143. package/dist/engine/indicators/state/ichimokuState.d.ts.map +1 -0
  144. package/dist/engine/indicators/{ichimokuState.js → state/ichimokuState.js} +1 -1
  145. package/dist/engine/indicators/state/ichimokuState.js.map +1 -0
  146. package/dist/engine/indicators/{kamaState.d.ts → state/kamaState.d.ts} +1 -1
  147. package/dist/engine/indicators/state/kamaState.d.ts.map +1 -0
  148. package/dist/engine/indicators/{kamaState.js → state/kamaState.js} +1 -1
  149. package/dist/engine/indicators/state/kamaState.js.map +1 -0
  150. package/dist/engine/indicators/{keltnerState.d.ts → state/keltnerState.d.ts} +1 -1
  151. package/dist/engine/indicators/state/keltnerState.d.ts.map +1 -0
  152. package/dist/engine/indicators/{keltnerState.js → state/keltnerState.js} +1 -1
  153. package/dist/engine/indicators/state/keltnerState.js.map +1 -0
  154. package/dist/engine/indicators/{kstState.d.ts → state/kstState.d.ts} +2 -2
  155. package/dist/engine/indicators/state/kstState.d.ts.map +1 -0
  156. package/dist/engine/indicators/{kstState.js → state/kstState.js} +1 -1
  157. package/dist/engine/indicators/state/kstState.js.map +1 -0
  158. package/dist/engine/indicators/{maState.d.ts → state/maState.d.ts} +1 -1
  159. package/dist/engine/indicators/state/maState.d.ts.map +1 -0
  160. package/dist/engine/indicators/{maState.js → state/maState.js} +1 -1
  161. package/dist/engine/indicators/state/maState.js.map +1 -0
  162. package/dist/engine/indicators/{macdState.d.ts → state/macdState.d.ts} +3 -19
  163. package/dist/engine/indicators/state/macdState.d.ts.map +1 -0
  164. package/dist/engine/indicators/state/macdState.js.map +1 -0
  165. package/dist/engine/indicators/{mfiState.d.ts → state/mfiState.d.ts} +1 -1
  166. package/dist/engine/indicators/state/mfiState.d.ts.map +1 -0
  167. package/dist/engine/indicators/{mfiState.js → state/mfiState.js} +1 -1
  168. package/dist/engine/indicators/state/mfiState.js.map +1 -0
  169. package/dist/engine/indicators/{momState.d.ts → state/momState.d.ts} +1 -1
  170. package/dist/engine/indicators/state/momState.d.ts.map +1 -0
  171. package/dist/engine/indicators/{momState.js → state/momState.js} +1 -1
  172. package/dist/engine/indicators/state/momState.js.map +1 -0
  173. package/dist/engine/indicators/{obvState.d.ts → state/obvState.d.ts} +1 -1
  174. package/dist/engine/indicators/state/obvState.d.ts.map +1 -0
  175. package/dist/engine/indicators/{obvState.js → state/obvState.js} +1 -1
  176. package/dist/engine/indicators/state/obvState.js.map +1 -0
  177. package/dist/engine/indicators/{parkinsonState.d.ts → state/parkinsonState.d.ts} +1 -1
  178. package/dist/engine/indicators/state/parkinsonState.d.ts.map +1 -0
  179. package/dist/engine/indicators/{parkinsonState.js → state/parkinsonState.js} +1 -1
  180. package/dist/engine/indicators/state/parkinsonState.js.map +1 -0
  181. package/dist/engine/indicators/{pivotState.d.ts → state/pivotState.d.ts} +1 -1
  182. package/dist/engine/indicators/state/pivotState.d.ts.map +1 -0
  183. package/dist/engine/indicators/{pivotState.js → state/pivotState.js} +1 -1
  184. package/dist/engine/indicators/state/pivotState.js.map +1 -0
  185. package/dist/engine/indicators/{pvtState.d.ts → state/pvtState.d.ts} +1 -1
  186. package/dist/engine/indicators/state/pvtState.d.ts.map +1 -0
  187. package/dist/engine/indicators/{pvtState.js → state/pvtState.js} +1 -1
  188. package/dist/engine/indicators/state/pvtState.js.map +1 -0
  189. package/dist/engine/indicators/{rocState.d.ts → state/rocState.d.ts} +1 -1
  190. package/dist/engine/indicators/state/rocState.d.ts.map +1 -0
  191. package/dist/engine/indicators/{rocState.js → state/rocState.js} +1 -1
  192. package/dist/engine/indicators/state/rocState.js.map +1 -0
  193. package/dist/engine/indicators/{rsiState.d.ts → state/rsiState.d.ts} +4 -1
  194. package/dist/engine/indicators/state/rsiState.d.ts.map +1 -0
  195. package/dist/engine/indicators/{rsiState.js → state/rsiState.js} +7 -4
  196. package/dist/engine/indicators/state/rsiState.js.map +1 -0
  197. package/dist/engine/indicators/{sarState.d.ts → state/sarState.d.ts} +1 -1
  198. package/dist/engine/indicators/state/sarState.d.ts.map +1 -0
  199. package/dist/engine/indicators/{sarState.js → state/sarState.js} +1 -1
  200. package/dist/engine/indicators/state/sarState.js.map +1 -0
  201. package/dist/engine/indicators/{stochState.d.ts → state/stochState.d.ts} +2 -2
  202. package/dist/engine/indicators/state/stochState.d.ts.map +1 -0
  203. package/dist/engine/indicators/{stochState.js → state/stochState.js} +1 -1
  204. package/dist/engine/indicators/state/stochState.js.map +1 -0
  205. package/dist/engine/indicators/{structureState.d.ts → state/structureState.d.ts} +1 -1
  206. package/dist/engine/indicators/state/structureState.d.ts.map +1 -0
  207. package/dist/engine/indicators/{structureState.js → state/structureState.js} +1 -1
  208. package/dist/engine/indicators/state/structureState.js.map +1 -0
  209. package/dist/engine/indicators/{supertrendState.d.ts → state/supertrendState.d.ts} +1 -1
  210. package/dist/engine/indicators/state/supertrendState.d.ts.map +1 -0
  211. package/dist/engine/indicators/{supertrendState.js → state/supertrendState.js} +1 -1
  212. package/dist/engine/indicators/state/supertrendState.js.map +1 -0
  213. package/dist/engine/indicators/{temaState.d.ts → state/temaState.d.ts} +1 -1
  214. package/dist/engine/indicators/state/temaState.d.ts.map +1 -0
  215. package/dist/engine/indicators/{temaState.js → state/temaState.js} +1 -1
  216. package/dist/engine/indicators/state/temaState.js.map +1 -0
  217. package/dist/engine/indicators/{trixState.d.ts → state/trixState.d.ts} +1 -1
  218. package/dist/engine/indicators/state/trixState.d.ts.map +1 -0
  219. package/dist/engine/indicators/{trixState.js → state/trixState.js} +1 -1
  220. package/dist/engine/indicators/state/trixState.js.map +1 -0
  221. package/dist/engine/indicators/{vmaState.d.ts → state/vmaState.d.ts} +1 -1
  222. package/dist/engine/indicators/state/vmaState.d.ts.map +1 -0
  223. package/dist/engine/indicators/{vmaState.js → state/vmaState.js} +1 -1
  224. package/dist/engine/indicators/state/vmaState.js.map +1 -0
  225. package/dist/engine/indicators/{volumeProfileState.d.ts → state/volumeProfileState.d.ts} +1 -1
  226. package/dist/engine/indicators/state/volumeProfileState.d.ts.map +1 -0
  227. package/dist/engine/indicators/{volumeProfileState.js → state/volumeProfileState.js} +1 -1
  228. package/dist/engine/indicators/state/volumeProfileState.js.map +1 -0
  229. package/dist/engine/indicators/{vwapState.d.ts → state/vwapState.d.ts} +1 -1
  230. package/dist/engine/indicators/state/vwapState.d.ts.map +1 -0
  231. package/dist/engine/indicators/{vwapState.js → state/vwapState.js} +1 -1
  232. package/dist/engine/indicators/state/vwapState.js.map +1 -0
  233. package/dist/engine/indicators/{wmaState.d.ts → state/wmaState.d.ts} +1 -1
  234. package/dist/engine/indicators/state/wmaState.d.ts.map +1 -0
  235. package/dist/engine/indicators/{wmaState.js → state/wmaState.js} +1 -1
  236. package/dist/engine/indicators/state/wmaState.js.map +1 -0
  237. package/dist/engine/indicators/{wmsrState.d.ts → state/wmsrState.d.ts} +1 -1
  238. package/dist/engine/indicators/state/wmsrState.d.ts.map +1 -0
  239. package/dist/engine/indicators/{wmsrState.js → state/wmsrState.js} +1 -1
  240. package/dist/engine/indicators/state/wmsrState.js.map +1 -0
  241. package/dist/engine/indicators/{zonesState.d.ts → state/zonesState.d.ts} +1 -1
  242. package/dist/engine/indicators/state/zonesState.d.ts.map +1 -0
  243. package/dist/engine/indicators/{zonesState.js → state/zonesState.js} +1 -1
  244. package/dist/engine/indicators/state/zonesState.js.map +1 -0
  245. package/dist/engine/indicators/stateComposer.d.ts +39 -39
  246. package/dist/engine/indicators/stateComposer.d.ts.map +1 -1
  247. package/dist/engine/indicators/visibleStateComposers.d.ts.map +1 -1
  248. package/dist/engine/indicators/visibleStateComposers.js +192 -31
  249. package/dist/engine/indicators/visibleStateComposers.js.map +1 -1
  250. package/dist/engine/layout/pane.js +2 -2
  251. package/dist/engine/layout/pane.js.map +1 -1
  252. package/dist/engine/paneRenderer.d.ts +2 -6
  253. package/dist/engine/paneRenderer.d.ts.map +1 -1
  254. package/dist/engine/paneRenderer.js.map +1 -1
  255. package/dist/engine/render/chartRenderer.d.ts.map +1 -1
  256. package/dist/engine/render/chartRenderer.js +33 -3
  257. package/dist/engine/render/chartRenderer.js.map +1 -1
  258. package/dist/engine/renderers/Indicator/atr.js +2 -2
  259. package/dist/engine/renderers/Indicator/atr.js.map +1 -1
  260. package/dist/engine/renderers/Indicator/cci.js +1 -1
  261. package/dist/engine/renderers/Indicator/cci.js.map +1 -1
  262. package/dist/engine/renderers/Indicator/chaikinVol.js +1 -1
  263. package/dist/engine/renderers/Indicator/chaikinVol.js.map +1 -1
  264. package/dist/engine/renderers/Indicator/cmf.js +1 -1
  265. package/dist/engine/renderers/Indicator/cmf.js.map +1 -1
  266. package/dist/engine/renderers/Indicator/dema.js +1 -1
  267. package/dist/engine/renderers/Indicator/dema.js.map +1 -1
  268. package/dist/engine/renderers/Indicator/donchian.js +1 -1
  269. package/dist/engine/renderers/Indicator/donchian.js.map +1 -1
  270. package/dist/engine/renderers/Indicator/fastk.js +1 -1
  271. package/dist/engine/renderers/Indicator/fastk.js.map +1 -1
  272. package/dist/engine/renderers/Indicator/fib.js +1 -1
  273. package/dist/engine/renderers/Indicator/fib.js.map +1 -1
  274. package/dist/engine/renderers/Indicator/hma.js +1 -1
  275. package/dist/engine/renderers/Indicator/hma.js.map +1 -1
  276. package/dist/engine/renderers/Indicator/hv.js +2 -2
  277. package/dist/engine/renderers/Indicator/hv.js.map +1 -1
  278. package/dist/engine/renderers/Indicator/ichimoku.js +95 -92
  279. package/dist/engine/renderers/Indicator/ichimoku.js.map +1 -1
  280. package/dist/engine/renderers/Indicator/kama.js +1 -1
  281. package/dist/engine/renderers/Indicator/kama.js.map +1 -1
  282. package/dist/engine/renderers/Indicator/keltner.js +1 -1
  283. package/dist/engine/renderers/Indicator/keltner.js.map +1 -1
  284. package/dist/engine/renderers/Indicator/kst.js +2 -2
  285. package/dist/engine/renderers/Indicator/kst.js.map +1 -1
  286. package/dist/engine/renderers/Indicator/ma.js.map +1 -1
  287. package/dist/engine/renderers/Indicator/macd.js +1 -1
  288. package/dist/engine/renderers/Indicator/macd.js.map +1 -1
  289. package/dist/engine/renderers/Indicator/mfi.js +1 -1
  290. package/dist/engine/renderers/Indicator/mfi.js.map +1 -1
  291. package/dist/engine/renderers/Indicator/mom.js +2 -2
  292. package/dist/engine/renderers/Indicator/mom.js.map +1 -1
  293. package/dist/engine/renderers/Indicator/obv.js +1 -1
  294. package/dist/engine/renderers/Indicator/obv.js.map +1 -1
  295. package/dist/engine/renderers/Indicator/parkinson.js +2 -2
  296. package/dist/engine/renderers/Indicator/parkinson.js.map +1 -1
  297. package/dist/engine/renderers/Indicator/pivot.js +1 -1
  298. package/dist/engine/renderers/Indicator/pivot.js.map +1 -1
  299. package/dist/engine/renderers/Indicator/pvt.js +1 -1
  300. package/dist/engine/renderers/Indicator/pvt.js.map +1 -1
  301. package/dist/engine/renderers/Indicator/roc.js +1 -1
  302. package/dist/engine/renderers/Indicator/roc.js.map +1 -1
  303. package/dist/engine/renderers/Indicator/rsi.js +1 -1
  304. package/dist/engine/renderers/Indicator/rsi.js.map +1 -1
  305. package/dist/engine/renderers/Indicator/sar.js +1 -1
  306. package/dist/engine/renderers/Indicator/sar.js.map +1 -1
  307. package/dist/engine/renderers/Indicator/stoch.js +1 -1
  308. package/dist/engine/renderers/Indicator/stoch.js.map +1 -1
  309. package/dist/engine/renderers/Indicator/structure.js +1 -1
  310. package/dist/engine/renderers/Indicator/structure.js.map +1 -1
  311. package/dist/engine/renderers/Indicator/supertrend.js +1 -1
  312. package/dist/engine/renderers/Indicator/supertrend.js.map +1 -1
  313. package/dist/engine/renderers/Indicator/tema.js +1 -1
  314. package/dist/engine/renderers/Indicator/tema.js.map +1 -1
  315. package/dist/engine/renderers/Indicator/trix.js +2 -2
  316. package/dist/engine/renderers/Indicator/trix.js.map +1 -1
  317. package/dist/engine/renderers/Indicator/vma.js +2 -2
  318. package/dist/engine/renderers/Indicator/vma.js.map +1 -1
  319. package/dist/engine/renderers/Indicator/volumeProfile.js +1 -1
  320. package/dist/engine/renderers/Indicator/volumeProfile.js.map +1 -1
  321. package/dist/engine/renderers/Indicator/vwap.js +1 -1
  322. package/dist/engine/renderers/Indicator/vwap.js.map +1 -1
  323. package/dist/engine/renderers/Indicator/wma.js +1 -1
  324. package/dist/engine/renderers/Indicator/wma.js.map +1 -1
  325. package/dist/engine/renderers/Indicator/wmsr.js +1 -1
  326. package/dist/engine/renderers/Indicator/wmsr.js.map +1 -1
  327. package/dist/engine/renderers/Indicator/zones.js +1 -1
  328. package/dist/engine/renderers/Indicator/zones.js.map +1 -1
  329. package/dist/engine/renderers/candle.d.ts.map +1 -1
  330. package/dist/engine/renderers/candle.js +94 -60
  331. package/dist/engine/renderers/candle.js.map +1 -1
  332. package/dist/engine/renderers/extremaMarkers.d.ts.map +1 -1
  333. package/dist/engine/renderers/extremaMarkers.js +5 -6
  334. package/dist/engine/renderers/extremaMarkers.js.map +1 -1
  335. package/dist/engine/renderers/gridLines.d.ts.map +1 -1
  336. package/dist/engine/renderers/gridLines.js +8 -36
  337. package/dist/engine/renderers/gridLines.js.map +1 -1
  338. package/dist/engine/renderers/leftYAxis.d.ts.map +1 -1
  339. package/dist/engine/renderers/leftYAxis.js +54 -21
  340. package/dist/engine/renderers/leftYAxis.js.map +1 -1
  341. package/dist/engine/renderers/timeAxis.d.ts.map +1 -1
  342. package/dist/engine/renderers/timeAxis.js +2 -0
  343. package/dist/engine/renderers/timeAxis.js.map +1 -1
  344. package/dist/engine/renderers/timeShare.js +1 -1
  345. package/dist/engine/renderers/timeShare.js.map +1 -1
  346. package/dist/engine/renderers/yAxis.d.ts.map +1 -1
  347. package/dist/engine/renderers/yAxis.js +16 -22
  348. package/dist/engine/renderers/yAxis.js.map +1 -1
  349. package/dist/engine/utils/tickPosition.d.ts +12 -0
  350. package/dist/engine/utils/tickPosition.d.ts.map +1 -1
  351. package/dist/engine/utils/tickPosition.js +15 -0
  352. package/dist/engine/utils/tickPosition.js.map +1 -1
  353. package/dist/engine/viewport/chartViewportManager.d.ts +34 -5
  354. package/dist/engine/viewport/chartViewportManager.d.ts.map +1 -1
  355. package/dist/engine/viewport/chartViewportManager.js +61 -30
  356. package/dist/engine/viewport/chartViewportManager.js.map +1 -1
  357. package/dist/mcp/chartBridge.js +1 -1
  358. package/dist/mcp/chartBridge.js.map +1 -1
  359. package/dist/plugin/types.d.ts +13 -0
  360. package/dist/plugin/types.d.ts.map +1 -1
  361. package/dist/plugin/types.js.map +1 -1
  362. package/dist/utils/dateFormat.d.ts +2 -2
  363. package/dist/utils/dateFormat.d.ts.map +1 -1
  364. package/dist/utils/dateFormat.js +6 -6
  365. package/dist/utils/dateFormat.js.map +1 -1
  366. package/dist/utils/kLineDraw/axis.d.ts +4 -0
  367. package/dist/utils/kLineDraw/axis.d.ts.map +1 -1
  368. package/dist/utils/kLineDraw/axis.js +2 -2
  369. package/dist/utils/kLineDraw/axis.js.map +1 -1
  370. package/dist/utils/volumePrice.d.ts.map +1 -1
  371. package/dist/utils/volumePrice.js +20 -3
  372. package/dist/utils/volumePrice.js.map +1 -1
  373. package/dist/version.d.ts +1 -1
  374. package/dist/version.js +1 -1
  375. package/package.json +1 -1
  376. package/src/config/chartSettings.ts +1 -2
  377. package/src/controllers/createChartController.ts +9 -2
  378. package/src/controllers/index.ts +1 -1
  379. package/src/controllers/types.ts +4 -2
  380. package/src/data-fetchers/__tests__/dataBuffer.test.ts +135 -0
  381. package/src/data-fetchers/dataBuffer.ts +61 -9
  382. package/src/engine/chart.ts +11 -46
  383. package/src/engine/controller/interaction.ts +187 -77
  384. package/src/engine/data/chartDataManager.ts +43 -43
  385. package/src/engine/drawing/index.ts +2 -36
  386. package/src/engine/drawing/interaction.ts +1 -1
  387. package/src/engine/drawing/linearRegression.ts +36 -0
  388. package/src/engine/drawing/plugin.ts +160 -97
  389. package/src/engine/indicators/__tests__/calculators.test.ts +9 -1
  390. package/src/engine/indicators/__tests__/indicatorRegistry.test.ts +10 -5
  391. package/src/engine/indicators/__tests__/scheduler.test.ts +12 -12
  392. package/src/engine/indicators/__tests__/soa.test.ts +46 -12
  393. package/src/engine/indicators/calculators/_shared.ts +44 -0
  394. package/src/engine/indicators/calculators/bands.ts +365 -0
  395. package/src/engine/indicators/calculators/index.ts +37 -0
  396. package/src/engine/indicators/calculators/movingAverages.ts +189 -0
  397. package/src/engine/indicators/calculators/oscillators.ts +446 -0
  398. package/src/engine/indicators/calculators/patterns.ts +287 -0
  399. package/src/engine/indicators/calculators/volatility.ts +150 -0
  400. package/src/engine/indicators/calculators/volume.ts +248 -0
  401. package/src/engine/indicators/calculators.ts +1 -2460
  402. package/src/engine/indicators/chartIndicatorManager.ts +3 -44
  403. package/src/engine/indicators/indicatorRegistry.ts +15 -0
  404. package/src/engine/indicators/indicatorRuntime.ts +2 -2
  405. package/src/engine/indicators/scheduler.ts +39 -154
  406. package/src/engine/indicators/{atrState.ts → state/atrState.ts} +2 -2
  407. package/src/engine/indicators/{bollState.ts → state/bollState.ts} +8 -5
  408. package/src/engine/indicators/{cciState.ts → state/cciState.ts} +2 -2
  409. package/src/engine/indicators/{chaikinVolState.ts → state/chaikinVolState.ts} +2 -2
  410. package/src/engine/indicators/{cmfState.ts → state/cmfState.ts} +2 -2
  411. package/src/engine/indicators/{demaState.ts → state/demaState.ts} +2 -2
  412. package/src/engine/indicators/{donchianState.ts → state/donchianState.ts} +2 -2
  413. package/src/engine/indicators/{eneState.ts → state/eneState.ts} +8 -5
  414. package/src/engine/indicators/{expmaState.ts → state/expmaState.ts} +8 -5
  415. package/src/engine/indicators/{fastkState.ts → state/fastkState.ts} +2 -2
  416. package/src/engine/indicators/{fibState.ts → state/fibState.ts} +2 -2
  417. package/src/engine/indicators/{hmaState.ts → state/hmaState.ts} +2 -2
  418. package/src/engine/indicators/{hvState.ts → state/hvState.ts} +2 -2
  419. package/src/engine/indicators/{ichimokuState.ts → state/ichimokuState.ts} +2 -2
  420. package/src/engine/indicators/{kamaState.ts → state/kamaState.ts} +2 -2
  421. package/src/engine/indicators/{keltnerState.ts → state/keltnerState.ts} +2 -2
  422. package/src/engine/indicators/{kstState.ts → state/kstState.ts} +3 -3
  423. package/src/engine/indicators/{maState.ts → state/maState.ts} +2 -2
  424. package/src/engine/indicators/{macdState.ts → state/macdState.ts} +3 -20
  425. package/src/engine/indicators/{mfiState.ts → state/mfiState.ts} +2 -2
  426. package/src/engine/indicators/{momState.ts → state/momState.ts} +2 -2
  427. package/src/engine/indicators/{obvState.ts → state/obvState.ts} +2 -2
  428. package/src/engine/indicators/{parkinsonState.ts → state/parkinsonState.ts} +2 -2
  429. package/src/engine/indicators/{pivotState.ts → state/pivotState.ts} +2 -2
  430. package/src/engine/indicators/{pvtState.ts → state/pvtState.ts} +2 -2
  431. package/src/engine/indicators/{rocState.ts → state/rocState.ts} +2 -2
  432. package/src/engine/indicators/{rsiState.ts → state/rsiState.ts} +9 -5
  433. package/src/engine/indicators/{sarState.ts → state/sarState.ts} +2 -2
  434. package/src/engine/indicators/{stochState.ts → state/stochState.ts} +3 -3
  435. package/src/engine/indicators/{structureState.ts → state/structureState.ts} +2 -2
  436. package/src/engine/indicators/{supertrendState.ts → state/supertrendState.ts} +2 -2
  437. package/src/engine/indicators/{temaState.ts → state/temaState.ts} +2 -2
  438. package/src/engine/indicators/{trixState.ts → state/trixState.ts} +2 -2
  439. package/src/engine/indicators/{vmaState.ts → state/vmaState.ts} +2 -2
  440. package/src/engine/indicators/{volumeProfileState.ts → state/volumeProfileState.ts} +2 -2
  441. package/src/engine/indicators/{vwapState.ts → state/vwapState.ts} +2 -2
  442. package/src/engine/indicators/{wmaState.ts → state/wmaState.ts} +2 -2
  443. package/src/engine/indicators/{wmsrState.ts → state/wmsrState.ts} +2 -2
  444. package/src/engine/indicators/{zonesState.ts → state/zonesState.ts} +2 -2
  445. package/src/engine/indicators/stateComposer.ts +39 -39
  446. package/src/engine/indicators/visibleStateComposers.ts +203 -30
  447. package/src/engine/layout/pane.ts +2 -2
  448. package/src/engine/paneRenderer.ts +2 -6
  449. package/src/engine/render/chartRenderer.ts +34 -4
  450. package/src/engine/renderers/Indicator/__tests__/createSubIndicatorRenderer.test.ts +1 -1
  451. package/src/engine/renderers/Indicator/atr.ts +3 -3
  452. package/src/engine/renderers/Indicator/boll.ts +1 -1
  453. package/src/engine/renderers/Indicator/cci.ts +2 -2
  454. package/src/engine/renderers/Indicator/chaikinVol.ts +2 -2
  455. package/src/engine/renderers/Indicator/cmf.ts +2 -2
  456. package/src/engine/renderers/Indicator/dema.ts +2 -2
  457. package/src/engine/renderers/Indicator/donchian.ts +2 -2
  458. package/src/engine/renderers/Indicator/ene.ts +1 -1
  459. package/src/engine/renderers/Indicator/expma.ts +1 -1
  460. package/src/engine/renderers/Indicator/fastk.ts +2 -2
  461. package/src/engine/renderers/Indicator/fib.ts +2 -2
  462. package/src/engine/renderers/Indicator/hma.ts +2 -2
  463. package/src/engine/renderers/Indicator/hv.ts +3 -3
  464. package/src/engine/renderers/Indicator/ichimoku.ts +111 -85
  465. package/src/engine/renderers/Indicator/kama.ts +3 -3
  466. package/src/engine/renderers/Indicator/keltner.ts +4 -4
  467. package/src/engine/renderers/Indicator/kst.ts +5 -5
  468. package/src/engine/renderers/Indicator/ma.ts +2 -2
  469. package/src/engine/renderers/Indicator/macd.ts +40 -40
  470. package/src/engine/renderers/Indicator/mfi.ts +3 -3
  471. package/src/engine/renderers/Indicator/mom.ts +4 -4
  472. package/src/engine/renderers/Indicator/obv.ts +3 -3
  473. package/src/engine/renderers/Indicator/parkinson.ts +5 -5
  474. package/src/engine/renderers/Indicator/pivot.ts +4 -4
  475. package/src/engine/renderers/Indicator/pvt.ts +3 -3
  476. package/src/engine/renderers/Indicator/roc.ts +3 -3
  477. package/src/engine/renderers/Indicator/rsi.ts +4 -4
  478. package/src/engine/renderers/Indicator/sar.ts +3 -3
  479. package/src/engine/renderers/Indicator/stoch.ts +4 -4
  480. package/src/engine/renderers/Indicator/structure.ts +24 -24
  481. package/src/engine/renderers/Indicator/supertrend.ts +4 -4
  482. package/src/engine/renderers/Indicator/tema.ts +3 -3
  483. package/src/engine/renderers/Indicator/trix.ts +34 -34
  484. package/src/engine/renderers/Indicator/vma.ts +3 -3
  485. package/src/engine/renderers/Indicator/volumeProfile.ts +28 -28
  486. package/src/engine/renderers/Indicator/vwap.ts +3 -3
  487. package/src/engine/renderers/Indicator/wma.ts +3 -3
  488. package/src/engine/renderers/Indicator/wmsr.ts +3 -3
  489. package/src/engine/renderers/Indicator/zones.ts +6 -6
  490. package/src/engine/renderers/__tests__/boll.renderer.test.ts +1 -1
  491. package/src/engine/renderers/__tests__/ene.renderer.test.ts +1 -1
  492. package/src/engine/renderers/__tests__/expma.renderer.test.ts +1 -1
  493. package/src/engine/renderers/__tests__/ma.renderer.test.ts +1 -1
  494. package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +4 -4
  495. package/src/engine/renderers/__tests__/yAxis.renderer.test.ts +30 -26
  496. package/src/engine/renderers/candle.ts +112 -70
  497. package/src/engine/renderers/extremaMarkers.ts +5 -3
  498. package/src/engine/renderers/gridLines.ts +8 -37
  499. package/src/engine/renderers/leftYAxis.ts +61 -22
  500. package/src/engine/renderers/timeAxis.ts +2 -0
  501. package/src/engine/renderers/timeShare.ts +10 -10
  502. package/src/engine/renderers/yAxis.ts +19 -23
  503. package/src/engine/utils/tickPosition.ts +34 -0
  504. package/src/engine/viewport/chartViewportManager.ts +66 -36
  505. package/src/mcp/chartBridge.ts +1 -1
  506. package/src/plugin/types.ts +14 -0
  507. package/src/utils/__tests__/dateFormat.test.ts +164 -0
  508. package/src/utils/dateFormat.ts +12 -6
  509. package/src/utils/kLineDraw/axis.ts +6 -2
  510. package/src/utils/volumePrice.ts +19 -3
  511. package/src/version.ts +1 -1
  512. package/dist/engine/indicators/atrState.d.ts.map +0 -1
  513. package/dist/engine/indicators/atrState.js.map +0 -1
  514. package/dist/engine/indicators/bollState.d.ts.map +0 -1
  515. package/dist/engine/indicators/bollState.js.map +0 -1
  516. package/dist/engine/indicators/cciState.d.ts.map +0 -1
  517. package/dist/engine/indicators/cciState.js.map +0 -1
  518. package/dist/engine/indicators/chaikinVolState.d.ts.map +0 -1
  519. package/dist/engine/indicators/chaikinVolState.js.map +0 -1
  520. package/dist/engine/indicators/cmfState.d.ts.map +0 -1
  521. package/dist/engine/indicators/cmfState.js.map +0 -1
  522. package/dist/engine/indicators/demaState.d.ts.map +0 -1
  523. package/dist/engine/indicators/demaState.js.map +0 -1
  524. package/dist/engine/indicators/donchianState.d.ts.map +0 -1
  525. package/dist/engine/indicators/donchianState.js.map +0 -1
  526. package/dist/engine/indicators/eneState.d.ts.map +0 -1
  527. package/dist/engine/indicators/eneState.js.map +0 -1
  528. package/dist/engine/indicators/expmaState.d.ts.map +0 -1
  529. package/dist/engine/indicators/expmaState.js.map +0 -1
  530. package/dist/engine/indicators/fastkState.d.ts.map +0 -1
  531. package/dist/engine/indicators/fastkState.js.map +0 -1
  532. package/dist/engine/indicators/fibState.d.ts.map +0 -1
  533. package/dist/engine/indicators/fibState.js.map +0 -1
  534. package/dist/engine/indicators/hmaState.d.ts.map +0 -1
  535. package/dist/engine/indicators/hmaState.js.map +0 -1
  536. package/dist/engine/indicators/hvState.d.ts.map +0 -1
  537. package/dist/engine/indicators/hvState.js.map +0 -1
  538. package/dist/engine/indicators/ichimokuState.d.ts.map +0 -1
  539. package/dist/engine/indicators/ichimokuState.js.map +0 -1
  540. package/dist/engine/indicators/kamaState.d.ts.map +0 -1
  541. package/dist/engine/indicators/kamaState.js.map +0 -1
  542. package/dist/engine/indicators/keltnerState.d.ts.map +0 -1
  543. package/dist/engine/indicators/keltnerState.js.map +0 -1
  544. package/dist/engine/indicators/kstState.d.ts.map +0 -1
  545. package/dist/engine/indicators/kstState.js.map +0 -1
  546. package/dist/engine/indicators/maState.d.ts.map +0 -1
  547. package/dist/engine/indicators/maState.js.map +0 -1
  548. package/dist/engine/indicators/macdState.d.ts.map +0 -1
  549. package/dist/engine/indicators/macdState.js.map +0 -1
  550. package/dist/engine/indicators/mfiState.d.ts.map +0 -1
  551. package/dist/engine/indicators/mfiState.js.map +0 -1
  552. package/dist/engine/indicators/momState.d.ts.map +0 -1
  553. package/dist/engine/indicators/momState.js.map +0 -1
  554. package/dist/engine/indicators/obvState.d.ts.map +0 -1
  555. package/dist/engine/indicators/obvState.js.map +0 -1
  556. package/dist/engine/indicators/parkinsonState.d.ts.map +0 -1
  557. package/dist/engine/indicators/parkinsonState.js.map +0 -1
  558. package/dist/engine/indicators/pivotState.d.ts.map +0 -1
  559. package/dist/engine/indicators/pivotState.js.map +0 -1
  560. package/dist/engine/indicators/pvtState.d.ts.map +0 -1
  561. package/dist/engine/indicators/pvtState.js.map +0 -1
  562. package/dist/engine/indicators/rocState.d.ts.map +0 -1
  563. package/dist/engine/indicators/rocState.js.map +0 -1
  564. package/dist/engine/indicators/rsiState.d.ts.map +0 -1
  565. package/dist/engine/indicators/rsiState.js.map +0 -1
  566. package/dist/engine/indicators/sarState.d.ts.map +0 -1
  567. package/dist/engine/indicators/sarState.js.map +0 -1
  568. package/dist/engine/indicators/stochState.d.ts.map +0 -1
  569. package/dist/engine/indicators/stochState.js.map +0 -1
  570. package/dist/engine/indicators/structureState.d.ts.map +0 -1
  571. package/dist/engine/indicators/structureState.js.map +0 -1
  572. package/dist/engine/indicators/supertrendState.d.ts.map +0 -1
  573. package/dist/engine/indicators/supertrendState.js.map +0 -1
  574. package/dist/engine/indicators/temaState.d.ts.map +0 -1
  575. package/dist/engine/indicators/temaState.js.map +0 -1
  576. package/dist/engine/indicators/trixState.d.ts.map +0 -1
  577. package/dist/engine/indicators/trixState.js.map +0 -1
  578. package/dist/engine/indicators/vmaState.d.ts.map +0 -1
  579. package/dist/engine/indicators/vmaState.js.map +0 -1
  580. package/dist/engine/indicators/volumeProfileState.d.ts.map +0 -1
  581. package/dist/engine/indicators/volumeProfileState.js.map +0 -1
  582. package/dist/engine/indicators/vwapState.d.ts.map +0 -1
  583. package/dist/engine/indicators/vwapState.js.map +0 -1
  584. package/dist/engine/indicators/wmaState.d.ts.map +0 -1
  585. package/dist/engine/indicators/wmaState.js.map +0 -1
  586. package/dist/engine/indicators/wmsrState.d.ts.map +0 -1
  587. package/dist/engine/indicators/wmsrState.js.map +0 -1
  588. package/dist/engine/indicators/zonesState.d.ts.map +0 -1
  589. package/dist/engine/indicators/zonesState.js.map +0 -1
  590. /package/dist/engine/indicators/{macdState.js → state/macdState.js} +0 -0
@@ -1,2462 +1,3 @@
1
- import type { KLineData } from '../../types/price'
2
-
3
- /**
4
- * MA 周期配置标志
5
- */
6
- export type MAFlags = {
7
- ma5?: boolean
8
- ma10?: boolean
9
- ma20?: boolean
10
- ma30?: boolean
11
- ma60?: boolean
12
- }
13
-
14
- /**
15
- * 默认 MA 周期列表
16
- */
17
- export const DEFAULT_MA_PERIODS = [5, 10, 20, 30, 60] as const
18
-
19
- // ============================================================================
20
- // BOLL 布林带
21
- // ============================================================================
22
-
23
- /**
24
- * BOLL 数据点
25
- */
26
- export interface BOLLPoint {
27
- upper: number
28
- middle: number
29
- lower: number
30
- }
31
-
32
- /**
33
- * 默认 BOLL 参数
34
- */
35
- export const DEFAULT_BOLL_PERIOD = 20
36
- export const DEFAULT_BOLL_MULTIPLIER = 2
37
-
38
- /**
39
- * 计算 BOLL 数据(使用滑动窗口优化)
40
- * @param data K线数据数组
41
- * @param period 周期(默认20)
42
- * @param multiplier 标准差倍数(默认2)
43
- * @returns 每个索引对应的BOLL值,前 period-1 个为 undefined
44
- */
45
- export function calcBOLLData(
46
- data: KLineData[],
47
- period: number,
48
- multiplier: number
49
- ): BOLLPoint[] {
50
- const result: BOLLPoint[] = new Array(data.length)
51
-
52
- if (data.length < period) return result
53
-
54
- // 使用滑动窗口计算,避免重复求和
55
- let sum = 0
56
- const window: number[] = []
57
-
58
- // 初始化第一个窗口
59
- for (let i = 0; i < period; i++) {
60
- const item = data[i]
61
- if (!item) return result
62
- const close = item.close
63
- window.push(close)
64
- sum += close
65
- }
66
-
67
- // 计算每个点的 BOLL
68
- for (let i = period - 1; i < data.length; i++) {
69
- const item = data[i]
70
- if (!item) continue
71
-
72
- // 更新窗口求和
73
- if (i >= period) {
74
- const oldVal = window.shift()
75
- if (oldVal !== undefined) sum -= oldVal
76
- const close = item.close
77
- window.push(close)
78
- sum += close
79
- }
80
-
81
- const ma = sum / period
82
-
83
- // 计算标准差
84
- let variance = 0
85
- for (let j = 0; j < period; j++) {
86
- const wVal = window[j]
87
- if (wVal !== undefined) {
88
- variance += Math.pow(wVal - ma, 2)
89
- }
90
- }
91
- const stdDev = Math.sqrt(variance / period)
92
-
93
- result[i] = {
94
- upper: ma + multiplier * stdDev,
95
- middle: ma,
96
- lower: ma - multiplier * stdDev,
97
- }
98
- }
99
-
100
- return result
101
- }
102
-
103
- // ============================================================================
104
- // EXPMA 指数平滑移动平均线
105
- // ============================================================================
106
-
107
- /**
108
- * EXPMA 数据点
109
- */
110
- export interface EXPMAPoint {
111
- fast: number
112
- slow: number
113
- }
114
-
115
- /**
116
- * 默认 EXPMA 参数
117
- */
118
- export const DEFAULT_EXPMA_FAST_PERIOD = 12
119
- export const DEFAULT_EXPMA_SLOW_PERIOD = 50
120
-
121
- /**
122
- * 计算 EXPMA 数据
123
- * 公式:EXPMA(i) = C(i) × K + EXPMA(i-1) × (1-K),K = 2/(N+1)
124
- * @param data K线数据数组
125
- * @param fastPeriod 快线周期(默认12)
126
- * @param slowPeriod 慢线周期(默认50)
127
- * @returns 每个索引对应的EXPMA值(从 index 0 开始有值)
128
- */
129
- export function calcEXPMAData(
130
- data: KLineData[],
131
- fastPeriod: number,
132
- slowPeriod: number
133
- ): EXPMAPoint[] {
134
- const result: EXPMAPoint[] = new Array(data.length)
135
-
136
- if (data.length === 0) return result
137
-
138
- const fastK = 2 / (fastPeriod + 1)
139
- const slowK = 2 / (slowPeriod + 1)
140
-
141
- // 第一个点的 EXPMA 等于第一天的收盘价
142
- const firstClose = data[0]!.close
143
- let fastEMA = firstClose
144
- let slowEMA = firstClose
145
-
146
- result[0] = { fast: fastEMA, slow: slowEMA }
147
-
148
- for (let i = 1; i < data.length; i++) {
149
- const close = data[i]!.close
150
- fastEMA = close * fastK + fastEMA * (1 - fastK)
151
- slowEMA = close * slowK + slowEMA * (1 - slowK)
152
- result[i] = { fast: fastEMA, slow: slowEMA }
153
- }
154
-
155
- return result
156
- }
157
-
158
- // ============================================================================
159
- // ENE 轨道线
160
- // ============================================================================
161
-
162
- /**
163
- * ENE 数据点
164
- */
165
- export interface ENEPoint {
166
- upper: number
167
- middle: number
168
- lower: number
169
- }
170
-
171
- /**
172
- * 默认 ENE 参数
173
- */
174
- export const DEFAULT_ENE_PERIOD = 10
175
- export const DEFAULT_ENE_DEVIATION = 11
176
-
177
- /**
178
- * 计算 ENE 数据
179
- * 中轨 = MA(close, N)
180
- * 上轨 = 中轨 × (1 + M/100)
181
- * 下轨 = 中轨 × (1 - M/100)
182
- * @param data K线数据数组
183
- * @param period 周期(默认10)
184
- * @param deviation 偏离率百分比(默认11)
185
- * @returns 每个索引对应的ENE值,前 period-1 个为 undefined
186
- */
187
- export function calcENEData(
188
- data: KLineData[],
189
- period: number,
190
- deviation: number
191
- ): ENEPoint[] {
192
- const result: ENEPoint[] = new Array(data.length)
193
-
194
- if (data.length < period) return result
195
-
196
- // 使用滑动窗口计算 MA
197
- let sum = 0
198
-
199
- // 初始化第一个窗口
200
- for (let i = 0; i < period; i++) {
201
- const item = data[i]
202
- if (!item) return result
203
- sum += item.close
204
- }
205
-
206
- // 第一个有效点
207
- const firstMA = sum / period
208
- const firstDeviation = deviation / 100
209
- result[period - 1] = {
210
- upper: firstMA * (1 + firstDeviation),
211
- middle: firstMA,
212
- lower: firstMA * (1 - firstDeviation),
213
- }
214
-
215
- // 滑动计算后续点
216
- for (let i = period; i < data.length; i++) {
217
- const prevItem = data[i - period]
218
- const currItem = data[i]
219
- if (!prevItem || !currItem) continue
220
-
221
- sum = sum - prevItem.close + currItem.close
222
- const ma = sum / period
223
- const dev = deviation / 100
224
-
225
- result[i] = {
226
- upper: ma * (1 + dev),
227
- middle: ma,
228
- lower: ma * (1 - dev),
229
- }
230
- }
231
-
232
- return result
233
- }
234
-
235
- /**
236
- * 计算指定周期的 MA 数据(使用滑动窗口优化,O(n) 复杂度)
237
- * @param data K线数据数组
238
- * @param period MA周期
239
- * @returns 每个索引对应的MA值,前 period-1 个为 undefined
240
- */
241
- export function calcMAData(data: KLineData[], period: number): (number | undefined)[] {
242
- const result: (number | undefined)[] = new Array(data.length)
243
-
244
- if (data.length < period) return result
245
-
246
- // 滑动窗口求和
247
- let sum = 0
248
-
249
- // 初始化第一个窗口
250
- for (let i = 0; i < period; i++) {
251
- const item = data[i]
252
- if (!item) return result
253
- sum += item.close
254
- }
255
-
256
- // 第一个有效点
257
- result[period - 1] = sum / period
258
-
259
- // 滑动计算后续点
260
- for (let i = period; i < data.length; i++) {
261
- const prevItem = data[i - period]
262
- const currItem = data[i]
263
- if (!prevItem || !currItem) continue
264
-
265
- sum = sum - prevItem.close + currItem.close
266
- result[i] = sum / period
267
- }
268
-
269
- return result
270
- }
271
-
272
- // ============================================================================
273
- // RSI 相对强弱指标
274
- // ============================================================================
275
-
276
- /**
277
- * 默认 RSI 参数
278
- */
279
- export const DEFAULT_RSI_PERIOD1 = 6
280
- export const DEFAULT_RSI_PERIOD2 = 12
281
- export const DEFAULT_RSI_PERIOD3 = 24
282
- const DEFAULT_RSI_PERIODS = [6, 12, 24] as const
283
-
284
- /**
285
- * 计算 RSI 数据
286
- * RSI = 100 - 100 / (1 + RS)
287
- * RS = 平均上涨幅度 / 平均下跌幅度
288
- * @param data K线数据数组
289
- * @param period RSI周期
290
- * @returns 每个索引对应的RSI值,前 period+1 个为 undefined(需要 period+1 个数据点计算初始平均)
291
- */
292
- export function calcRSIData(data: KLineData[], period: number): (number | undefined)[] {
293
- const result: (number | undefined)[] = new Array(data.length)
294
-
295
- if (data.length < period + 1) return result
296
-
297
- // 计算价格变化
298
- const changes: number[] = []
299
- for (let i = 1; i < data.length; i++) {
300
- changes.push(data[i]!.close - data[i - 1]!.close)
301
- }
302
-
303
- // 初始化:计算前 period 天的平均涨跌
304
- let sumGain = 0
305
- let sumLoss = 0
306
-
307
- for (let i = 0; i < period; i++) {
308
- const change = changes[i]
309
- if (change !== undefined) {
310
- if (change > 0) sumGain += change
311
- else sumLoss += Math.abs(change)
312
- }
313
- }
314
-
315
- // 第一个 RSI 值
316
- let avgGain = sumGain / period
317
- let avgLoss = sumLoss / period
318
-
319
- if (avgLoss === 0) {
320
- result[period] = 100
321
- } else {
322
- const rs = avgGain / avgLoss
323
- result[period] = 100 - 100 / (1 + rs)
324
- }
325
-
326
- // 后续使用平滑计算(Wilder's smoothing)
327
- for (let i = period; i < changes.length; i++) {
328
- const change = changes[i]
329
- if (change === undefined) continue
330
-
331
- if (change > 0) {
332
- avgGain = (avgGain * (period - 1) + change) / period
333
- avgLoss = (avgLoss * (period - 1)) / period
334
- } else {
335
- avgGain = (avgGain * (period - 1)) / period
336
- avgLoss = (avgLoss * (period - 1) + Math.abs(change)) / period
337
- }
338
-
339
- if (avgLoss === 0) {
340
- result[i + 1] = 100
341
- } else {
342
- const rs = avgGain / avgLoss
343
- result[i + 1] = 100 - 100 / (1 + rs)
344
- }
345
- }
346
-
347
- return result
348
- }
349
-
350
- // ============================================================================
351
- // CCI 顺势指标
352
- // ============================================================================
353
-
354
- const DEFAULT_CCI_PERIOD = 14
355
-
356
- export function calcCCIData(data: KLineData[], period: number): (number | undefined)[] {
357
- const result: (number | undefined)[] = new Array(data.length)
358
-
359
- if (data.length < period) return result
360
-
361
- // 计算 TP (Typical Price) = (H + L + C) / 3
362
- const tpValues: number[] = []
363
- for (const item of data) {
364
- tpValues.push((item.high + item.low + item.close) / 3)
365
- }
366
-
367
- // 计算 TP 的 SMA
368
- let sum = 0
369
- for (let i = 0; i < period; i++) {
370
- sum += tpValues[i]!
371
- }
372
-
373
- for (let i = period - 1; i < data.length; i++) {
374
- if (i >= period) {
375
- sum = sum - tpValues[i - period]! + tpValues[i]!
376
- }
377
- const sma = sum / period
378
-
379
- // 计算平均绝对偏差
380
- let meanDeviation = 0
381
- for (let j = 0; j < period; j++) {
382
- meanDeviation += Math.abs(tpValues[i - j]! - sma)
383
- }
384
- meanDeviation /= period
385
-
386
- if (meanDeviation === 0) {
387
- result[i] = 0
388
- } else {
389
- result[i] = (tpValues[i]! - sma) / (0.015 * meanDeviation)
390
- }
391
- }
392
-
393
- return result
394
- }
395
-
396
- // ============================================================================
397
- // STOCH 随机指标
398
- // ============================================================================
399
-
400
- const DEFAULT_STOCH_N = 9
401
- const DEFAULT_STOCH_M = 3
402
-
403
- export interface STOCHPoint {
404
- k: number
405
- d: number
406
- }
407
-
408
- export function calcSTOCHData(data: KLineData[], n: number, m: number): STOCHPoint[] {
409
- const result: STOCHPoint[] = new Array(data.length)
410
-
411
- if (data.length < n) return result
412
-
413
- // 计算 RSV 和 K
414
- const kValues: (number | undefined)[] = new Array(data.length)
415
-
416
- for (let i = n - 1; i < data.length; i++) {
417
- let highest = -Infinity
418
- let lowest = Infinity
419
-
420
- for (let j = 0; j < n; j++) {
421
- const item = data[i - j]
422
- if (!item) continue
423
- highest = Math.max(highest, item.high)
424
- lowest = Math.min(lowest, item.low)
425
- }
426
-
427
- const close = data[i]!.close
428
- if (highest === lowest) {
429
- kValues[i] = 50
430
- } else {
431
- kValues[i] = ((close - lowest) / (highest - lowest)) * 100
432
- }
433
- }
434
-
435
- // 计算 D (K 的 M 日移动平均)
436
- for (let i = n - 1 + m - 1; i < data.length; i++) {
437
- const k = kValues[i]
438
- if (k === undefined) continue
439
-
440
- let sum = 0
441
- let validCount = 0
442
- for (let j = 0; j < m; j++) {
443
- const kv = kValues[i - j]
444
- if (kv !== undefined) {
445
- sum += kv
446
- validCount++
447
- }
448
- }
449
-
450
- if (validCount === m) {
451
- result[i] = { k, d: sum / m }
452
- }
453
- }
454
-
455
- return result
456
- }
457
-
458
- // ============================================================================
459
- // MOM 动量指标
460
- // ============================================================================
461
-
462
- const DEFAULT_MOM_PERIOD = 10
463
-
464
- export function calcMOMData(data: KLineData[], period: number): (number | undefined)[] {
465
- const result: (number | undefined)[] = new Array(data.length)
466
-
467
- if (data.length < period + 1) return result
468
-
469
- for (let i = period; i < data.length; i++) {
470
- const currentClose = data[i]?.close
471
- const prevClose = data[i - period]?.close
472
-
473
- if (currentClose !== undefined && prevClose !== undefined) {
474
- result[i] = currentClose - prevClose
475
- }
476
- }
477
-
478
- return result
479
- }
480
-
481
- // ============================================================================
482
- // WMSR 威廉指标
483
- // ============================================================================
484
-
485
- const DEFAULT_WMSR_PERIOD = 14
486
-
487
- export function calcWMSRData(data: KLineData[], period: number): (number | undefined)[] {
488
- const result: (number | undefined)[] = new Array(data.length)
489
-
490
- if (data.length < period) return result
491
-
492
- for (let i = period - 1; i < data.length; i++) {
493
- let highest = -Infinity
494
- let lowest = Infinity
495
-
496
- for (let j = 0; j < period; j++) {
497
- const item = data[i - j]
498
- if (!item) continue
499
- highest = Math.max(highest, item.high)
500
- lowest = Math.min(lowest, item.low)
501
- }
502
-
503
- const close = data[i]!.close
504
- if (highest === lowest) {
505
- result[i] = -50
506
- } else {
507
- result[i] = ((highest - close) / (highest - lowest)) * -100
508
- }
509
- }
510
-
511
- return result
512
- }
513
-
514
- // ============================================================================
515
- // KST 确知指标
516
- // ============================================================================
517
-
518
- const DEFAULT_KST_ROC1 = 10
519
- const DEFAULT_KST_ROC2 = 15
520
- const DEFAULT_KST_ROC3 = 20
521
- const DEFAULT_KST_ROC4 = 30
522
- const DEFAULT_KST_SIGNAL = 9
523
-
524
- export interface KSTPoint {
525
- kst: number
526
- signal: number
527
- }
528
-
529
- function calcROCInternal(data: KLineData[], period: number): (number | undefined)[] {
530
- const result: (number | undefined)[] = new Array(data.length)
531
-
532
- if (data.length < period + 1) return result
533
-
534
- for (let i = period; i < data.length; i++) {
535
- const currentClose = data[i]?.close
536
- const prevClose = data[i - period]?.close
537
-
538
- if (currentClose !== undefined && prevClose !== undefined && prevClose !== 0) {
539
- result[i] = ((currentClose - prevClose) / prevClose) * 100
540
- }
541
- }
542
-
543
- return result
544
- }
545
-
546
- function calcSMAInternal(data: (number | undefined)[], period: number): (number | undefined)[] {
547
- const result: (number | undefined)[] = new Array(data.length)
548
-
549
- let sum = 0
550
- let count = 0
551
-
552
- for (let i = 0; i < data.length; i++) {
553
- const val = data[i]
554
-
555
- if (val !== undefined) {
556
- sum += val
557
- count++
558
-
559
- if (count > period) {
560
- const oldVal = data[i - period]
561
- if (oldVal !== undefined) {
562
- sum -= oldVal
563
- count--
564
- }
565
- }
566
-
567
- if (count === period) {
568
- result[i] = sum / period
569
- }
570
- }
571
- }
572
-
573
- return result
574
- }
575
-
576
- export function calcKSTData(
577
- data: KLineData[],
578
- roc1: number,
579
- roc2: number,
580
- roc3: number,
581
- roc4: number,
582
- signalPeriod: number
583
- ): KSTPoint[] {
584
- const result: KSTPoint[] = new Array(data.length)
585
-
586
- const roc1Data = calcROCInternal(data, roc1)
587
- const roc2Data = calcROCInternal(data, roc2)
588
- const roc3Data = calcROCInternal(data, roc3)
589
- const roc4Data = calcROCInternal(data, roc4)
590
-
591
- const sma1 = calcSMAInternal(roc1Data, 10)
592
- const sma2 = calcSMAInternal(roc2Data, 10)
593
- const sma3 = calcSMAInternal(roc3Data, 10)
594
- const sma4 = calcSMAInternal(roc4Data, 15)
595
-
596
- const kstValues: (number | undefined)[] = new Array(data.length)
597
-
598
- for (let i = 0; i < data.length; i++) {
599
- const v1 = sma1[i]
600
- const v2 = sma2[i]
601
- const v3 = sma3[i]
602
- const v4 = sma4[i]
603
-
604
- if (v1 !== undefined && v2 !== undefined && v3 !== undefined && v4 !== undefined) {
605
- kstValues[i] = v1 * 1 + v2 * 2 + v3 * 3 + v4 * 4
606
- }
607
- }
608
-
609
- const signalData = calcSMAInternal(kstValues, signalPeriod)
610
-
611
- for (let i = 0; i < data.length; i++) {
612
- const kst = kstValues[i]
613
- const signal = signalData[i]
614
-
615
- if (kst !== undefined && signal !== undefined) {
616
- result[i] = { kst, signal }
617
- }
618
- }
619
-
620
- return result
621
- }
622
-
623
- // ============================================================================
624
- // FASTK 快速随机指标
625
- // ============================================================================
626
-
627
- const DEFAULT_FASTK_PERIOD = 9
628
-
629
- export function calcFASTKData(data: KLineData[], period: number): (number | undefined)[] {
630
- const result: (number | undefined)[] = new Array(data.length)
631
-
632
- if (data.length < period) return result
633
-
634
- for (let i = period - 1; i < data.length; i++) {
635
- let highest = -Infinity
636
- let lowest = Infinity
637
-
638
- for (let j = 0; j < period; j++) {
639
- const item = data[i - j]
640
- if (!item) continue
641
- highest = Math.max(highest, item.high)
642
- lowest = Math.min(lowest, item.low)
643
- }
644
-
645
- const close = data[i]!.close
646
- if (highest === lowest) {
647
- result[i] = 50
648
- } else {
649
- result[i] = ((close - lowest) / (highest - lowest)) * 100
650
- }
651
- }
652
-
653
- return result
654
- }
655
-
656
- // ============================================================================
657
- // MACD 指数平滑异同移动平均线
658
- // ============================================================================
659
-
660
- /**
661
- * MACD 数据点
662
- */
663
- export interface MACDPoint {
664
- /** DIF 线值 */
665
- dif: number
666
- /** DEA 线值 */
667
- dea: number
668
- /** MACD 柱状图值 */
669
- macd: number
670
- }
671
-
672
- /**
673
- * 默认 MACD 参数
674
- */
675
- const DEFAULT_MACD_FAST_PERIOD = 12
676
- const DEFAULT_MACD_SLOW_PERIOD = 26
677
- const DEFAULT_MACD_SIGNAL_PERIOD = 9
678
-
679
- /**
680
- * 计算 EMA(指数移动平均)值
681
- * EMA(today) = close × K + EMA(yesterday) × (1 - K)
682
- * K = 2 / (period + 1)
683
- * @param data K线数据数组
684
- * @param period 周期
685
- * @returns EMA 值数组,第一个值使用第一个收盘价
686
- */
687
- function calcEMA(data: KLineData[], period: number): number[] {
688
- const result: number[] = new Array(data.length)
689
- const k = 2 / (period + 1)
690
-
691
- if (data.length === 0) return result
692
-
693
- // 第一个 EMA 值使用第一个收盘价
694
- result[0] = data[0]!.close
695
-
696
- for (let i = 1; i < data.length; i++) {
697
- const item = data[i]
698
- if (!item) continue
699
- result[i] = item.close * k + result[i - 1]! * (1 - k)
700
- }
701
-
702
- return result
703
- }
704
-
705
- /**
706
- * 基于数值数组计算 EMA
707
- * @param values 数值数组(可能包含 undefined)
708
- * @param period 周期
709
- * @returns EMA 值数组
710
- */
711
- function calcEMAFromArray(values: (number | undefined)[], period: number): (number | undefined)[] {
712
- const result: (number | undefined)[] = new Array(values.length)
713
- const k = 2 / (period + 1)
714
-
715
- const firstValid = values.findIndex(v => v !== undefined)
716
- if (firstValid === -1) return result
717
-
718
- result[firstValid] = values[firstValid]
719
-
720
- for (let i = firstValid + 1; i < values.length; i++) {
721
- const val = values[i]
722
- const prev = result[i - 1]
723
- if (val === undefined || prev === undefined) continue
724
- result[i] = val * k + prev * (1 - k)
725
- }
726
-
727
- return result
728
- }
729
-
730
- /**
731
- * 计算 MACD 数据
732
- * DIF = EMA(close, fastPeriod) - EMA(close, slowPeriod)
733
- * DEA = EMA(DIF, signalPeriod)
734
- * MACD = (DIF - DEA) × 2
735
- * @param data K线数据数组
736
- * @param fastPeriod 快线周期(默认12)
737
- * @param slowPeriod 慢线周期(默认26)
738
- * @param signalPeriod 信号线周期(默认9)
739
- * @returns MACD 数据点数组,前 slowPeriod-1 个可能为 undefined
740
- */
741
- export function calcMACDData(
742
- data: KLineData[],
743
- fastPeriod: number,
744
- slowPeriod: number,
745
- signalPeriod: number
746
- ): MACDPoint[] {
747
- const result: MACDPoint[] = new Array(data.length)
748
-
749
- if (data.length < slowPeriod) return result
750
-
751
- // 计算 EMA12 和 EMA26
752
- const emaFast = calcEMA(data, fastPeriod)
753
- const emaSlow = calcEMA(data, slowPeriod)
754
-
755
- // 计算 DIF
756
- const dif: (number | undefined)[] = new Array(data.length)
757
- for (let i = 0; i < data.length; i++) {
758
- const fast = emaFast[i]
759
- const slow = emaSlow[i]
760
- if (fast !== undefined && slow !== undefined) {
761
- dif[i] = fast - slow
762
- }
763
- }
764
-
765
- // 计算 DEA(DIF 的 signalPeriod 日 EMA)
766
- const dea = calcEMAFromArray(dif, signalPeriod)
767
-
768
- // 计算 MACD 柱
769
- for (let i = 0; i < data.length; i++) {
770
- const d = dif[i]
771
- const e = dea[i]
772
- if (d !== undefined && e !== undefined) {
773
- result[i] = {
774
- dif: d,
775
- dea: e,
776
- macd: (d - e) * 2,
777
- }
778
- }
779
- }
780
-
781
- return result
782
- }
783
-
784
- // ============================================================================
785
- // SoA (Structure of Arrays) 包装函数
786
- // 用于验证 SoA 数据层与原始 AoS 计算的一致性
787
- // ============================================================================
788
-
789
- import type { KLineSoALayout } from './soa'
790
- import { SharedKLineBuffer } from './soa'
791
-
792
- /**
793
- * 从 SoA 布局计算 BOLL 数据(验证用包装函数)
794
- * @param layout SoA 布局
795
- * @param period 周期
796
- * @param multiplier 标准差倍数
797
- * @returns BOLL 数据点数组
798
- */
799
- export function calcBOLLDataSoA(
800
- layout: KLineSoALayout,
801
- period: number,
802
- multiplier: number
803
- ): BOLLPoint[] {
804
- const data = SharedKLineBuffer.toKLineData(layout)
805
- return calcBOLLData(data, period, multiplier)
806
- }
807
-
808
- /**
809
- * 从 SoA 布局计算 EXPMA 数据(验证用包装函数)
810
- * @param layout SoA 布局
811
- * @param fastPeriod 快线周期
812
- * @param slowPeriod 慢线周期
813
- * @returns EXPMA 数据点数组
814
- */
815
- export function calcEXPMADataSoA(
816
- layout: KLineSoALayout,
817
- fastPeriod: number,
818
- slowPeriod: number
819
- ): EXPMAPoint[] {
820
- const data = SharedKLineBuffer.toKLineData(layout)
821
- return calcEXPMAData(data, fastPeriod, slowPeriod)
822
- }
823
-
824
- /**
825
- * 从 SoA 布局计算 ENE 数据(验证用包装函数)
826
- * @param layout SoA 布局
827
- * @param period 周期
828
- * @param deviation 偏离率百分比
829
- * @returns ENE 数据点数组
830
- */
831
- export function calcENEDataSoA(
832
- layout: KLineSoALayout,
833
- period: number,
834
- deviation: number
835
- ): ENEPoint[] {
836
- const data = SharedKLineBuffer.toKLineData(layout)
837
- return calcENEData(data, period, deviation)
838
- }
839
-
840
- /**
841
- * 从 SoA 布局计算 MA 数据(验证用包装函数)
842
- * @param layout SoA 布局
843
- * @param period MA周期
844
- * @returns MA 值数组
845
- */
846
- export function calcMADataSoA(
847
- layout: KLineSoALayout,
848
- period: number
849
- ): (number | undefined)[] {
850
- const data = SharedKLineBuffer.toKLineData(layout)
851
- return calcMAData(data, period)
852
- }
853
-
854
- /**
855
- * 从 SoA 布局计算 RSI 数据(验证用包装函数)
856
- * @param layout SoA 布局
857
- * @param period RSI周期
858
- * @returns RSI 值数组
859
- */
860
- export function calcRSIDataSoA(
861
- layout: KLineSoALayout,
862
- period: number
863
- ): (number | undefined)[] {
864
- const data = SharedKLineBuffer.toKLineData(layout)
865
- return calcRSIData(data, period)
866
- }
867
-
868
- /**
869
- * 从 SoA 布局计算 CCI 数据(验证用包装函数)
870
- * @param layout SoA 布局
871
- * @param period 周期
872
- * @returns CCI 值数组
873
- */
874
- export function calcCCIDataSoA(
875
- layout: KLineSoALayout,
876
- period: number
877
- ): (number | undefined)[] {
878
- const data = SharedKLineBuffer.toKLineData(layout)
879
- return calcCCIData(data, period)
880
- }
881
-
882
- /**
883
- * 从 SoA 布局计算 STOCH 数据(验证用包装函数)
884
- * @param layout SoA 布局
885
- * @param n RSV周期
886
- * @param m K的M日移动平均周期
887
- * @returns STOCH 数据点数组
888
- */
889
- export function calcSTOCHDataSoA(
890
- layout: KLineSoALayout,
891
- n: number,
892
- m: number
893
- ): STOCHPoint[] {
894
- const data = SharedKLineBuffer.toKLineData(layout)
895
- return calcSTOCHData(data, n, m)
896
- }
897
-
898
- /**
899
- * 从 SoA 布局计算 MOM 数据(验证用包装函数)
900
- * @param layout SoA 布局
901
- * @param period 周期
902
- * @returns MOM 值数组
903
- */
904
- export function calcMOMDataSoA(
905
- layout: KLineSoALayout,
906
- period: number
907
- ): (number | undefined)[] {
908
- const data = SharedKLineBuffer.toKLineData(layout)
909
- return calcMOMData(data, period)
910
- }
911
-
912
- /**
913
- * 从 SoA 布局计算 WMSR 数据(验证用包装函数)
914
- * @param layout SoA 布局
915
- * @param period 周期
916
- * @returns WMSR 值数组
917
- */
918
- export function calcWMSRDataSoA(
919
- layout: KLineSoALayout,
920
- period: number
921
- ): (number | undefined)[] {
922
- const data = SharedKLineBuffer.toKLineData(layout)
923
- return calcWMSRData(data, period)
924
- }
925
-
926
- /**
927
- * 从 SoA 布局计算 KST 数据(验证用包装函数)
928
- * @param layout SoA 布局
929
- * @param roc1 第一个ROC周期
930
- * @param roc2 第二个ROC周期
931
- * @param roc3 第三个ROC周期
932
- * @param roc4 第四个ROC周期
933
- * @param signalPeriod 信号线周期
934
- * @returns KST 数据点数组
935
- */
936
- export function calcKSTDataSoA(
937
- layout: KLineSoALayout,
938
- roc1: number,
939
- roc2: number,
940
- roc3: number,
941
- roc4: number,
942
- signalPeriod: number
943
- ): KSTPoint[] {
944
- const data = SharedKLineBuffer.toKLineData(layout)
945
- return calcKSTData(data, roc1, roc2, roc3, roc4, signalPeriod)
946
- }
947
-
948
- /**
949
- * 从 SoA 布局计算 FASTK 数据(验证用包装函数)
950
- * @param layout SoA 布局
951
- * @param period 周期
952
- * @returns FASTK 值数组
953
- */
954
- export function calcFASTKDataSoA(
955
- layout: KLineSoALayout,
956
- period: number
957
- ): (number | undefined)[] {
958
- const data = SharedKLineBuffer.toKLineData(layout)
959
- return calcFASTKData(data, period)
960
- }
961
-
962
- /**
963
- * 从 SoA 布局计算 MACD 数据(验证用包装函数)
964
- * @param layout SoA 布局
965
- * @param fastPeriod 快线周期
966
- * @param slowPeriod 慢线周期
967
- * @param signalPeriod 信号线周期
968
- * @returns MACD 数据点数组
969
- */
970
- export function calcMACDDataSoA(
971
- layout: KLineSoALayout,
972
- fastPeriod: number,
973
- slowPeriod: number,
974
- signalPeriod: number
975
- ): MACDPoint[] {
976
- const data = SharedKLineBuffer.toKLineData(layout)
977
- return calcMACDData(data, fastPeriod, slowPeriod, signalPeriod)
978
- }
979
-
980
- // ============================================================================
981
- // ATR — Wilder's Average True Range
982
- // ============================================================================
983
-
984
- const DEFAULT_ATR_PERIOD = 14
985
-
986
- /**
987
- * 计算 Wilder ATR。
988
- * TR(0) = H(0) - L(0)
989
- * TR(t) = max(H(t) - L(t), |H(t) - C(t-1)|, |L(t) - C(t-1)|)
990
- * ATR(period-1) = mean(TR[0..period-1])
991
- * ATR(t) = ((period-1) * ATR(t-1) + TR(t)) / period for t >= period
992
- *
993
- * @param data K 线数组
994
- * @param period 周期,需 >= 1;若 <= 0 或 data.length < period,返回全 undefined
995
- */
996
- export function calcATRData(data: KLineData[], period: number): (number | undefined)[] {
997
- const n = data.length
998
- const result: (number | undefined)[] = new Array(n).fill(undefined)
999
- if (n === 0 || period <= 0) return result
1000
-
1001
- if (period === 1) {
1002
- const first = data[0]!
1003
- result[0] = first.high - first.low
1004
- let prevClose = first.close
1005
- for (let i = 1; i < n; i++) {
1006
- const cur = data[i]!
1007
- const tr = Math.max(
1008
- cur.high - cur.low,
1009
- Math.abs(cur.high - prevClose),
1010
- Math.abs(cur.low - prevClose),
1011
- )
1012
- result[i] = tr
1013
- prevClose = cur.close
1014
- }
1015
- return result
1016
- }
1017
-
1018
- if (n < period) return result
1019
-
1020
- const first = data[0]!
1021
- let sumTR = first.high - first.low
1022
- let prevClose = first.close
1023
-
1024
- for (let i = 1; i < period; i++) {
1025
- const cur = data[i]!
1026
- sumTR += Math.max(
1027
- cur.high - cur.low,
1028
- Math.abs(cur.high - prevClose),
1029
- Math.abs(cur.low - prevClose),
1030
- )
1031
- prevClose = cur.close
1032
- }
1033
-
1034
- let atr = sumTR / period
1035
- result[period - 1] = atr
1036
-
1037
- const periodMinusOne = period - 1
1038
- for (let i = period; i < n; i++) {
1039
- const cur = data[i]!
1040
- const tr = Math.max(
1041
- cur.high - cur.low,
1042
- Math.abs(cur.high - prevClose),
1043
- Math.abs(cur.low - prevClose),
1044
- )
1045
- atr = (periodMinusOne * atr + tr) / period
1046
- result[i] = atr
1047
- prevClose = cur.close
1048
- }
1049
-
1050
- return result
1051
- }
1052
-
1053
-
1054
- // ============================================================================
1055
- // WMA — Weighted Moving Average (linear weights)
1056
- // 权重: w_i = i (i=1..period),分母 = period*(period+1)/2
1057
- // 滞后 = (period-1)/3 (相比 SMA 更快响应)
1058
- // ============================================================================
1059
-
1060
- const DEFAULT_WMA_PERIOD = 9
1061
-
1062
- function _computeWMAOnNumbers(values: (number | undefined)[], period: number): (number | undefined)[] {
1063
- const n = values.length
1064
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1065
- if (n === 0 || period <= 0 || n < period) return result
1066
-
1067
- const denom = (period * (period + 1)) / 2
1068
-
1069
- for (let t = period - 1; t < n; t++) {
1070
- let sw = 0
1071
- let valid = true
1072
- for (let k = 0; k < period; k++) {
1073
- const v = values[t - period + 1 + k]
1074
- if (v === undefined) {
1075
- valid = false
1076
- break
1077
- }
1078
- sw += (k + 1) * v
1079
- }
1080
- if (valid) result[t] = sw / denom
1081
- }
1082
- return result
1083
- }
1084
-
1085
- export function calcWMAData(data: KLineData[], period: number): (number | undefined)[] {
1086
- if (data.length === 0 || period <= 0) {
1087
- return new Array(data.length).fill(undefined)
1088
- }
1089
- const closes = new Array<number | undefined>(data.length)
1090
- for (let i = 0; i < data.length; i++) closes[i] = data[i]!.close
1091
- return _computeWMAOnNumbers(closes, period)
1092
- }
1093
-
1094
-
1095
-
1096
- // ============================================================================
1097
- // EMA helper(DEMA / TEMA 复用,沿用 EXPMA 的 first-close seed 习惯)
1098
- // alpha = 2 / (period + 1)
1099
- // ============================================================================
1100
-
1101
- function _computeEMASeries(values: (number | undefined)[], period: number): (number | undefined)[] {
1102
- const n = values.length
1103
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1104
- if (n === 0 || period <= 0) return result
1105
-
1106
- const alpha = 2 / (period + 1)
1107
-
1108
- let i = 0
1109
- while (i < n && values[i] === undefined) i++
1110
- if (i >= n) return result
1111
-
1112
- let ema = values[i]!
1113
- result[i] = ema
1114
- for (let t = i + 1; t < n; t++) {
1115
- const v = values[t]
1116
- if (v === undefined) continue
1117
- ema = v * alpha + ema * (1 - alpha)
1118
- result[t] = ema
1119
- }
1120
- return result
1121
- }
1122
-
1123
- // ============================================================================
1124
- // DEMA — Double Exponential Moving Average
1125
- // 公式: DEMA(t) = 2*EMA(t) - EMA(EMA)(t)
1126
- // 性质: 对线性输入零滞后(稳态),warmup ~ 2*(period-1)
1127
- // ============================================================================
1128
-
1129
- const DEFAULT_DEMA_PERIOD = 20
1130
-
1131
- export function calcDEMAData(data: KLineData[], period: number): (number | undefined)[] {
1132
- const n = data.length
1133
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1134
- if (n === 0 || period <= 0) return result
1135
-
1136
- const closes = new Array<number | undefined>(n)
1137
- for (let i = 0; i < n; i++) closes[i] = data[i]!.close
1138
-
1139
- const ema1 = _computeEMASeries(closes, period)
1140
- const ema2 = _computeEMASeries(ema1, period)
1141
-
1142
- for (let i = 0; i < n; i++) {
1143
- const e1 = ema1[i]
1144
- const e2 = ema2[i]
1145
- if (e1 === undefined || e2 === undefined) continue
1146
- result[i] = 2 * e1 - e2
1147
- }
1148
- return result
1149
- }
1150
-
1151
-
1152
-
1153
- // ============================================================================
1154
- // TEMA — Triple Exponential Moving Average
1155
- // 公式: TEMA(t) = 3*EMA(t) - 3*EMA(EMA)(t) + EMA(EMA(EMA))(t)
1156
- // 性质: 对二次多项式输入零滞后(稳态),warmup ~ 3*(period-1)
1157
- // ============================================================================
1158
-
1159
- const DEFAULT_TEMA_PERIOD = 20
1160
-
1161
- export function calcTEMAData(data: KLineData[], period: number): (number | undefined)[] {
1162
- const n = data.length
1163
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1164
- if (n === 0 || period <= 0) return result
1165
-
1166
- const closes = new Array<number | undefined>(n)
1167
- for (let i = 0; i < n; i++) closes[i] = data[i]!.close
1168
-
1169
- const ema1 = _computeEMASeries(closes, period)
1170
- const ema2 = _computeEMASeries(ema1, period)
1171
- const ema3 = _computeEMASeries(ema2, period)
1172
-
1173
- for (let i = 0; i < n; i++) {
1174
- const e1 = ema1[i]
1175
- const e2 = ema2[i]
1176
- const e3 = ema3[i]
1177
- if (e1 === undefined || e2 === undefined || e3 === undefined) continue
1178
- result[i] = 3 * e1 - 3 * e2 + e3
1179
- }
1180
- return result
1181
- }
1182
-
1183
-
1184
-
1185
- // ============================================================================
1186
- // HMA — Hull Moving Average
1187
- // 公式: HMA(n) = WMA( 2*WMA(close, n/2) - WMA(close, n), sqrt(n) )
1188
- // 性质: 平滑性高于 WMA,滞后远低于同期 SMA
1189
- // warmup ≈ period - 1 + round(sqrt(period)) - 1
1190
- // ============================================================================
1191
-
1192
- const DEFAULT_HMA_PERIOD = 9
1193
-
1194
- export function calcHMAData(data: KLineData[], period: number): (number | undefined)[] {
1195
- const n = data.length
1196
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1197
- if (n === 0 || period <= 0) return result
1198
-
1199
- const closes = new Array<number | undefined>(n)
1200
- for (let i = 0; i < n; i++) closes[i] = data[i]!.close
1201
-
1202
- const halfPeriod = Math.max(1, Math.floor(period / 2))
1203
- const sqrtPeriod = Math.max(1, Math.round(Math.sqrt(period)))
1204
-
1205
- const wmaHalf = _computeWMAOnNumbers(closes, halfPeriod)
1206
- const wmaFull = _computeWMAOnNumbers(closes, period)
1207
-
1208
- const raw: (number | undefined)[] = new Array(n).fill(undefined)
1209
- for (let i = 0; i < n; i++) {
1210
- const h = wmaHalf[i]
1211
- const f = wmaFull[i]
1212
- if (h === undefined || f === undefined) continue
1213
- raw[i] = 2 * h - f
1214
- }
1215
- return _computeWMAOnNumbers(raw, sqrtPeriod)
1216
- }
1217
-
1218
-
1219
-
1220
- // ============================================================================
1221
- // KAMA — Kaufman's Adaptive Moving Average
1222
- // 自适应:在趋势强时跟得紧(接近 fast EMA),在震荡时跟得慢(接近 slow EMA)。
1223
- // ER (efficiency ratio) = |close[t] - close[t-n]| / sum(|close[i] - close[i-1]|, i=t-n+1..t)
1224
- // SC = (ER * (2/(fast+1) - 2/(slow+1)) + 2/(slow+1))^2
1225
- // KAMA(t) = KAMA(t-1) + SC * (close[t] - KAMA(t-1))
1226
- // 种子 KAMA(n-1) = close[n-1](或 SMA(n);这里采用 close 种子以保持与项目内 EMA 系列一致)
1227
- // ============================================================================
1228
-
1229
- const DEFAULT_KAMA_PERIOD = 10
1230
- const DEFAULT_KAMA_FAST_PERIOD = 2
1231
- const DEFAULT_KAMA_SLOW_PERIOD = 30
1232
-
1233
- export function calcKAMAData(
1234
- data: KLineData[],
1235
- period: number,
1236
- fastPeriod: number,
1237
- slowPeriod: number,
1238
- ): (number | undefined)[] {
1239
- const n = data.length
1240
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1241
- if (n === 0 || period <= 0 || fastPeriod <= 0 || slowPeriod <= 0 || n <= period) return result
1242
-
1243
- const fastSC = 2 / (fastPeriod + 1)
1244
- const slowSC = 2 / (slowPeriod + 1)
1245
- const scRange = fastSC - slowSC
1246
-
1247
- // 维护滚动求和:sum(|close[i] - close[i-1]|, i=t-period+1..t)
1248
- let volSum = 0
1249
- for (let i = 1; i <= period; i++) {
1250
- volSum += Math.abs(data[i]!.close - data[i - 1]!.close)
1251
- }
1252
-
1253
- let kama = data[period - 1]!.close
1254
- result[period - 1] = kama
1255
-
1256
- for (let t = period; t < n; t++) {
1257
- const close = data[t]!.close
1258
- const closeNPeriodsAgo = data[t - period]!.close
1259
- const direction = Math.abs(close - closeNPeriodsAgo)
1260
-
1261
- const er = volSum > 0 ? direction / volSum : 0
1262
- const sc = (er * scRange + slowSC) ** 2
1263
-
1264
- kama = kama + sc * (close - kama)
1265
- result[t] = kama
1266
-
1267
- // 滚动 volSum:减去最旧的 |close[t-period+1] - close[t-period]|,加上最新的 |close[t+1] - close[t]|
1268
- if (t < n - 1) {
1269
- volSum -= Math.abs(data[t - period + 1]!.close - data[t - period]!.close)
1270
- volSum += Math.abs(data[t + 1]!.close - data[t]!.close)
1271
- }
1272
- }
1273
-
1274
- return result
1275
- }
1276
-
1277
-
1278
-
1279
- // ============================================================================
1280
- // SAR — Parabolic Stop and Reverse
1281
- // 经典 Wilder 公式:SAR(t+1) = SAR(t) + AF * (EP - SAR(t)),AF 在每次创出新极端时 +step(上限 maxStep)
1282
- // 趋势翻转条件:上升趋势中 SAR 越过 low(或反之)
1283
- // 种子:从 bar[1] 起,初始 trend=up,SAR=low[0],EP=high[0],AF=step
1284
- // 返回每根 K 线对应的 SAR 点(带方向)
1285
- // ============================================================================
1286
-
1287
- export interface SARPoint {
1288
- value: number
1289
- trend: 'up' | 'down'
1290
- }
1291
-
1292
- const DEFAULT_SAR_STEP = 0.02
1293
- const DEFAULT_SAR_MAX_STEP = 0.2
1294
-
1295
- export function calcSARData(
1296
- data: KLineData[],
1297
- step: number,
1298
- maxStep: number,
1299
- ): (SARPoint | undefined)[] {
1300
- const n = data.length
1301
- const result: (SARPoint | undefined)[] = new Array(n).fill(undefined)
1302
- if (n < 2 || step <= 0 || maxStep <= 0) return result
1303
-
1304
- let trend: 'up' | 'down' = data[1]!.close >= data[0]!.close ? 'up' : 'down'
1305
- let sar = trend === 'up' ? data[0]!.low : data[0]!.high
1306
- let ep = trend === 'up' ? data[0]!.high : data[0]!.low
1307
- let af = step
1308
-
1309
- result[0] = { value: sar, trend }
1310
-
1311
- for (let t = 1; t < n; t++) {
1312
- const bar = data[t]!
1313
- // 先按当前趋势推进 SAR
1314
- sar = sar + af * (ep - sar)
1315
-
1316
- // 边界约束:SAR 不能穿透前两根 K 线的极端
1317
- if (trend === 'up') {
1318
- const cap1 = data[t - 1]!.low
1319
- const cap2 = t >= 2 ? data[t - 2]!.low : cap1
1320
- sar = Math.min(sar, cap1, cap2)
1321
- } else {
1322
- const cap1 = data[t - 1]!.high
1323
- const cap2 = t >= 2 ? data[t - 2]!.high : cap1
1324
- sar = Math.max(sar, cap1, cap2)
1325
- }
1326
-
1327
- // 检测翻转
1328
- if (trend === 'up' && bar.low < sar) {
1329
- trend = 'down'
1330
- sar = ep
1331
- ep = bar.low
1332
- af = step
1333
- } else if (trend === 'down' && bar.high > sar) {
1334
- trend = 'up'
1335
- sar = ep
1336
- ep = bar.high
1337
- af = step
1338
- } else {
1339
- // 同趋势:更新 EP / AF
1340
- if (trend === 'up' && bar.high > ep) {
1341
- ep = bar.high
1342
- af = Math.min(af + step, maxStep)
1343
- } else if (trend === 'down' && bar.low < ep) {
1344
- ep = bar.low
1345
- af = Math.min(af + step, maxStep)
1346
- }
1347
- }
1348
-
1349
- result[t] = { value: sar, trend }
1350
- }
1351
-
1352
- return result
1353
- }
1354
-
1355
-
1356
-
1357
- // ============================================================================
1358
- // SuperTrend — ATR-based trend-following stop/band
1359
- // ============================================================================
1360
-
1361
- export interface SuperTrendPoint {
1362
- value: number
1363
- trend: 'up' | 'down'
1364
- }
1365
-
1366
- const DEFAULT_SUPERTREND_ATR_PERIOD = 10
1367
- const DEFAULT_SUPERTREND_MULTIPLIER = 3
1368
-
1369
- export function calcSuperTrendData(
1370
- data: KLineData[],
1371
- atrPeriod: number,
1372
- multiplier: number,
1373
- ): (SuperTrendPoint | undefined)[] {
1374
- const n = data.length
1375
- const result: (SuperTrendPoint | undefined)[] = new Array(n).fill(undefined)
1376
- if (n === 0 || atrPeriod <= 0 || multiplier <= 0) return result
1377
-
1378
- const atr = calcATRData(data, atrPeriod)
1379
-
1380
- let trend: 'up' | 'down' = 'up'
1381
- let prevUpper = Infinity
1382
- let prevLower = -Infinity
1383
-
1384
- for (let t = 0; t < n; t++) {
1385
- const bar = data[t]!
1386
- const a = atr[t]
1387
- if (a === undefined) continue
1388
-
1389
- const hl2 = (bar.high + bar.low) / 2
1390
- const upperBasic = hl2 + multiplier * a
1391
- const lowerBasic = hl2 - multiplier * a
1392
-
1393
- // Smoothing: keep the previous band unless price has broken through it
1394
- const prevClose = t > 0 ? data[t - 1]!.close : bar.close
1395
- const upper = (upperBasic < prevUpper || prevClose > prevUpper) ? upperBasic : prevUpper
1396
- const lower = (lowerBasic > prevLower || prevClose < prevLower) ? lowerBasic : prevLower
1397
-
1398
- // Trend update
1399
- if (trend === 'up' && bar.close < lower) {
1400
- trend = 'down'
1401
- } else if (trend === 'down' && bar.close > upper) {
1402
- trend = 'up'
1403
- }
1404
-
1405
- result[t] = { value: trend === 'up' ? lower : upper, trend }
1406
-
1407
- prevUpper = upper
1408
- prevLower = lower
1409
- }
1410
-
1411
- return result
1412
- }
1413
-
1414
-
1415
-
1416
- // ============================================================================
1417
- // Keltner Channel — EMA ± multiplier × ATR
1418
- // ============================================================================
1419
-
1420
- export interface KeltnerPoint {
1421
- upper: number
1422
- middle: number
1423
- lower: number
1424
- }
1425
-
1426
- const DEFAULT_KELTNER_EMA_PERIOD = 20
1427
- const DEFAULT_KELTNER_ATR_PERIOD = 10
1428
- const DEFAULT_KELTNER_MULTIPLIER = 2
1429
-
1430
- export function calcKeltnerData(
1431
- data: KLineData[],
1432
- emaPeriod: number,
1433
- atrPeriod: number,
1434
- multiplier: number,
1435
- ): (KeltnerPoint | undefined)[] {
1436
- const n = data.length
1437
- const result: (KeltnerPoint | undefined)[] = new Array(n).fill(undefined)
1438
- if (n === 0 || emaPeriod <= 0 || atrPeriod <= 0) return result
1439
-
1440
- const closes = new Array<number | undefined>(n)
1441
- for (let i = 0; i < n; i++) closes[i] = data[i]!.close
1442
-
1443
- const ema = _computeEMASeries(closes, emaPeriod)
1444
- const atr = calcATRData(data, atrPeriod)
1445
-
1446
- for (let t = 0; t < n; t++) {
1447
- const m = ema[t]
1448
- const a = atr[t]
1449
- if (m === undefined || a === undefined) continue
1450
- result[t] = {
1451
- upper: m + multiplier * a,
1452
- middle: m,
1453
- lower: m - multiplier * a,
1454
- }
1455
- }
1456
- return result
1457
- }
1458
-
1459
-
1460
-
1461
- // ============================================================================
1462
- // Donchian Channel — rolling max(high) / min(low) over period
1463
- // ============================================================================
1464
-
1465
- export interface DonchianPoint {
1466
- upper: number
1467
- middle: number
1468
- lower: number
1469
- }
1470
-
1471
- const DEFAULT_DONCHIAN_PERIOD = 20
1472
-
1473
- export function calcDonchianData(
1474
- data: KLineData[],
1475
- period: number,
1476
- ): (DonchianPoint | undefined)[] {
1477
- const n = data.length
1478
- const result: (DonchianPoint | undefined)[] = new Array(n).fill(undefined)
1479
- if (n === 0 || period <= 0 || n < period) return result
1480
-
1481
- for (let t = period - 1; t < n; t++) {
1482
- let hi = -Infinity
1483
- let lo = Infinity
1484
- for (let k = 0; k < period; k++) {
1485
- const bar = data[t - k]!
1486
- if (bar.high > hi) hi = bar.high
1487
- if (bar.low < lo) lo = bar.low
1488
- }
1489
- result[t] = { upper: hi, middle: (hi + lo) / 2, lower: lo }
1490
- }
1491
- return result
1492
- }
1493
-
1494
-
1495
-
1496
- // ============================================================================
1497
- // Ichimoku Kinko Hyo — 一目均衡表
1498
- // 5 线 + 云图(spanA/B 前置位移构成):
1499
- // tenkan(t) = (max(high[t-tenkanPeriod+1..t]) + min(low[t-tenkanPeriod+1..t])) / 2
1500
- // kijun(t) = 同公式但用 kijunPeriod
1501
- // spanA(t) = (tenkan(t-displacement) + kijun(t-displacement)) / 2 ← 前置 displacement
1502
- // spanB(t) = 用 spanBPeriod 计算后再前置 displacement
1503
- // chikou(t) = close(t+displacement) ← 后置 displacement
1504
- // 输出长度 = data.length + displacement,末尾 displacement 根为未来云(仅 spanA/spanB)
1505
- // ============================================================================
1506
-
1507
- export interface IchimokuPoint {
1508
- tenkan?: number
1509
- kijun?: number
1510
- spanA?: number
1511
- spanB?: number
1512
- chikou?: number
1513
- }
1514
-
1515
- const DEFAULT_ICHIMOKU_TENKAN = 9
1516
- const DEFAULT_ICHIMOKU_KIJUN = 26
1517
- const DEFAULT_ICHIMOKU_SPAN_B = 52
1518
- const DEFAULT_ICHIMOKU_DISPLACEMENT = 26
1519
-
1520
- function _rollingMidline(data: KLineData[], period: number): (number | undefined)[] {
1521
- const n = data.length
1522
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1523
- if (n < period || period <= 0) return result
1524
- for (let t = period - 1; t < n; t++) {
1525
- let hi = -Infinity
1526
- let lo = Infinity
1527
- for (let k = 0; k < period; k++) {
1528
- const bar = data[t - k]!
1529
- if (bar.high > hi) hi = bar.high
1530
- if (bar.low < lo) lo = bar.low
1531
- }
1532
- result[t] = (hi + lo) / 2
1533
- }
1534
- return result
1535
- }
1536
-
1537
- export function calcIchimokuData(
1538
- data: KLineData[],
1539
- tenkanPeriod: number,
1540
- kijunPeriod: number,
1541
- spanBPeriod: number,
1542
- displacement: number,
1543
- ): (IchimokuPoint | undefined)[] {
1544
- const n = data.length
1545
- const totalLen = n + displacement
1546
- const result: (IchimokuPoint | undefined)[] = new Array(totalLen).fill(undefined)
1547
- if (n === 0 || tenkanPeriod <= 0 || kijunPeriod <= 0 || spanBPeriod <= 0) {
1548
- return result.slice(0, n)
1549
- }
1550
-
1551
- const tenkan = _rollingMidline(data, tenkanPeriod)
1552
- const kijun = _rollingMidline(data, kijunPeriod)
1553
- const spanBSource = _rollingMidline(data, spanBPeriod)
1554
-
1555
- for (let t = 0; t < n; t++) {
1556
- const point: IchimokuPoint = {}
1557
- if (tenkan[t] !== undefined) point.tenkan = tenkan[t]
1558
- if (kijun[t] !== undefined) point.kijun = kijun[t]
1559
-
1560
- // spanA / spanB 由 displacement 根之前的值填到当前槽位
1561
- const src = t - displacement
1562
- if (src >= 0) {
1563
- if (tenkan[src] !== undefined && kijun[src] !== undefined) {
1564
- point.spanA = (tenkan[src]! + kijun[src]!) / 2
1565
- }
1566
- if (spanBSource[src] !== undefined) {
1567
- point.spanB = spanBSource[src]
1568
- }
1569
- }
1570
-
1571
- // chikou:当前 close 后置 displacement 根(即存到 t - displacement 槽位上 close[t])
1572
- // 这里改成:存当前槽位的 chikou = close[t + displacement],需 future 数据;不可用时 undefined
1573
- const future = t + displacement
1574
- if (future < n) point.chikou = data[future]!.close
1575
-
1576
- result[t] = point
1577
- }
1578
-
1579
- // 未来云:在 data 末尾延伸 displacement 根,仅含 spanA/spanB
1580
- for (let f = 0; f < displacement; f++) {
1581
- const t = n + f
1582
- const src = t - displacement
1583
- const point: IchimokuPoint = {}
1584
- if (src >= 0 && src < n) {
1585
- if (tenkan[src] !== undefined && kijun[src] !== undefined) {
1586
- point.spanA = (tenkan[src]! + kijun[src]!) / 2
1587
- }
1588
- if (spanBSource[src] !== undefined) {
1589
- point.spanB = spanBSource[src]
1590
- }
1591
- }
1592
- result[t] = point
1593
- }
1594
-
1595
- return result
1596
- }
1597
-
1598
-
1599
-
1600
- // ============================================================================
1601
- // ROC — Rate of Change
1602
- // ROC(t) = (close[t] - close[t-period]) / close[t-period] * 100
1603
- // ============================================================================
1604
-
1605
- const DEFAULT_ROC_PERIOD = 12
1606
-
1607
- export function calcROCData(data: KLineData[], period: number): (number | undefined)[] {
1608
- const n = data.length
1609
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1610
- if (n === 0 || period <= 0) return result
1611
- for (let t = period; t < n; t++) {
1612
- const prev = data[t - period]!.close
1613
- if (prev === 0) continue
1614
- result[t] = (data[t]!.close - prev) / prev * 100
1615
- }
1616
- return result
1617
- }
1618
-
1619
-
1620
-
1621
- // ============================================================================
1622
- // TRIX — Triple Exponential Smoothing Oscillator
1623
- // EMA3 = EMA(EMA(EMA(close, p), p), p)
1624
- // TRIX(t) = (EMA3[t] - EMA3[t-1]) / EMA3[t-1] * 100
1625
- // Signal(t) = EMA(TRIX, signalPeriod) —— 配合金叉/死叉
1626
- // ============================================================================
1627
-
1628
- export interface TRIXResult {
1629
- series: (number | undefined)[]
1630
- signalSeries: (number | undefined)[]
1631
- }
1632
-
1633
- const DEFAULT_TRIX_PERIOD = 15
1634
- const DEFAULT_TRIX_SIGNAL_PERIOD = 9
1635
-
1636
- export function calcTRIXData(
1637
- data: KLineData[],
1638
- period: number,
1639
- signalPeriod: number,
1640
- ): TRIXResult {
1641
- const n = data.length
1642
- const series: (number | undefined)[] = new Array(n).fill(undefined)
1643
- const signalSeries: (number | undefined)[] = new Array(n).fill(undefined)
1644
- if (n === 0 || period <= 0) return { series, signalSeries }
1645
-
1646
- const closes = new Array<number | undefined>(n)
1647
- for (let i = 0; i < n; i++) closes[i] = data[i]!.close
1648
-
1649
- const ema1 = _computeEMASeries(closes, period)
1650
- const ema2 = _computeEMASeries(ema1, period)
1651
- const ema3 = _computeEMASeries(ema2, period)
1652
-
1653
- for (let t = 1; t < n; t++) {
1654
- const cur = ema3[t]
1655
- const prev = ema3[t - 1]
1656
- if (cur === undefined || prev === undefined || prev === 0) continue
1657
- series[t] = (cur - prev) / prev * 100
1658
- }
1659
-
1660
- if (signalPeriod > 0) {
1661
- const smoothed = _computeEMASeries(series, signalPeriod)
1662
- for (let i = 0; i < n; i++) signalSeries[i] = smoothed[i]
1663
- }
1664
-
1665
- return { series, signalSeries }
1666
- }
1667
-
1668
-
1669
-
1670
- // ============================================================================
1671
- // HV — Historical Volatility (close-to-close log returns)
1672
- // HV(t) = stdDev(log(close[i]/close[i-1]), i=t-period+1..t) * sqrt(annualization)
1673
- // 输出年化波动率(百分比形式 × 100)
1674
- // ============================================================================
1675
-
1676
- const DEFAULT_HV_PERIOD = 20
1677
- const DEFAULT_HV_ANNUALIZATION = 252
1678
-
1679
- export function calcHVData(
1680
- data: KLineData[],
1681
- period: number,
1682
- annualizationFactor: number,
1683
- ): (number | undefined)[] {
1684
- const n = data.length
1685
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1686
- if (n < 2 || period <= 0 || annualizationFactor <= 0) return result
1687
-
1688
- const logReturns: number[] = new Array(n)
1689
- logReturns[0] = 0
1690
- for (let t = 1; t < n; t++) {
1691
- const prev = data[t - 1]!.close
1692
- const cur = data[t]!.close
1693
- logReturns[t] = (prev > 0 && cur > 0) ? Math.log(cur / prev) : 0
1694
- }
1695
-
1696
- const annScale = Math.sqrt(annualizationFactor)
1697
- for (let t = period; t < n; t++) {
1698
- let sum = 0
1699
- for (let k = 1; k <= period; k++) sum += logReturns[t - period + k]!
1700
- const mean = sum / period
1701
- let varSum = 0
1702
- for (let k = 1; k <= period; k++) {
1703
- const diff = logReturns[t - period + k]! - mean
1704
- varSum += diff * diff
1705
- }
1706
- const std = Math.sqrt(varSum / (period - 1 > 0 ? period - 1 : 1))
1707
- result[t] = std * annScale * 100
1708
- }
1709
-
1710
- return result
1711
- }
1712
-
1713
-
1714
-
1715
- // ============================================================================
1716
- // Parkinson Volatility — high-low range volatility
1717
- // PV(t) = sqrt( (1/(4*ln(2))) * mean(ln(high[i]/low[i])^2) * annualization ) * 100
1718
- // ============================================================================
1719
-
1720
- const DEFAULT_PARKINSON_PERIOD = 20
1721
- const DEFAULT_PARKINSON_ANNUALIZATION = 252
1722
-
1723
- export function calcParkinsonData(
1724
- data: KLineData[],
1725
- period: number,
1726
- annualizationFactor: number,
1727
- ): (number | undefined)[] {
1728
- const n = data.length
1729
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1730
- if (n === 0 || period <= 0 || annualizationFactor <= 0 || n < period) return result
1731
-
1732
- const factor = 1 / (4 * Math.log(2))
1733
- const annScale = Math.sqrt(annualizationFactor)
1734
-
1735
- const hlLogSq: number[] = new Array(n)
1736
- for (let i = 0; i < n; i++) {
1737
- const bar = data[i]!
1738
- if (bar.high > 0 && bar.low > 0) {
1739
- const ln = Math.log(bar.high / bar.low)
1740
- hlLogSq[i] = ln * ln
1741
- } else {
1742
- hlLogSq[i] = 0
1743
- }
1744
- }
1745
-
1746
- for (let t = period - 1; t < n; t++) {
1747
- let sum = 0
1748
- for (let k = 0; k < period; k++) sum += hlLogSq[t - k]!
1749
- const mean = sum / period
1750
- result[t] = Math.sqrt(factor * mean) * annScale * 100
1751
- }
1752
-
1753
- return result
1754
- }
1755
-
1756
-
1757
-
1758
- // ============================================================================
1759
- // Chaikin Volatility — EMA(high-low) 的 ROC
1760
- // ChaikinVol(t) = (EMA(H-L, p)[t] - EMA(H-L, p)[t-rocPeriod]) / EMA(H-L, p)[t-rocPeriod] * 100
1761
- // ============================================================================
1762
-
1763
- const DEFAULT_CHAIKIN_VOL_EMA_PERIOD = 10
1764
- const DEFAULT_CHAIKIN_VOL_ROC_PERIOD = 10
1765
-
1766
- export function calcChaikinVolData(
1767
- data: KLineData[],
1768
- emaPeriod: number,
1769
- rocPeriod: number,
1770
- ): (number | undefined)[] {
1771
- const n = data.length
1772
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1773
- if (n === 0 || emaPeriod <= 0 || rocPeriod <= 0) return result
1774
-
1775
- const hl: (number | undefined)[] = new Array(n)
1776
- for (let i = 0; i < n; i++) hl[i] = data[i]!.high - data[i]!.low
1777
-
1778
- const emaSeries = _computeEMASeries(hl, emaPeriod)
1779
-
1780
- for (let t = rocPeriod; t < n; t++) {
1781
- const cur = emaSeries[t]
1782
- const prev = emaSeries[t - rocPeriod]
1783
- if (cur === undefined || prev === undefined || prev === 0) continue
1784
- result[t] = (cur - prev) / prev * 100
1785
- }
1786
-
1787
- return result
1788
- }
1789
-
1790
-
1791
-
1792
- // ============================================================================
1793
- // VMA — Volume Moving Average (SMA of volume)
1794
- // ============================================================================
1795
-
1796
- const DEFAULT_VMA_PERIOD = 5
1797
-
1798
- export function calcVMAData(data: KLineData[], period: number): (number | undefined)[] {
1799
- const n = data.length
1800
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1801
- if (n === 0 || period <= 0 || n < period) return result
1802
- let sum = 0
1803
- for (let i = 0; i < period; i++) sum += data[i]!.volume ?? 0
1804
- result[period - 1] = sum / period
1805
- for (let t = period; t < n; t++) {
1806
- sum += (data[t]!.volume ?? 0) - (data[t - period]!.volume ?? 0)
1807
- result[t] = sum / period
1808
- }
1809
- return result
1810
- }
1811
-
1812
-
1813
-
1814
- // ============================================================================
1815
- // OBV — On Balance Volume (cumulative)
1816
- // close[t] > close[t-1] → OBV += volume[t]
1817
- // close[t] < close[t-1] → OBV -= volume[t]
1818
- // else → OBV unchanged
1819
- // ============================================================================
1820
-
1821
- export function calcOBVData(data: KLineData[]): (number | undefined)[] {
1822
- const n = data.length
1823
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1824
- if (n === 0) return result
1825
- let obv = 0
1826
- result[0] = 0
1827
- for (let t = 1; t < n; t++) {
1828
- const cur = data[t]!
1829
- const prev = data[t - 1]!
1830
- if (cur.close > prev.close) obv += cur.volume ?? 0
1831
- else if (cur.close < prev.close) obv -= cur.volume ?? 0
1832
- result[t] = obv
1833
- }
1834
- return result
1835
- }
1836
-
1837
-
1838
-
1839
- // ============================================================================
1840
- // PVT — Price Volume Trend (cumulative)
1841
- // PVT(t) = PVT(t-1) + ((close[t] - close[t-1]) / close[t-1]) * volume[t]
1842
- // ============================================================================
1843
-
1844
- export function calcPVTData(data: KLineData[]): (number | undefined)[] {
1845
- const n = data.length
1846
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1847
- if (n === 0) return result
1848
- let pvt = 0
1849
- result[0] = 0
1850
- for (let t = 1; t < n; t++) {
1851
- const prevClose = data[t - 1]!.close
1852
- if (prevClose === 0) {
1853
- result[t] = pvt
1854
- continue
1855
- }
1856
- pvt += ((data[t]!.close - prevClose) / prevClose) * (data[t]!.volume ?? 0)
1857
- result[t] = pvt
1858
- }
1859
- return result
1860
- }
1861
-
1862
-
1863
-
1864
- // ============================================================================
1865
- // VWAP — Volume-Weighted Average Price
1866
- // VWAP(t) = sum_{i in session} TP(i) * V(i) / sum_{i in session} V(i)
1867
- // where TP(i) = (H+L+C)/3 (typical price)
1868
- // Session reset: if sessionResetGapMs > 0, reset cumulative sums when the gap
1869
- // between consecutive bar timestamps exceeds this value (e.g., overnight)
1870
- // ============================================================================
1871
-
1872
- const DEFAULT_VWAP_SESSION_GAP_MS = 0
1873
-
1874
- export function calcVWAPData(
1875
- data: KLineData[],
1876
- sessionResetGapMs: number,
1877
- ): (number | undefined)[] {
1878
- const n = data.length
1879
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1880
- if (n === 0) return result
1881
-
1882
- let cumPV = 0
1883
- let cumV = 0
1884
- let prevTs = data[0]!.timestamp
1885
-
1886
- for (let t = 0; t < n; t++) {
1887
- const bar = data[t]!
1888
- if (sessionResetGapMs > 0 && t > 0 && bar.timestamp - prevTs > sessionResetGapMs) {
1889
- cumPV = 0
1890
- cumV = 0
1891
- }
1892
- const tp = (bar.high + bar.low + bar.close) / 3
1893
- cumPV += tp * (bar.volume ?? 0)
1894
- cumV += bar.volume ?? 0
1895
- result[t] = cumV > 0 ? cumPV / cumV : tp
1896
- prevTs = bar.timestamp
1897
- }
1898
-
1899
- return result
1900
- }
1901
-
1902
-
1903
-
1904
- // ============================================================================
1905
- // CMF — Chaikin Money Flow
1906
- // MFM = ((C-L) - (H-C)) / (H-L) ∈ [-1, 1]
1907
- // MFV = MFM * Volume
1908
- // CMF(t) = sum(MFV[t-period+1..t]) / sum(Volume[t-period+1..t]) ∈ [-1, 1]
1909
- // ============================================================================
1910
-
1911
- const DEFAULT_CMF_PERIOD = 20
1912
-
1913
- export function calcCMFData(data: KLineData[], period: number): (number | undefined)[] {
1914
- const n = data.length
1915
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1916
- if (n === 0 || period <= 0 || n < period) return result
1917
-
1918
- const mfv: number[] = new Array(n)
1919
- for (let i = 0; i < n; i++) {
1920
- const bar = data[i]!
1921
- const range = bar.high - bar.low
1922
- const mfm = range > 0 ? ((bar.close - bar.low) - (bar.high - bar.close)) / range : 0
1923
- mfv[i] = mfm * (bar.volume ?? 0)
1924
- }
1925
-
1926
- let sumMFV = 0
1927
- let sumV = 0
1928
- for (let i = 0; i < period; i++) {
1929
- sumMFV += mfv[i]!
1930
- sumV += data[i]!.volume ?? 0
1931
- }
1932
- result[period - 1] = sumV > 0 ? sumMFV / sumV : 0
1933
-
1934
- for (let t = period; t < n; t++) {
1935
- sumMFV += mfv[t]! - mfv[t - period]!
1936
- sumV += (data[t]!.volume ?? 0) - (data[t - period]!.volume ?? 0)
1937
- result[t] = sumV > 0 ? sumMFV / sumV : 0
1938
- }
1939
- return result
1940
- }
1941
-
1942
-
1943
-
1944
- // ============================================================================
1945
- // MFI — Money Flow Index
1946
- // TP = (H+L+C)/3, RMF = TP * Volume
1947
- // PMF = sum of RMF where TP > TP[-1]; NMF = sum where TP < TP[-1]
1948
- // MFR = PMF / NMF; MFI = 100 - 100 / (1 + MFR) ∈ [0, 100]
1949
- // ============================================================================
1950
-
1951
- const DEFAULT_MFI_PERIOD = 14
1952
-
1953
- export function calcMFIData(data: KLineData[], period: number): (number | undefined)[] {
1954
- const n = data.length
1955
- const result: (number | undefined)[] = new Array(n).fill(undefined)
1956
- if (n < period + 1 || period <= 0) return result
1957
-
1958
- const tp: number[] = new Array(n)
1959
- for (let i = 0; i < n; i++) tp[i] = (data[i]!.high + data[i]!.low + data[i]!.close) / 3
1960
-
1961
- // Pre-classified positive/negative money flow per bar
1962
- const pmfArr: number[] = new Array(n)
1963
- const nmfArr: number[] = new Array(n)
1964
- pmfArr[0] = 0
1965
- nmfArr[0] = 0
1966
- for (let i = 1; i < n; i++) {
1967
- const rmf = tp[i]! * (data[i]!.volume ?? 0)
1968
- if (tp[i]! > tp[i - 1]!) {
1969
- pmfArr[i] = rmf
1970
- nmfArr[i] = 0
1971
- } else if (tp[i]! < tp[i - 1]!) {
1972
- pmfArr[i] = 0
1973
- nmfArr[i] = rmf
1974
- } else {
1975
- pmfArr[i] = 0
1976
- nmfArr[i] = 0
1977
- }
1978
- }
1979
-
1980
- let pSum = 0
1981
- let nSum = 0
1982
- for (let i = 1; i <= period; i++) {
1983
- pSum += pmfArr[i]!
1984
- nSum += nmfArr[i]!
1985
- }
1986
- result[period] = nSum > 0 ? 100 - 100 / (1 + pSum / nSum) : 100
1987
-
1988
- for (let t = period + 1; t < n; t++) {
1989
- pSum += pmfArr[t]! - pmfArr[t - period]!
1990
- nSum += nmfArr[t]! - nmfArr[t - period]!
1991
- result[t] = nSum > 0 ? 100 - 100 / (1 + pSum / nSum) : 100
1992
- }
1993
- return result
1994
- }
1995
-
1996
-
1997
-
1998
- // ============================================================================
1999
- // Pivot Points — Classic floor-trader pivots from prior bar's HLC
2000
- // PP = (H + L + C) / 3
2001
- // R1 = 2·PP - L; S1 = 2·PP - H
2002
- // R2 = PP + (H - L); S2 = PP - (H - L)
2003
- // R3 = H + 2·(PP - L); S3 = L - 2·(H - PP)
2004
- // Each bar t (t >= 1) shows pivots derived from bar[t-1]'s HLC.
2005
- // ============================================================================
2006
-
2007
- export interface PivotPoint {
2008
- pp: number
2009
- r1: number
2010
- r2: number
2011
- r3: number
2012
- s1: number
2013
- s2: number
2014
- s3: number
2015
- }
2016
-
2017
- export function calcPivotData(data: KLineData[]): (PivotPoint | undefined)[] {
2018
- const n = data.length
2019
- const result: (PivotPoint | undefined)[] = new Array(n).fill(undefined)
2020
- if (n < 2) return result
2021
- for (let t = 1; t < n; t++) {
2022
- const p = data[t - 1]!
2023
- const pp = (p.high + p.low + p.close) / 3
2024
- const range = p.high - p.low
2025
- result[t] = {
2026
- pp,
2027
- r1: 2 * pp - p.low,
2028
- s1: 2 * pp - p.high,
2029
- r2: pp + range,
2030
- s2: pp - range,
2031
- r3: p.high + 2 * (pp - p.low),
2032
- s3: p.low - 2 * (p.high - pp),
2033
- }
2034
- }
2035
- return result
2036
- }
2037
-
2038
-
2039
-
2040
- // ============================================================================
2041
- // Fibonacci Retracement — anchored to rolling-window high/low
2042
- // Window = last `period` bars. High = max(high), Low = min(low).
2043
- // Direction = 'up' if the most recent extreme is the high (price moved up);
2044
- // 'down' otherwise. Retracement levels computed accordingly.
2045
- // ============================================================================
2046
-
2047
- export interface FibPoint {
2048
- high: number
2049
- low: number
2050
- direction: 'up' | 'down'
2051
- level236: number
2052
- level382: number
2053
- level500: number
2054
- level618: number
2055
- level786: number
2056
- }
2057
-
2058
- const DEFAULT_FIB_PERIOD = 50
2059
-
2060
- export function calcFibData(data: KLineData[], period: number): (FibPoint | undefined)[] {
2061
- const n = data.length
2062
- const result: (FibPoint | undefined)[] = new Array(n).fill(undefined)
2063
- if (n === 0 || period <= 0 || n < period) return result
2064
-
2065
- for (let t = period - 1; t < n; t++) {
2066
- let hi = -Infinity
2067
- let lo = Infinity
2068
- let hiIdx = t
2069
- let loIdx = t
2070
- for (let k = 0; k < period; k++) {
2071
- const bar = data[t - k]!
2072
- if (bar.high > hi) { hi = bar.high; hiIdx = t - k }
2073
- if (bar.low < lo) { lo = bar.low; loIdx = t - k }
2074
- }
2075
- const direction: 'up' | 'down' = hiIdx >= loIdx ? 'up' : 'down'
2076
- const range = hi - lo
2077
- // For uptrend retracements: 0% at high, 100% at low; price retraces FROM high TOWARD low
2078
- // For downtrend: 0% at low, 100% at high
2079
- const level = (frac: number) =>
2080
- direction === 'up' ? hi - range * frac : lo + range * frac
2081
- result[t] = {
2082
- high: hi,
2083
- low: lo,
2084
- direction,
2085
- level236: level(0.236),
2086
- level382: level(0.382),
2087
- level500: level(0.5),
2088
- level618: level(0.618),
2089
- level786: level(0.786),
2090
- }
2091
- }
2092
- return result
2093
- }
2094
-
2095
-
2096
-
2097
- // ============================================================================
2098
- // SMC Structure — Swing detection + BOS / CHOCH events
2099
- // Window-based swing fractal (default left=right=2 = Bill Williams fractal).
2100
- // Swing confirmed when right-window bars have closed after the extremum.
2101
- // Trend state machine: HH+HL = up, LL+LH = down, mixed = range.
2102
- // BOS = continuation break (close > last HH in up trend, or < last LL in down trend).
2103
- // CHOCH = reversal break (against current trend).
2104
- // ============================================================================
2105
-
2106
- export interface SwingPoint {
2107
- index: number
2108
- price: number
2109
- kind: 'high' | 'low'
2110
- label: 'HH' | 'HL' | 'LH' | 'LL'
2111
- confirmed: boolean
2112
- }
2113
-
2114
- export type StructureEventKind = 'BOS' | 'CHOCH'
2115
-
2116
- export interface StructureEvent {
2117
- kind: StructureEventKind
2118
- index: number
2119
- triggerPrice: number
2120
- brokenLevel: number
2121
- brokenSwingIndex: number
2122
- direction: 'up' | 'down'
2123
- }
2124
-
2125
- export interface StructureSnapshot {
2126
- swings: SwingPoint[]
2127
- events: StructureEvent[]
2128
- trend: 'up' | 'down' | 'range'
2129
- }
2130
-
2131
- const DEFAULT_STRUCTURE_LEFT = 2
2132
- const DEFAULT_STRUCTURE_RIGHT = 2
2133
-
2134
- export function calcStructureData(
2135
- data: KLineData[],
2136
- leftWindow: number,
2137
- rightWindow: number,
2138
- breakoutSource: 'close' | 'wick',
2139
- ): StructureSnapshot {
2140
- const n = data.length
2141
- if (n === 0 || leftWindow < 0 || rightWindow < 0) {
2142
- return { swings: [], events: [], trend: 'range' }
2143
- }
2144
-
2145
- const rawSwings: { index: number; price: number; kind: 'high' | 'low'; confirmed: boolean }[] = []
2146
- for (let i = 0; i < n; i++) {
2147
- const bar = data[i]!
2148
- if (isExtremum(data, i, leftWindow, rightWindow, 'high')) {
2149
- rawSwings.push({ index: i, price: bar.high, kind: 'high', confirmed: i + rightWindow < n })
2150
- }
2151
- if (isExtremum(data, i, leftWindow, rightWindow, 'low')) {
2152
- rawSwings.push({ index: i, price: bar.low, kind: 'low', confirmed: i + rightWindow < n })
2153
- }
2154
- }
2155
- rawSwings.sort((a, b) => a.index - b.index)
2156
-
2157
- const swings: SwingPoint[] = []
2158
- let lastHigh: { index: number; price: number } | null = null
2159
- let lastLow: { index: number; price: number } | null = null
2160
- for (const s of rawSwings) {
2161
- let label: 'HH' | 'HL' | 'LH' | 'LL'
2162
- if (s.kind === 'high') {
2163
- label = lastHigh && s.price > lastHigh.price ? 'HH' : 'LH'
2164
- lastHigh = { index: s.index, price: s.price }
2165
- } else {
2166
- label = lastLow && s.price > lastLow.price ? 'HL' : 'LL'
2167
- lastLow = { index: s.index, price: s.price }
2168
- }
2169
- swings.push({ ...s, label })
2170
- }
2171
-
2172
- const events: StructureEvent[] = []
2173
- let trend: 'up' | 'down' | 'range' = 'range'
2174
- let lastSwingHigh: { index: number; price: number } | null = null
2175
- let lastSwingLow: { index: number; price: number } | null = null
2176
- const confirmedSwings = swings.filter((s) => s.confirmed)
2177
- let swingCursor = 0
2178
-
2179
- for (let t = 0; t < n; t++) {
2180
- while (swingCursor < confirmedSwings.length && confirmedSwings[swingCursor]!.index + rightWindow <= t) {
2181
- const s = confirmedSwings[swingCursor]!
2182
- if (s.kind === 'high') lastSwingHigh = { index: s.index, price: s.price }
2183
- else lastSwingLow = { index: s.index, price: s.price }
2184
- swingCursor++
2185
- }
2186
-
2187
- const bar = data[t]!
2188
- const upBreakPrice = breakoutSource === 'close' ? bar.close : bar.high
2189
- const downBreakPrice = breakoutSource === 'close' ? bar.close : bar.low
2190
-
2191
- if (lastSwingHigh && upBreakPrice > lastSwingHigh.price) {
2192
- const kind: StructureEventKind = trend === 'down' ? 'CHOCH' : 'BOS'
2193
- events.push({
2194
- kind,
2195
- index: t,
2196
- triggerPrice: upBreakPrice,
2197
- brokenLevel: lastSwingHigh.price,
2198
- brokenSwingIndex: lastSwingHigh.index,
2199
- direction: 'up',
2200
- })
2201
- trend = 'up'
2202
- lastSwingHigh = null
2203
- } else if (lastSwingLow && downBreakPrice < lastSwingLow.price) {
2204
- const kind: StructureEventKind = trend === 'up' ? 'CHOCH' : 'BOS'
2205
- events.push({
2206
- kind,
2207
- index: t,
2208
- triggerPrice: downBreakPrice,
2209
- brokenLevel: lastSwingLow.price,
2210
- brokenSwingIndex: lastSwingLow.index,
2211
- direction: 'down',
2212
- })
2213
- trend = 'down'
2214
- lastSwingLow = null
2215
- }
2216
- }
2217
-
2218
- return { swings, events, trend }
2219
- }
2220
-
2221
- function isExtremum(
2222
- data: KLineData[],
2223
- i: number,
2224
- left: number,
2225
- right: number,
2226
- kind: 'high' | 'low',
2227
- ): boolean {
2228
- const n = data.length
2229
- if (i < left || i + right >= n) return false
2230
- const center = kind === 'high' ? data[i]!.high : data[i]!.low
2231
- for (let k = 1; k <= left; k++) {
2232
- const v = kind === 'high' ? data[i - k]!.high : data[i - k]!.low
2233
- if (kind === 'high' ? v >= center : v <= center) return false
2234
- }
2235
- for (let k = 1; k <= right; k++) {
2236
- const v = kind === 'high' ? data[i + k]!.high : data[i + k]!.low
2237
- if (kind === 'high' ? v >= center : v <= center) return false
2238
- }
2239
- return true
2240
- }
2241
-
2242
-
2243
-
2244
- // ============================================================================
2245
- // SMC Zones — FVG (Fair Value Gap) + Order Blocks
2246
- // FVG (3-bar pattern):
2247
- // Bullish FVG: bar[t-2].high < bar[t].low → gap zone [bar[t-2].high, bar[t].low] anchored at bar[t-1]
2248
- // Bearish FVG: bar[t-2].low > bar[t].high → gap zone [bar[t].high, bar[t-2].low] anchored at bar[t-1]
2249
- // Zone is "filled" (endIndex set) when price re-enters it.
2250
- // Order Blocks:
2251
- // Computed in conjunction with BOS events from calcStructureData.
2252
- // Bullish OB = last bearish candle (close < open) within obLookback bars before an upward BOS.
2253
- // Bearish OB = last bullish candle (close > open) within obLookback bars before a downward BOS.
2254
- // Mitigated (endIndex set) when price returns into the candle's range.
2255
- // ============================================================================
2256
-
2257
- export type ZoneKind = 'FVG_BULL' | 'FVG_BEAR' | 'OB_BULL' | 'OB_BEAR'
2258
-
2259
- export interface Zone {
2260
- kind: ZoneKind
2261
- startIndex: number
2262
- endIndex?: number
2263
- high: number
2264
- low: number
2265
- }
2266
-
2267
- const DEFAULT_ZONES_OB_LOOKBACK = 5
2268
-
2269
- export function calcZonesData(
2270
- data: KLineData[],
2271
- obLookback: number,
2272
- structureLeftWindow: number,
2273
- structureRightWindow: number,
2274
- breakoutSource: 'close' | 'wick',
2275
- ): Zone[] {
2276
- const n = data.length
2277
- if (n < 3) return []
2278
- const zones: Zone[] = []
2279
-
2280
- // 1. Detect FVGs
2281
- for (let t = 2; t < n; t++) {
2282
- const a = data[t - 2]!
2283
- const c = data[t]!
2284
- // Bullish FVG: a.high < c.low → gap
2285
- if (a.high < c.low) {
2286
- zones.push({
2287
- kind: 'FVG_BULL',
2288
- startIndex: t - 1,
2289
- high: c.low,
2290
- low: a.high,
2291
- })
2292
- }
2293
- // Bearish FVG: a.low > c.high → gap
2294
- if (a.low > c.high) {
2295
- zones.push({
2296
- kind: 'FVG_BEAR',
2297
- startIndex: t - 1,
2298
- high: a.low,
2299
- low: c.high,
2300
- })
2301
- }
2302
- }
2303
-
2304
- // 2. Detect Order Blocks using structure BOS events
2305
- const struct = calcStructureData(data, structureLeftWindow, structureRightWindow, breakoutSource)
2306
- for (const ev of struct.events) {
2307
- if (ev.kind !== 'BOS') continue
2308
- // Look back obLookback bars for the OB candle
2309
- const start = Math.max(0, ev.index - obLookback)
2310
- if (ev.direction === 'up') {
2311
- // Bullish OB: latest bearish candle (close < open) in [start, ev.index)
2312
- for (let k = ev.index - 1; k >= start; k--) {
2313
- const bar = data[k]!
2314
- if (bar.close < bar.open) {
2315
- zones.push({ kind: 'OB_BULL', startIndex: k, high: bar.high, low: bar.low })
2316
- break
2317
- }
2318
- }
2319
- } else {
2320
- // Bearish OB: latest bullish candle (close > open) in [start, ev.index)
2321
- for (let k = ev.index - 1; k >= start; k--) {
2322
- const bar = data[k]!
2323
- if (bar.close > bar.open) {
2324
- zones.push({ kind: 'OB_BEAR', startIndex: k, high: bar.high, low: bar.low })
2325
- break
2326
- }
2327
- }
2328
- }
2329
- }
2330
-
2331
- // 3. Mark zones as filled when price re-enters their range
2332
- for (const zone of zones) {
2333
- for (let t = zone.startIndex + 1; t < n; t++) {
2334
- const bar = data[t]!
2335
- // Zone is touched if the bar overlaps the zone's [low, high]
2336
- if (bar.low <= zone.high && bar.high >= zone.low) {
2337
- zone.endIndex = t
2338
- break
2339
- }
2340
- }
2341
- }
2342
-
2343
- return zones
2344
- }
2345
-
2346
-
2347
-
2348
- // ============================================================================
2349
- // Volume Profile — price-bin volume distribution
2350
- // For each bar, volume is distributed uniformly across [low, high] into bins.
2351
- // Outputs: bins[], POC (max-volume bin center), VAH/VAL (value area boundaries).
2352
- // Value area = contiguous bins around POC summing to valueAreaPercent of total V.
2353
- // ============================================================================
2354
-
2355
- export interface VolumeProfileBin {
2356
- priceLow: number
2357
- priceHigh: number
2358
- volume: number
2359
- }
2360
-
2361
- export interface VolumeProfileResult {
2362
- bins: VolumeProfileBin[]
2363
- poc: number
2364
- vah: number
2365
- val: number
2366
- totalVolume: number
2367
- }
2368
-
2369
- const DEFAULT_VP_BINS = 24
2370
- const DEFAULT_VP_LOOKBACK = 0
2371
- const DEFAULT_VP_VALUE_AREA = 0.7
2372
-
2373
- export function calcVolumeProfileData(
2374
- data: KLineData[],
2375
- bins: number,
2376
- lookback: number,
2377
- valueAreaPercent: number,
2378
- ): VolumeProfileResult {
2379
- const n = data.length
2380
- if (n === 0 || bins <= 0) {
2381
- return { bins: [], poc: 0, vah: 0, val: 0, totalVolume: 0 }
2382
- }
2383
-
2384
- const startIdx = lookback > 0 ? Math.max(0, n - lookback) : 0
2385
- let priceMin = Infinity
2386
- let priceMax = -Infinity
2387
- for (let i = startIdx; i < n; i++) {
2388
- const bar = data[i]!
2389
- if (bar.low < priceMin) priceMin = bar.low
2390
- if (bar.high > priceMax) priceMax = bar.high
2391
- }
2392
- if (!Number.isFinite(priceMin) || !Number.isFinite(priceMax) || priceMax <= priceMin) {
2393
- return { bins: [], poc: priceMin, vah: priceMin, val: priceMin, totalVolume: 0 }
2394
- }
2395
-
2396
- const binWidth = (priceMax - priceMin) / bins
2397
- const binVolumes: number[] = new Array(bins).fill(0)
2398
-
2399
- // Distribute each bar's volume uniformly across the bins its [low, high] covers
2400
- for (let i = startIdx; i < n; i++) {
2401
- const bar = data[i]!
2402
- const barRange = bar.high - bar.low
2403
- if (barRange <= 0) {
2404
- const binIdx = Math.min(bins - 1, Math.max(0, Math.floor((bar.close - priceMin) / binWidth)))
2405
- binVolumes[binIdx]! += bar.volume ?? 0
2406
- continue
2407
- }
2408
- const volPerPrice = (bar.volume ?? 0) / barRange
2409
- const startBin = Math.max(0, Math.floor((bar.low - priceMin) / binWidth))
2410
- const endBin = Math.min(bins - 1, Math.floor((bar.high - priceMin) / binWidth))
2411
- for (let b = startBin; b <= endBin; b++) {
2412
- const binLow = priceMin + b * binWidth
2413
- const binHigh = binLow + binWidth
2414
- const overlapLow = Math.max(bar.low, binLow)
2415
- const overlapHigh = Math.min(bar.high, binHigh)
2416
- const overlap = overlapHigh - overlapLow
2417
- if (overlap > 0) {
2418
- binVolumes[b]! += overlap * volPerPrice
2419
- }
2420
- }
2421
- }
2422
-
2423
- const binsArr: VolumeProfileBin[] = binVolumes.map((v, b) => ({
2424
- priceLow: priceMin + b * binWidth,
2425
- priceHigh: priceMin + (b + 1) * binWidth,
2426
- volume: v,
2427
- }))
2428
-
2429
- // POC = max-volume bin center
2430
- let pocBinIdx = 0
2431
- for (let b = 1; b < bins; b++) {
2432
- if (binVolumes[b]! > binVolumes[pocBinIdx]!) pocBinIdx = b
2433
- }
2434
- const poc = (binsArr[pocBinIdx]!.priceLow + binsArr[pocBinIdx]!.priceHigh) / 2
2435
-
2436
- const totalVolume = binVolumes.reduce((a, b) => a + b, 0)
2437
-
2438
- // Value Area: expand outward from POC until cumulative volume >= valueAreaPercent of total
2439
- const target = totalVolume * valueAreaPercent
2440
- let acc = binVolumes[pocBinIdx]!
2441
- let lo = pocBinIdx
2442
- let hi = pocBinIdx
2443
- while (acc < target && (lo > 0 || hi < bins - 1)) {
2444
- const loCand = lo > 0 ? binVolumes[lo - 1]! : -Infinity
2445
- const hiCand = hi < bins - 1 ? binVolumes[hi + 1]! : -Infinity
2446
- if (loCand >= hiCand && lo > 0) {
2447
- lo--
2448
- acc += binVolumes[lo]!
2449
- } else if (hi < bins - 1) {
2450
- hi++
2451
- acc += binVolumes[hi]!
2452
- } else {
2453
- break
2454
- }
2455
- }
2456
- const val = binsArr[lo]!.priceLow
2457
- const vah = binsArr[hi]!.priceHigh
2458
-
2459
- return { bins: binsArr, poc, vah, val, totalVolume }
2460
- }
1
+ export * from './calculators/index'
2461
2
 
2462
3