@363045841yyt/klinechart-core 0.8.11-alpha.0 → 0.8.12

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 (578) hide show
  1. package/dist/alerts/createAlertController.d.ts +30 -0
  2. package/dist/alerts/createAlertController.d.ts.map +1 -0
  3. package/dist/alerts/createAlertController.js +261 -0
  4. package/dist/alerts/createAlertController.js.map +1 -0
  5. package/dist/alerts/index.d.ts +5 -0
  6. package/dist/alerts/index.d.ts.map +1 -0
  7. package/dist/alerts/index.js +4 -0
  8. package/dist/alerts/index.js.map +1 -0
  9. package/dist/alerts/predicates.d.ts +28 -0
  10. package/dist/alerts/predicates.d.ts.map +1 -0
  11. package/dist/alerts/predicates.js +211 -0
  12. package/dist/alerts/predicates.js.map +1 -0
  13. package/dist/alerts/rollingVolume.d.ts +15 -0
  14. package/dist/alerts/rollingVolume.d.ts.map +1 -0
  15. package/dist/alerts/rollingVolume.js +42 -0
  16. package/dist/alerts/rollingVolume.js.map +1 -0
  17. package/dist/alerts/ruleSchema.d.ts +42 -0
  18. package/dist/alerts/ruleSchema.d.ts.map +1 -0
  19. package/dist/alerts/ruleSchema.js +240 -0
  20. package/dist/alerts/ruleSchema.js.map +1 -0
  21. package/dist/alerts/types.d.ts +169 -0
  22. package/dist/alerts/types.d.ts.map +1 -0
  23. package/dist/alerts/types.js +24 -0
  24. package/dist/alerts/types.js.map +1 -0
  25. package/dist/chartTypes/heikinAshi.d.ts +49 -0
  26. package/dist/chartTypes/heikinAshi.d.ts.map +1 -0
  27. package/dist/chartTypes/heikinAshi.js +94 -0
  28. package/dist/chartTypes/heikinAshi.js.map +1 -0
  29. package/dist/chartTypes/index.d.ts +6 -0
  30. package/dist/chartTypes/index.d.ts.map +1 -0
  31. package/dist/chartTypes/index.js +5 -0
  32. package/dist/chartTypes/index.js.map +1 -0
  33. package/dist/chartTypes/pointAndFigure.d.ts +83 -0
  34. package/dist/chartTypes/pointAndFigure.d.ts.map +1 -0
  35. package/dist/chartTypes/pointAndFigure.js +159 -0
  36. package/dist/chartTypes/pointAndFigure.js.map +1 -0
  37. package/dist/chartTypes/rangeBars.d.ts +61 -0
  38. package/dist/chartTypes/rangeBars.d.ts.map +1 -0
  39. package/dist/chartTypes/rangeBars.js +181 -0
  40. package/dist/chartTypes/rangeBars.js.map +1 -0
  41. package/dist/chartTypes/renko.d.ts +94 -0
  42. package/dist/chartTypes/renko.d.ts.map +1 -0
  43. package/dist/chartTypes/renko.js +207 -0
  44. package/dist/chartTypes/renko.js.map +1 -0
  45. package/dist/chartTypes/types.d.ts +97 -0
  46. package/dist/chartTypes/types.d.ts.map +1 -0
  47. package/dist/chartTypes/types.js +39 -0
  48. package/dist/chartTypes/types.js.map +1 -0
  49. package/dist/components/anchoredVwap/computeAnchoredVwap.d.ts +76 -0
  50. package/dist/components/anchoredVwap/computeAnchoredVwap.d.ts.map +1 -0
  51. package/dist/components/anchoredVwap/computeAnchoredVwap.js +155 -0
  52. package/dist/components/anchoredVwap/computeAnchoredVwap.js.map +1 -0
  53. package/dist/components/anchoredVwap/createAnchoredVwapController.d.ts +28 -0
  54. package/dist/components/anchoredVwap/createAnchoredVwapController.d.ts.map +1 -0
  55. package/dist/components/anchoredVwap/createAnchoredVwapController.js +282 -0
  56. package/dist/components/anchoredVwap/createAnchoredVwapController.js.map +1 -0
  57. package/dist/components/anchoredVwap/index.d.ts +11 -0
  58. package/dist/components/anchoredVwap/index.d.ts.map +1 -0
  59. package/dist/components/anchoredVwap/index.js +10 -0
  60. package/dist/components/anchoredVwap/index.js.map +1 -0
  61. package/dist/components/anchoredVwap/types.d.ts +171 -0
  62. package/dist/components/anchoredVwap/types.d.ts.map +1 -0
  63. package/dist/components/anchoredVwap/types.js +25 -0
  64. package/dist/components/anchoredVwap/types.js.map +1 -0
  65. package/dist/components/crosshairSync/createCrosshairSync.d.ts +94 -0
  66. package/dist/components/crosshairSync/createCrosshairSync.d.ts.map +1 -0
  67. package/dist/components/crosshairSync/createCrosshairSync.js +118 -0
  68. package/dist/components/crosshairSync/createCrosshairSync.js.map +1 -0
  69. package/dist/components/crosshairSync/index.d.ts +2 -0
  70. package/dist/components/crosshairSync/index.d.ts.map +1 -0
  71. package/dist/components/crosshairSync/index.js +2 -0
  72. package/dist/components/crosshairSync/index.js.map +1 -0
  73. package/dist/components/footprint/aggressor.d.ts +89 -0
  74. package/dist/components/footprint/aggressor.d.ts.map +1 -0
  75. package/dist/components/footprint/aggressor.js +106 -0
  76. package/dist/components/footprint/aggressor.js.map +1 -0
  77. package/dist/components/footprint/createFootprintController.d.ts +29 -0
  78. package/dist/components/footprint/createFootprintController.d.ts.map +1 -0
  79. package/dist/components/footprint/createFootprintController.js +264 -0
  80. package/dist/components/footprint/createFootprintController.js.map +1 -0
  81. package/dist/components/footprint/index.d.ts +7 -0
  82. package/dist/components/footprint/index.d.ts.map +1 -0
  83. package/dist/components/footprint/index.js +4 -0
  84. package/dist/components/footprint/index.js.map +1 -0
  85. package/dist/components/footprint/perBarStats.d.ts +63 -0
  86. package/dist/components/footprint/perBarStats.d.ts.map +1 -0
  87. package/dist/components/footprint/perBarStats.js +123 -0
  88. package/dist/components/footprint/perBarStats.js.map +1 -0
  89. package/dist/components/footprint/types.d.ts +197 -0
  90. package/dist/components/footprint/types.d.ts.map +1 -0
  91. package/dist/components/footprint/types.js +23 -0
  92. package/dist/components/footprint/types.js.map +1 -0
  93. package/dist/components/mtfOverlay/alignToBaseIndex.d.ts +45 -0
  94. package/dist/components/mtfOverlay/alignToBaseIndex.d.ts.map +1 -0
  95. package/dist/components/mtfOverlay/alignToBaseIndex.js +92 -0
  96. package/dist/components/mtfOverlay/alignToBaseIndex.js.map +1 -0
  97. package/dist/components/mtfOverlay/createMtfController.d.ts +7 -0
  98. package/dist/components/mtfOverlay/createMtfController.d.ts.map +1 -0
  99. package/dist/components/mtfOverlay/createMtfController.js +134 -0
  100. package/dist/components/mtfOverlay/createMtfController.js.map +1 -0
  101. package/dist/components/mtfOverlay/index.d.ts +19 -0
  102. package/dist/components/mtfOverlay/index.d.ts.map +1 -0
  103. package/dist/components/mtfOverlay/index.js +18 -0
  104. package/dist/components/mtfOverlay/index.js.map +1 -0
  105. package/dist/components/mtfOverlay/resampleBars.d.ts +37 -0
  106. package/dist/components/mtfOverlay/resampleBars.d.ts.map +1 -0
  107. package/dist/components/mtfOverlay/resampleBars.js +93 -0
  108. package/dist/components/mtfOverlay/resampleBars.js.map +1 -0
  109. package/dist/components/mtfOverlay/types.d.ts +132 -0
  110. package/dist/components/mtfOverlay/types.d.ts.map +1 -0
  111. package/dist/components/mtfOverlay/types.js +22 -0
  112. package/dist/components/mtfOverlay/types.js.map +1 -0
  113. package/dist/components/orderBookHeatmap/createHeatmapController.d.ts +21 -0
  114. package/dist/components/orderBookHeatmap/createHeatmapController.d.ts.map +1 -0
  115. package/dist/components/orderBookHeatmap/createHeatmapController.js +234 -0
  116. package/dist/components/orderBookHeatmap/createHeatmapController.js.map +1 -0
  117. package/dist/components/orderBookHeatmap/createOrderBookState.d.ts +18 -0
  118. package/dist/components/orderBookHeatmap/createOrderBookState.d.ts.map +1 -0
  119. package/dist/components/orderBookHeatmap/createOrderBookState.js +102 -0
  120. package/dist/components/orderBookHeatmap/createOrderBookState.js.map +1 -0
  121. package/dist/components/orderBookHeatmap/deltaArchive.d.ts +25 -0
  122. package/dist/components/orderBookHeatmap/deltaArchive.d.ts.map +1 -0
  123. package/dist/components/orderBookHeatmap/deltaArchive.js +106 -0
  124. package/dist/components/orderBookHeatmap/deltaArchive.js.map +1 -0
  125. package/dist/components/orderBookHeatmap/index.d.ts +13 -0
  126. package/dist/components/orderBookHeatmap/index.d.ts.map +1 -0
  127. package/dist/components/orderBookHeatmap/index.js +12 -0
  128. package/dist/components/orderBookHeatmap/index.js.map +1 -0
  129. package/dist/components/orderBookHeatmap/logColorScale.d.ts +20 -0
  130. package/dist/components/orderBookHeatmap/logColorScale.d.ts.map +1 -0
  131. package/dist/components/orderBookHeatmap/logColorScale.js +71 -0
  132. package/dist/components/orderBookHeatmap/logColorScale.js.map +1 -0
  133. package/dist/components/orderBookHeatmap/snapshotRing.d.ts +16 -0
  134. package/dist/components/orderBookHeatmap/snapshotRing.d.ts.map +1 -0
  135. package/dist/components/orderBookHeatmap/snapshotRing.js +61 -0
  136. package/dist/components/orderBookHeatmap/snapshotRing.js.map +1 -0
  137. package/dist/components/orderBookHeatmap/types.d.ts +128 -0
  138. package/dist/components/orderBookHeatmap/types.d.ts.map +1 -0
  139. package/dist/components/orderBookHeatmap/types.js +16 -0
  140. package/dist/components/orderBookHeatmap/types.js.map +1 -0
  141. package/dist/components/volumeProfile/binning.d.ts +54 -0
  142. package/dist/components/volumeProfile/binning.d.ts.map +1 -0
  143. package/dist/components/volumeProfile/binning.js +123 -0
  144. package/dist/components/volumeProfile/binning.js.map +1 -0
  145. package/dist/components/volumeProfile/createVolumeProfileController.d.ts +31 -0
  146. package/dist/components/volumeProfile/createVolumeProfileController.d.ts.map +1 -0
  147. package/dist/components/volumeProfile/createVolumeProfileController.js +179 -0
  148. package/dist/components/volumeProfile/createVolumeProfileController.js.map +1 -0
  149. package/dist/components/volumeProfile/index.d.ts +6 -0
  150. package/dist/components/volumeProfile/index.d.ts.map +1 -0
  151. package/dist/components/volumeProfile/index.js +5 -0
  152. package/dist/components/volumeProfile/index.js.map +1 -0
  153. package/dist/components/volumeProfile/poc.d.ts +35 -0
  154. package/dist/components/volumeProfile/poc.d.ts.map +1 -0
  155. package/dist/components/volumeProfile/poc.js +51 -0
  156. package/dist/components/volumeProfile/poc.js.map +1 -0
  157. package/dist/components/volumeProfile/types.d.ts +138 -0
  158. package/dist/components/volumeProfile/types.d.ts.map +1 -0
  159. package/dist/components/volumeProfile/types.js +19 -0
  160. package/dist/components/volumeProfile/types.js.map +1 -0
  161. package/dist/components/volumeProfile/valueArea.d.ts +58 -0
  162. package/dist/components/volumeProfile/valueArea.d.ts.map +1 -0
  163. package/dist/components/volumeProfile/valueArea.js +172 -0
  164. package/dist/components/volumeProfile/valueArea.js.map +1 -0
  165. package/dist/controllers/createChartController.d.ts.map +1 -1
  166. package/dist/controllers/createChartController.js +22 -11
  167. package/dist/controllers/createChartController.js.map +1 -1
  168. package/dist/controllers/types.d.ts +3 -1
  169. package/dist/controllers/types.d.ts.map +1 -1
  170. package/dist/data-fetchers/baostock.d.ts.map +1 -1
  171. package/dist/data-fetchers/baostock.js +2 -1
  172. package/dist/data-fetchers/baostock.js.map +1 -1
  173. package/dist/data-fetchers/dataBuffer.effects.d.ts.map +1 -1
  174. package/dist/data-fetchers/dataBuffer.effects.js +2 -1
  175. package/dist/data-fetchers/dataBuffer.effects.js.map +1 -1
  176. package/dist/data-fetchers/fetcherDefinitionRegistry.d.ts.map +1 -1
  177. package/dist/data-fetchers/fetcherDefinitionRegistry.js +2 -1
  178. package/dist/data-fetchers/fetcherDefinitionRegistry.js.map +1 -1
  179. package/dist/data-fetchers/gotdx.js +4 -3
  180. package/dist/data-fetchers/gotdx.js.map +1 -1
  181. package/dist/data-fetchers/router.d.ts.map +1 -1
  182. package/dist/data-fetchers/router.js +4 -3
  183. package/dist/data-fetchers/router.js.map +1 -1
  184. package/dist/data-fetchers/tradingview.js +3 -2
  185. package/dist/data-fetchers/tradingview.js.map +1 -1
  186. package/dist/engine/chart.d.ts +13 -0
  187. package/dist/engine/chart.d.ts.map +1 -1
  188. package/dist/engine/chart.js +109 -0
  189. package/dist/engine/chart.js.map +1 -1
  190. package/dist/engine/data/chartDataManager.d.ts +1 -0
  191. package/dist/engine/data/chartDataManager.d.ts.map +1 -1
  192. package/dist/engine/data/chartDataManager.js +27 -4
  193. package/dist/engine/data/chartDataManager.js.map +1 -1
  194. package/dist/engine/drawing/toolConfig.d.ts.map +1 -1
  195. package/dist/engine/drawing/toolConfig.js +2 -1
  196. package/dist/engine/drawing/toolConfig.js.map +1 -1
  197. package/dist/engine/indicators/chartIndicatorManager.d.ts.map +1 -1
  198. package/dist/engine/indicators/chartIndicatorManager.js +2 -1
  199. package/dist/engine/indicators/chartIndicatorManager.js.map +1 -1
  200. package/dist/engine/indicators/indicatorDefinitionRegistry.d.ts.map +1 -1
  201. package/dist/engine/indicators/indicatorDefinitionRegistry.js +2 -1
  202. package/dist/engine/indicators/indicatorDefinitionRegistry.js.map +1 -1
  203. package/dist/engine/indicators/indicatorMetadata.d.ts.map +1 -1
  204. package/dist/engine/indicators/indicatorMetadata.js +2 -1
  205. package/dist/engine/indicators/indicatorMetadata.js.map +1 -1
  206. package/dist/engine/indicators/indicatorRegistry.d.ts.map +1 -1
  207. package/dist/engine/indicators/indicatorRegistry.js +6 -5
  208. package/dist/engine/indicators/indicatorRegistry.js.map +1 -1
  209. package/dist/engine/indicators/registerBuiltins.d.ts.map +1 -1
  210. package/dist/engine/indicators/registerBuiltins.js +2 -1
  211. package/dist/engine/indicators/registerBuiltins.js.map +1 -1
  212. package/dist/engine/indicators/scheduler.d.ts +6 -0
  213. package/dist/engine/indicators/scheduler.d.ts.map +1 -1
  214. package/dist/engine/indicators/scheduler.js +12 -0
  215. package/dist/engine/indicators/scheduler.js.map +1 -1
  216. package/dist/engine/indicators/soa.d.ts.map +1 -1
  217. package/dist/engine/indicators/soa.js +2 -1
  218. package/dist/engine/indicators/soa.js.map +1 -1
  219. package/dist/engine/indicators/stateComposer.d.ts.map +1 -1
  220. package/dist/engine/indicators/stateComposer.js +2 -1
  221. package/dist/engine/indicators/stateComposer.js.map +1 -1
  222. package/dist/engine/subPaneManager.d.ts.map +1 -1
  223. package/dist/engine/subPaneManager.js +2 -1
  224. package/dist/engine/subPaneManager.js.map +1 -1
  225. package/dist/engine/utils/chartZoomController.d.ts +2 -0
  226. package/dist/engine/utils/chartZoomController.d.ts.map +1 -1
  227. package/dist/engine/utils/chartZoomController.js +5 -1
  228. package/dist/engine/utils/chartZoomController.js.map +1 -1
  229. package/dist/errors-help.d.ts +72 -0
  230. package/dist/errors-help.d.ts.map +1 -0
  231. package/dist/errors-help.js +149 -0
  232. package/dist/errors-help.js.map +1 -0
  233. package/dist/errors.d.ts +64 -0
  234. package/dist/errors.d.ts.map +1 -0
  235. package/dist/errors.js +61 -0
  236. package/dist/errors.js.map +1 -0
  237. package/dist/index.d.ts +18 -0
  238. package/dist/index.d.ts.map +1 -1
  239. package/dist/index.js +22 -0
  240. package/dist/index.js.map +1 -1
  241. package/dist/indicators/alma.d.ts +27 -0
  242. package/dist/indicators/alma.d.ts.map +1 -0
  243. package/dist/indicators/alma.js +36 -0
  244. package/dist/indicators/alma.js.map +1 -0
  245. package/dist/indicators/awesomeOscillator.d.ts +19 -0
  246. package/dist/indicators/awesomeOscillator.d.ts.map +1 -0
  247. package/dist/indicators/awesomeOscillator.js +36 -0
  248. package/dist/indicators/awesomeOscillator.js.map +1 -0
  249. package/dist/indicators/dpo.d.ts +17 -0
  250. package/dist/indicators/dpo.d.ts.map +1 -0
  251. package/dist/indicators/dpo.js +24 -0
  252. package/dist/indicators/dpo.js.map +1 -0
  253. package/dist/indicators/fisherTransform.d.ts +26 -0
  254. package/dist/indicators/fisherTransform.d.ts.map +1 -0
  255. package/dist/indicators/fisherTransform.js +44 -0
  256. package/dist/indicators/fisherTransform.js.map +1 -0
  257. package/dist/indicators/frama.d.ts +25 -0
  258. package/dist/indicators/frama.d.ts.map +1 -0
  259. package/dist/indicators/frama.js +61 -0
  260. package/dist/indicators/frama.js.map +1 -0
  261. package/dist/indicators/index.d.ts +24 -0
  262. package/dist/indicators/index.d.ts.map +1 -0
  263. package/dist/indicators/index.js +26 -0
  264. package/dist/indicators/index.js.map +1 -0
  265. package/dist/indicators/lsma.d.ts +21 -0
  266. package/dist/indicators/lsma.d.ts.map +1 -0
  267. package/dist/indicators/lsma.js +37 -0
  268. package/dist/indicators/lsma.js.map +1 -0
  269. package/dist/indicators/schaffTrendCycle.d.ts +24 -0
  270. package/dist/indicators/schaffTrendCycle.d.ts.map +1 -0
  271. package/dist/indicators/schaffTrendCycle.js +70 -0
  272. package/dist/indicators/schaffTrendCycle.js.map +1 -0
  273. package/dist/indicators/stochRSI.d.ts +24 -0
  274. package/dist/indicators/stochRSI.d.ts.map +1 -0
  275. package/dist/indicators/stochRSI.js +86 -0
  276. package/dist/indicators/stochRSI.js.map +1 -0
  277. package/dist/indicators/t3.d.ts +31 -0
  278. package/dist/indicators/t3.d.ts.map +1 -0
  279. package/dist/indicators/t3.js +63 -0
  280. package/dist/indicators/t3.js.map +1 -0
  281. package/dist/indicators/ultimateOscillator.d.ts +26 -0
  282. package/dist/indicators/ultimateOscillator.d.ts.map +1 -0
  283. package/dist/indicators/ultimateOscillator.js +59 -0
  284. package/dist/indicators/ultimateOscillator.js.map +1 -0
  285. package/dist/indicators/vidya.d.ts +24 -0
  286. package/dist/indicators/vidya.d.ts.map +1 -0
  287. package/dist/indicators/vidya.js +54 -0
  288. package/dist/indicators/vidya.js.map +1 -0
  289. package/dist/indicators/zlema.d.ts +16 -0
  290. package/dist/indicators/zlema.d.ts.map +1 -0
  291. package/dist/indicators/zlema.js +26 -0
  292. package/dist/indicators/zlema.js.map +1 -0
  293. package/dist/input/gesture.d.ts +125 -0
  294. package/dist/input/gesture.d.ts.map +1 -0
  295. package/dist/input/gesture.js +249 -0
  296. package/dist/input/gesture.js.map +1 -0
  297. package/dist/input/index.d.ts +9 -0
  298. package/dist/input/index.d.ts.map +1 -0
  299. package/dist/input/index.js +9 -0
  300. package/dist/input/index.js.map +1 -0
  301. package/dist/input/keyboard.d.ts +140 -0
  302. package/dist/input/keyboard.d.ts.map +1 -0
  303. package/dist/input/keyboard.js +260 -0
  304. package/dist/input/keyboard.js.map +1 -0
  305. package/dist/mcp/chartBridge.d.ts +2 -2
  306. package/dist/mcp/chartBridge.d.ts.map +1 -1
  307. package/dist/mcp/chartBridge.js +27 -21
  308. package/dist/mcp/chartBridge.js.map +1 -1
  309. package/dist/plugin/PluginHost.d.ts +0 -3
  310. package/dist/plugin/PluginHost.d.ts.map +1 -1
  311. package/dist/plugin/PluginHost.js +7 -3
  312. package/dist/plugin/PluginHost.js.map +1 -1
  313. package/dist/plugin/PluginRegistry.d.ts +0 -3
  314. package/dist/plugin/PluginRegistry.d.ts.map +1 -1
  315. package/dist/plugin/PluginRegistry.js +5 -1
  316. package/dist/plugin/PluginRegistry.js.map +1 -1
  317. package/dist/render/Renderer.d.ts +116 -0
  318. package/dist/render/Renderer.d.ts.map +1 -0
  319. package/dist/render/Renderer.js +31 -0
  320. package/dist/render/Renderer.js.map +1 -0
  321. package/dist/render/SurfaceBackend.d.ts +83 -0
  322. package/dist/render/SurfaceBackend.d.ts.map +1 -0
  323. package/dist/render/SurfaceBackend.js +27 -0
  324. package/dist/render/SurfaceBackend.js.map +1 -0
  325. package/dist/render/index.d.ts +12 -0
  326. package/dist/render/index.d.ts.map +1 -0
  327. package/dist/render/index.js +11 -0
  328. package/dist/render/index.js.map +1 -0
  329. package/dist/renderer-tier/detectRendererTier.d.ts +57 -0
  330. package/dist/renderer-tier/detectRendererTier.d.ts.map +1 -0
  331. package/dist/renderer-tier/detectRendererTier.js +143 -0
  332. package/dist/renderer-tier/detectRendererTier.js.map +1 -0
  333. package/dist/renderer-tier/index.d.ts +12 -0
  334. package/dist/renderer-tier/index.d.ts.map +1 -0
  335. package/dist/renderer-tier/index.js +12 -0
  336. package/dist/renderer-tier/index.js.map +1 -0
  337. package/dist/renderer-tier/selectBackend.d.ts +106 -0
  338. package/dist/renderer-tier/selectBackend.d.ts.map +1 -0
  339. package/dist/renderer-tier/selectBackend.js +113 -0
  340. package/dist/renderer-tier/selectBackend.js.map +1 -0
  341. package/dist/renderer-tier/types.d.ts +50 -0
  342. package/dist/renderer-tier/types.d.ts.map +1 -0
  343. package/dist/renderer-tier/types.js +24 -0
  344. package/dist/renderer-tier/types.js.map +1 -0
  345. package/dist/replay/createReplayController.d.ts +3 -0
  346. package/dist/replay/createReplayController.d.ts.map +1 -0
  347. package/dist/replay/createReplayController.js +251 -0
  348. package/dist/replay/createReplayController.js.map +1 -0
  349. package/dist/replay/index.d.ts +5 -0
  350. package/dist/replay/index.d.ts.map +1 -0
  351. package/dist/replay/index.js +3 -0
  352. package/dist/replay/index.js.map +1 -0
  353. package/dist/replay/timeline.d.ts +50 -0
  354. package/dist/replay/timeline.d.ts.map +1 -0
  355. package/dist/replay/timeline.js +104 -0
  356. package/dist/replay/timeline.js.map +1 -0
  357. package/dist/replay/types.d.ts +94 -0
  358. package/dist/replay/types.d.ts.map +1 -0
  359. package/dist/replay/types.js +21 -0
  360. package/dist/replay/types.js.map +1 -0
  361. package/dist/scale/anchoredZoom.d.ts +64 -0
  362. package/dist/scale/anchoredZoom.d.ts.map +1 -0
  363. package/dist/scale/anchoredZoom.js +67 -0
  364. package/dist/scale/anchoredZoom.js.map +1 -0
  365. package/dist/scale/createPriceScale.d.ts +49 -0
  366. package/dist/scale/createPriceScale.d.ts.map +1 -0
  367. package/dist/scale/createPriceScale.js +175 -0
  368. package/dist/scale/createPriceScale.js.map +1 -0
  369. package/dist/scale/createTimeScale.d.ts +27 -0
  370. package/dist/scale/createTimeScale.d.ts.map +1 -0
  371. package/dist/scale/createTimeScale.js +139 -0
  372. package/dist/scale/createTimeScale.js.map +1 -0
  373. package/dist/scale/index.d.ts +6 -0
  374. package/dist/scale/index.d.ts.map +1 -0
  375. package/dist/scale/index.js +5 -0
  376. package/dist/scale/index.js.map +1 -0
  377. package/dist/scale/originShift.d.ts +69 -0
  378. package/dist/scale/originShift.d.ts.map +1 -0
  379. package/dist/scale/originShift.js +41 -0
  380. package/dist/scale/originShift.js.map +1 -0
  381. package/dist/scale/types.d.ts +90 -0
  382. package/dist/scale/types.d.ts.map +1 -0
  383. package/dist/scale/types.js +17 -0
  384. package/dist/scale/types.js.map +1 -0
  385. package/dist/scene/createScene.d.ts +22 -0
  386. package/dist/scene/createScene.d.ts.map +1 -0
  387. package/dist/scene/createScene.js +114 -0
  388. package/dist/scene/createScene.js.map +1 -0
  389. package/dist/scene/index.d.ts +13 -0
  390. package/dist/scene/index.d.ts.map +1 -0
  391. package/dist/scene/index.js +11 -0
  392. package/dist/scene/index.js.map +1 -0
  393. package/dist/scene/layerRegistry.d.ts +83 -0
  394. package/dist/scene/layerRegistry.d.ts.map +1 -0
  395. package/dist/scene/layerRegistry.js +43 -0
  396. package/dist/scene/layerRegistry.js.map +1 -0
  397. package/dist/scene/types.d.ts +128 -0
  398. package/dist/scene/types.d.ts.map +1 -0
  399. package/dist/scene/types.js +21 -0
  400. package/dist/scene/types.js.map +1 -0
  401. package/dist/scheduler/createFrameBudget.d.ts +91 -0
  402. package/dist/scheduler/createFrameBudget.d.ts.map +1 -0
  403. package/dist/scheduler/createFrameBudget.js +232 -0
  404. package/dist/scheduler/createFrameBudget.js.map +1 -0
  405. package/dist/scheduler/index.d.ts +2 -0
  406. package/dist/scheduler/index.d.ts.map +1 -0
  407. package/dist/scheduler/index.js +2 -0
  408. package/dist/scheduler/index.js.map +1 -0
  409. package/dist/tokens/themeToCssVars.d.ts.map +1 -1
  410. package/dist/tokens/themeToCssVars.js +2 -28
  411. package/dist/tokens/themeToCssVars.js.map +1 -1
  412. package/dist/version.d.ts +1 -1
  413. package/dist/version.d.ts.map +1 -1
  414. package/dist/version.js +1 -1
  415. package/dist/version.js.map +1 -1
  416. package/package.json +1 -1
  417. package/src/__bench__/indicators.bench.ts +215 -0
  418. package/src/__bench__/orderBookHeatmap.bench.ts +68 -0
  419. package/src/__bench__/scale.bench.ts +80 -0
  420. package/src/__bench__/signal.bench.ts +41 -0
  421. package/src/__bench__/volumeProfile.bench.ts +66 -0
  422. package/src/__tests__/errors-help.test.ts +184 -0
  423. package/src/__tests__/errors.test.ts +187 -0
  424. package/src/alerts/__tests__/controller.test.ts +231 -0
  425. package/src/alerts/__tests__/predicates.test.ts +374 -0
  426. package/src/alerts/__tests__/ruleSchema.test.ts +180 -0
  427. package/src/alerts/createAlertController.ts +312 -0
  428. package/src/alerts/index.ts +18 -0
  429. package/src/alerts/predicates.ts +231 -0
  430. package/src/alerts/rollingVolume.ts +51 -0
  431. package/src/alerts/ruleSchema.ts +278 -0
  432. package/src/alerts/types.ts +177 -0
  433. package/src/chartTypes/__tests__/heikinAshi.test.ts +122 -0
  434. package/src/chartTypes/__tests__/pointAndFigure.test.ts +167 -0
  435. package/src/chartTypes/__tests__/rangeBars.test.ts +146 -0
  436. package/src/chartTypes/__tests__/renko.test.ts +160 -0
  437. package/src/chartTypes/heikinAshi.ts +116 -0
  438. package/src/chartTypes/index.ts +5 -0
  439. package/src/chartTypes/pointAndFigure.ts +278 -0
  440. package/src/chartTypes/rangeBars.ts +303 -0
  441. package/src/chartTypes/renko.ts +311 -0
  442. package/src/chartTypes/types.ts +99 -0
  443. package/src/components/anchoredVwap/__tests__/computeAnchoredVwap.test.ts +331 -0
  444. package/src/components/anchoredVwap/__tests__/controller.test.ts +430 -0
  445. package/src/components/anchoredVwap/computeAnchoredVwap.ts +174 -0
  446. package/src/components/anchoredVwap/createAnchoredVwapController.ts +358 -0
  447. package/src/components/anchoredVwap/index.ts +17 -0
  448. package/src/components/anchoredVwap/types.ts +187 -0
  449. package/src/components/crosshairSync/__tests__/crosshairSync.test.ts +261 -0
  450. package/src/components/crosshairSync/createCrosshairSync.ts +187 -0
  451. package/src/components/crosshairSync/index.ts +5 -0
  452. package/src/components/footprint/__tests__/aggressor.test.ts +127 -0
  453. package/src/components/footprint/__tests__/controller.test.ts +130 -0
  454. package/src/components/footprint/__tests__/perBarStats.test.ts +114 -0
  455. package/src/components/footprint/aggressor.ts +165 -0
  456. package/src/components/footprint/createFootprintController.ts +338 -0
  457. package/src/components/footprint/index.ts +21 -0
  458. package/src/components/footprint/perBarStats.ts +137 -0
  459. package/src/components/footprint/types.ts +232 -0
  460. package/src/components/mtfOverlay/__tests__/alignToBaseIndex.test.ts +103 -0
  461. package/src/components/mtfOverlay/__tests__/controller.test.ts +172 -0
  462. package/src/components/mtfOverlay/__tests__/resampleBars.test.ts +106 -0
  463. package/src/components/mtfOverlay/alignToBaseIndex.ts +108 -0
  464. package/src/components/mtfOverlay/createMtfController.ts +180 -0
  465. package/src/components/mtfOverlay/index.ts +26 -0
  466. package/src/components/mtfOverlay/resampleBars.ts +134 -0
  467. package/src/components/mtfOverlay/types.ts +148 -0
  468. package/src/components/orderBookHeatmap/__tests__/controller.test.ts +237 -0
  469. package/src/components/orderBookHeatmap/__tests__/deltaArchive.test.ts +88 -0
  470. package/src/components/orderBookHeatmap/__tests__/logColorScale.test.ts +69 -0
  471. package/src/components/orderBookHeatmap/__tests__/orderBookState.test.ts +113 -0
  472. package/src/components/orderBookHeatmap/__tests__/snapshotRing.test.ts +53 -0
  473. package/src/components/orderBookHeatmap/computeShader.wgsl.md +130 -0
  474. package/src/components/orderBookHeatmap/createHeatmapController.ts +273 -0
  475. package/src/components/orderBookHeatmap/createOrderBookState.ts +116 -0
  476. package/src/components/orderBookHeatmap/deltaArchive.ts +113 -0
  477. package/src/components/orderBookHeatmap/index.ts +25 -0
  478. package/src/components/orderBookHeatmap/logColorScale.ts +77 -0
  479. package/src/components/orderBookHeatmap/snapshotRing.ts +65 -0
  480. package/src/components/orderBookHeatmap/types.ts +168 -0
  481. package/src/components/volumeProfile/__tests__/binning.test.ts +133 -0
  482. package/src/components/volumeProfile/__tests__/poc.test.ts +44 -0
  483. package/src/components/volumeProfile/__tests__/valueArea.test.ts +177 -0
  484. package/src/components/volumeProfile/binning.ts +133 -0
  485. package/src/components/volumeProfile/createVolumeProfileController.ts +213 -0
  486. package/src/components/volumeProfile/index.ts +12 -0
  487. package/src/components/volumeProfile/poc.ts +53 -0
  488. package/src/components/volumeProfile/types.ts +148 -0
  489. package/src/components/volumeProfile/valueArea.ts +187 -0
  490. package/src/controllers/createChartController.ts +23 -10
  491. package/src/controllers/types.ts +5 -1
  492. package/src/data-fetchers/baostock.ts +2 -1
  493. package/src/data-fetchers/dataBuffer.effects.ts +3 -1
  494. package/src/data-fetchers/fetcherDefinitionRegistry.ts +2 -1
  495. package/src/data-fetchers/gotdx.ts +4 -3
  496. package/src/data-fetchers/router.ts +6 -3
  497. package/src/data-fetchers/tradingview.ts +3 -2
  498. package/src/engine/chart.ts +126 -3
  499. package/src/engine/data/chartDataManager.ts +26 -4
  500. package/src/engine/drawing/toolConfig.ts +2 -1
  501. package/src/engine/indicators/chartIndicatorManager.ts +2 -1
  502. package/src/engine/indicators/indicatorDefinitionRegistry.ts +2 -1
  503. package/src/engine/indicators/indicatorMetadata.ts +2 -1
  504. package/src/engine/indicators/indicatorRegistry.ts +6 -5
  505. package/src/engine/indicators/registerBuiltins.ts +2 -1
  506. package/src/engine/indicators/scheduler.ts +15 -0
  507. package/src/engine/indicators/soa.ts +2 -1
  508. package/src/engine/indicators/stateComposer.ts +2 -1
  509. package/src/engine/subPaneManager.ts +2 -1
  510. package/src/engine/utils/chartZoomController.ts +7 -1
  511. package/src/errors-help.ts +218 -0
  512. package/src/errors.ts +128 -0
  513. package/src/index.ts +37 -0
  514. package/src/indicators/__tests__/maFamily.test.ts +159 -0
  515. package/src/indicators/__tests__/oscillators.test.ts +150 -0
  516. package/src/indicators/alma.ts +61 -0
  517. package/src/indicators/awesomeOscillator.ts +54 -0
  518. package/src/indicators/dpo.ts +37 -0
  519. package/src/indicators/fisherTransform.ts +63 -0
  520. package/src/indicators/frama.ts +87 -0
  521. package/src/indicators/index.ts +39 -0
  522. package/src/indicators/lsma.ts +58 -0
  523. package/src/indicators/schaffTrendCycle.ts +92 -0
  524. package/src/indicators/stochRSI.ts +108 -0
  525. package/src/indicators/t3.ts +96 -0
  526. package/src/indicators/ultimateOscillator.ts +83 -0
  527. package/src/indicators/vidya.ts +80 -0
  528. package/src/indicators/zlema.ts +41 -0
  529. package/src/input/__tests__/gesture.test.ts +271 -0
  530. package/src/input/__tests__/keyboard.test.ts +317 -0
  531. package/src/input/gesture.ts +357 -0
  532. package/src/input/index.ts +26 -0
  533. package/src/input/keyboard.ts +373 -0
  534. package/src/mcp/chartBridge.ts +98 -72
  535. package/src/plugin/PluginHost.ts +4 -3
  536. package/src/plugin/PluginRegistry.ts +2 -1
  537. package/src/render/Renderer.ts +127 -0
  538. package/src/render/SurfaceBackend.ts +94 -0
  539. package/src/render/__tests__/contract.test.ts +176 -0
  540. package/src/render/index.ts +27 -0
  541. package/src/renderer-tier/__tests__/detectRendererTier.test.ts +180 -0
  542. package/src/renderer-tier/__tests__/selectBackend.test.ts +253 -0
  543. package/src/renderer-tier/detectRendererTier.ts +168 -0
  544. package/src/renderer-tier/index.ts +30 -0
  545. package/src/renderer-tier/selectBackend.ts +201 -0
  546. package/src/renderer-tier/types.ts +59 -0
  547. package/src/replay/__tests__/controller.test.ts +309 -0
  548. package/src/replay/__tests__/timeline.test.ts +99 -0
  549. package/src/replay/createReplayController.ts +302 -0
  550. package/src/replay/index.ts +15 -0
  551. package/src/replay/timeline.ts +106 -0
  552. package/src/replay/types.ts +107 -0
  553. package/src/scale/__tests__/anchoredZoom.test.ts +125 -0
  554. package/src/scale/__tests__/originShift.test.ts +97 -0
  555. package/src/scale/__tests__/priceScale.test.ts +219 -0
  556. package/src/scale/__tests__/timeScale.test.ts +119 -0
  557. package/src/scale/anchoredZoom.ts +104 -0
  558. package/src/scale/createPriceScale.ts +226 -0
  559. package/src/scale/createTimeScale.ts +150 -0
  560. package/src/scale/index.ts +9 -0
  561. package/src/scale/originShift.ts +111 -0
  562. package/src/scale/types.ts +100 -0
  563. package/src/scene/__tests__/layerRegistry.test.ts +207 -0
  564. package/src/scene/__tests__/scene.test.ts +411 -0
  565. package/src/scene/createScene.ts +117 -0
  566. package/src/scene/index.ts +29 -0
  567. package/src/scene/layerRegistry.ts +117 -0
  568. package/src/scene/types.ts +143 -0
  569. package/src/scheduler/__tests__/createFrameBudget.test.ts +315 -0
  570. package/src/scheduler/createFrameBudget.ts +316 -0
  571. package/src/scheduler/index.ts +7 -0
  572. package/src/tokens/themeToCssVars.ts +3 -1
  573. package/src/version.ts +1 -1
  574. package/dist/data-fetchers/gotdx/gotdx.d.ts +0 -7
  575. package/dist/data-fetchers/gotdx/gotdx.d.ts.map +0 -1
  576. package/dist/data-fetchers/gotdx/gotdx.js +0 -7
  577. package/dist/data-fetchers/gotdx/gotdx.js.map +0 -1
  578. package/src/data-fetchers/gotdx/gotdx.ts +0 -6
@@ -0,0 +1,261 @@
1
+ /**
2
+ * Tests for `createCrosshairSync`.
3
+ *
4
+ * Coverage:
5
+ * 1. Single-pane register + paneCount.
6
+ * 2. Multi-pane register: paneCount tracks correctly.
7
+ * 3. Idempotent register (re-registering same id is a no-op).
8
+ * 4. unregister removes + drops position when it owns the source.
9
+ * 5. unregister of unknown id is a no-op.
10
+ * 6. move emits position with index + source.
11
+ * 7. move from unregistered pane throws KLineChartError(NOT_REGISTERED).
12
+ * 8. move with non-finite index throws INVALID_PARAM.
13
+ * 9. Index normalisation: fractional inputs round to integer.
14
+ * 10. Repeat move with same (index, source) is coalesced.
15
+ * 11. clear() resets position to null.
16
+ * 12. clear() when already null doesn't notify.
17
+ * 13. reset() empties registry + position.
18
+ * 14. dispose() turns everything into a no-op.
19
+ * 15. Loop-prevention pattern: subscribers can identify own emissions.
20
+ */
21
+
22
+ import { describe, it, expect } from 'vitest'
23
+
24
+ import { createCrosshairSync, type CrosshairPosition } from '..'
25
+ import { isKLineChartError } from '../../../errors'
26
+
27
+ function collect(s: ReturnType<typeof createCrosshairSync>): CrosshairPosition[] {
28
+ const out: CrosshairPosition[] = []
29
+ s.position.subscribe(() => {
30
+ const p = s.position.peek()
31
+ if (p !== null) out.push(p)
32
+ })
33
+ return out
34
+ }
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Registration
38
+ // ---------------------------------------------------------------------------
39
+
40
+ describe('register', () => {
41
+ it('starts with paneCount = 0 and position null', () => {
42
+ const s = createCrosshairSync()
43
+ expect(s.paneCount.peek()).toBe(0)
44
+ expect(s.position.peek()).toBeNull()
45
+ })
46
+
47
+ it('register increments paneCount', () => {
48
+ const s = createCrosshairSync()
49
+ s.register('a')
50
+ s.register('b')
51
+ s.register('c')
52
+ expect(s.paneCount.peek()).toBe(3)
53
+ })
54
+
55
+ it('re-registering same id is a no-op', () => {
56
+ const s = createCrosshairSync()
57
+ s.register('a')
58
+ s.register('a')
59
+ expect(s.paneCount.peek()).toBe(1)
60
+ })
61
+
62
+ it('empty paneId throws', () => {
63
+ const s = createCrosshairSync()
64
+ try {
65
+ s.register('')
66
+ expect.fail('expected throw')
67
+ } catch (e) {
68
+ expect(isKLineChartError(e, 'INVALID_PARAM')).toBe(true)
69
+ }
70
+ })
71
+ })
72
+
73
+ // ---------------------------------------------------------------------------
74
+ // unregister
75
+ // ---------------------------------------------------------------------------
76
+
77
+ describe('unregister', () => {
78
+ it('removes the pane', () => {
79
+ const s = createCrosshairSync()
80
+ s.register('a')
81
+ s.register('b')
82
+ s.unregister('a')
83
+ expect(s.paneCount.peek()).toBe(1)
84
+ })
85
+
86
+ it('drops position when the source pane leaves', () => {
87
+ const s = createCrosshairSync()
88
+ s.register('a')
89
+ s.register('b')
90
+ s.move('a', 100)
91
+ expect(s.position.peek()).not.toBeNull()
92
+ s.unregister('a')
93
+ expect(s.position.peek()).toBeNull()
94
+ })
95
+
96
+ it('keeps position when a non-source pane leaves', () => {
97
+ const s = createCrosshairSync()
98
+ s.register('a')
99
+ s.register('b')
100
+ s.move('a', 100)
101
+ s.unregister('b')
102
+ expect(s.position.peek()?.index).toBe(100)
103
+ })
104
+
105
+ it('unregistering an unknown id is a no-op', () => {
106
+ const s = createCrosshairSync()
107
+ s.register('a')
108
+ expect(() => s.unregister('nope')).not.toThrow()
109
+ expect(s.paneCount.peek()).toBe(1)
110
+ })
111
+ })
112
+
113
+ // ---------------------------------------------------------------------------
114
+ // move
115
+ // ---------------------------------------------------------------------------
116
+
117
+ describe('move', () => {
118
+ it('emits position with index and source', () => {
119
+ const s = createCrosshairSync()
120
+ s.register('a')
121
+ s.move('a', 50)
122
+ const p = s.position.peek()
123
+ expect(p?.index).toBe(50)
124
+ expect(p?.source).toBe('a')
125
+ })
126
+
127
+ it('throws NOT_REGISTERED when the producer pane is unknown', () => {
128
+ const s = createCrosshairSync()
129
+ try {
130
+ s.move('ghost', 50)
131
+ expect.fail('expected throw')
132
+ } catch (e) {
133
+ expect(isKLineChartError(e, 'NOT_REGISTERED')).toBe(true)
134
+ }
135
+ })
136
+
137
+ it('throws INVALID_PARAM on non-finite index', () => {
138
+ const s = createCrosshairSync()
139
+ s.register('a')
140
+ for (const bad of [Number.NaN, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY]) {
141
+ try {
142
+ s.move('a', bad)
143
+ expect.fail(`expected throw for ${bad}`)
144
+ } catch (e) {
145
+ expect(isKLineChartError(e, 'INVALID_PARAM')).toBe(true)
146
+ }
147
+ }
148
+ })
149
+
150
+ it('rounds fractional indices to integers', () => {
151
+ const s = createCrosshairSync()
152
+ s.register('a')
153
+ s.move('a', 50.4)
154
+ expect(s.position.peek()?.index).toBe(50)
155
+ s.move('a', 50.6)
156
+ expect(s.position.peek()?.index).toBe(51)
157
+ })
158
+
159
+ it('coalesces identical (index, source) updates', () => {
160
+ const s = createCrosshairSync()
161
+ s.register('a')
162
+ const events = collect(s)
163
+ s.move('a', 50)
164
+ s.move('a', 50)
165
+ s.move('a', 50)
166
+ expect(events.length).toBe(1)
167
+ })
168
+
169
+ it('does not coalesce when source changes even if index is same', () => {
170
+ const s = createCrosshairSync()
171
+ s.register('a')
172
+ s.register('b')
173
+ const events = collect(s)
174
+ s.move('a', 50)
175
+ s.move('b', 50)
176
+ expect(events.length).toBe(2)
177
+ expect(events[0]?.source).toBe('a')
178
+ expect(events[1]?.source).toBe('b')
179
+ })
180
+ })
181
+
182
+ // ---------------------------------------------------------------------------
183
+ // Loop prevention pattern
184
+ // ---------------------------------------------------------------------------
185
+
186
+ describe('loop-prevention pattern', () => {
187
+ it('subscribers can skip their own emissions using source', () => {
188
+ const s = createCrosshairSync()
189
+ s.register('a')
190
+ s.register('b')
191
+ const aSaw: number[] = []
192
+ const bSaw: number[] = []
193
+ s.position.subscribe(() => {
194
+ const p = s.position.peek()
195
+ if (p === null || p.source === 'a') return
196
+ aSaw.push(p.index)
197
+ })
198
+ s.position.subscribe(() => {
199
+ const p = s.position.peek()
200
+ if (p === null || p.source === 'b') return
201
+ bSaw.push(p.index)
202
+ })
203
+ s.move('a', 10)
204
+ s.move('b', 20)
205
+ expect(aSaw).toEqual([20]) // a skips its own emission
206
+ expect(bSaw).toEqual([10]) // b skips its own emission
207
+ })
208
+ })
209
+
210
+ // ---------------------------------------------------------------------------
211
+ // clear / reset
212
+ // ---------------------------------------------------------------------------
213
+
214
+ describe('clear', () => {
215
+ it('sets position to null', () => {
216
+ const s = createCrosshairSync()
217
+ s.register('a')
218
+ s.move('a', 5)
219
+ s.clear()
220
+ expect(s.position.peek()).toBeNull()
221
+ })
222
+
223
+ it('clear when already null does not notify', () => {
224
+ const s = createCrosshairSync()
225
+ let notifies = 0
226
+ s.position.subscribe(() => {
227
+ notifies++
228
+ })
229
+ s.clear()
230
+ s.clear()
231
+ expect(notifies).toBe(0)
232
+ })
233
+ })
234
+
235
+ describe('reset', () => {
236
+ it('empties registry and position', () => {
237
+ const s = createCrosshairSync()
238
+ s.register('a')
239
+ s.register('b')
240
+ s.move('a', 5)
241
+ s.reset()
242
+ expect(s.paneCount.peek()).toBe(0)
243
+ expect(s.position.peek()).toBeNull()
244
+ })
245
+ })
246
+
247
+ // ---------------------------------------------------------------------------
248
+ // dispose
249
+ // ---------------------------------------------------------------------------
250
+
251
+ describe('dispose', () => {
252
+ it('makes register / move / unregister / clear no-ops', () => {
253
+ const s = createCrosshairSync()
254
+ s.register('a')
255
+ s.dispose()
256
+ expect(() => s.register('b')).not.toThrow()
257
+ expect(() => s.move('a', 5)).not.toThrow()
258
+ expect(() => s.clear()).not.toThrow()
259
+ expect(s.position.peek()).toBeNull()
260
+ })
261
+ })
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Crosshair sync — broadcasts crosshair position across multiple
3
+ * chart panes so a hover in one pane lights up the same time index
4
+ * in every other pane.
5
+ *
6
+ * Why this is the controller, not just a callback:
7
+ *
8
+ * - Multi-pane layouts (price + volume + 1+ indicator panes) are
9
+ * standard for finance charts. TradingView's multi-pane crosshair
10
+ * is one of its iconic UX features.
11
+ * - Each pane has its own scale/transform. The universal coordinate
12
+ * they share is the **time index** — the integer-valued position
13
+ * within the bar array. Sync on index, not pixel; the rendering
14
+ * pane converts back to its own x coordinate.
15
+ * - Loop prevention: a pane that consumed a sync event must not
16
+ * re-emit it. We track the source identifier so the pane that
17
+ * drove the update doesn't recurse.
18
+ *
19
+ * Pure data, signal-based, framework-agnostic — same shape as the
20
+ * rest of `@klinechart-quant/core`.
21
+ *
22
+ * const sync = createCrosshairSync()
23
+ * sync.register('price')
24
+ * sync.register('volume')
25
+ * sync.register('rsi')
26
+ *
27
+ * priceContainer.addEventListener('pointermove', e => {
28
+ * const idx = timeScale.xToBarIndex(e.offsetX)
29
+ * sync.move('price', idx)
30
+ * })
31
+ *
32
+ * sync.position.subscribe(() => {
33
+ * const p = sync.position.peek()
34
+ * if (p === null) hideCrosshair()
35
+ * else if (p.source !== 'volume') drawCrosshairInVolume(p.index)
36
+ * })
37
+ */
38
+
39
+ import { createSignal, type Signal } from '../../reactivity'
40
+ import { KLineChartError } from '../../errors'
41
+
42
+ // ---------------------------------------------------------------------------
43
+ // Public shape
44
+ // ---------------------------------------------------------------------------
45
+
46
+ /**
47
+ * Current crosshair position broadcast across registered panes.
48
+ * `null` means no pane has the crosshair active (host left every chart
49
+ * or `clear()` was called).
50
+ */
51
+ export interface CrosshairPosition {
52
+ /** Integer index into the canonical bar array. */
53
+ readonly index: number
54
+ /**
55
+ * Pane id of the pane that produced this update. Consumers MUST
56
+ * compare to skip handling their own emissions (loop prevention).
57
+ */
58
+ readonly source: string
59
+ }
60
+
61
+ export interface CrosshairSync {
62
+ /**
63
+ * Current position. `null` when no crosshair is active.
64
+ *
65
+ * NOTE: this is a `Signal<...>` — reading `position()` inside an
66
+ * `effect` tracks the subscription; reading `position.peek()` does
67
+ * not. Manual `subscribe` returns an unsubscribe function.
68
+ */
69
+ readonly position: Signal<CrosshairPosition | null>
70
+ /**
71
+ * Number of currently registered panes. Helpful for the
72
+ * "single-pane no-op" branch in adapters.
73
+ */
74
+ readonly paneCount: Signal<number>
75
+
76
+ /**
77
+ * Register a pane that participates in the sync. Idempotent —
78
+ * re-registering the same id is a no-op.
79
+ */
80
+ register(paneId: string): void
81
+ /**
82
+ * Unregister a pane. If the unregistered pane is the source of the
83
+ * current position, the position is cleared (the producer is gone).
84
+ */
85
+ unregister(paneId: string): void
86
+ /**
87
+ * Producer-side update — pane `paneId` is reporting that its
88
+ * crosshair is on bar `index`. Subscribers receive the new
89
+ * position; `paneId` is recorded as `source` for loop prevention.
90
+ *
91
+ * Calling `move` from a pane that has not been registered throws
92
+ * `KLineChartError('NOT_REGISTERED')` — the wiring contract is
93
+ * easy to mess up and silent ignores would mask bugs.
94
+ */
95
+ move(paneId: string, index: number): void
96
+ /** Clears the position. All panes see `position = null`. */
97
+ clear(): void
98
+ /** Reset to empty registry + null position. */
99
+ reset(): void
100
+ dispose(): void
101
+ }
102
+
103
+ // ---------------------------------------------------------------------------
104
+ // Factory
105
+ // ---------------------------------------------------------------------------
106
+
107
+ export function createCrosshairSync(): CrosshairSync {
108
+ const position = createSignal<CrosshairPosition | null>(null)
109
+ const paneCount = createSignal<number>(0)
110
+ const panes = new Set<string>()
111
+ let disposed = false
112
+
113
+ function publishPaneCount(): void {
114
+ paneCount.set(panes.size)
115
+ }
116
+
117
+ function register(paneId: string): void {
118
+ if (disposed) return
119
+ if (paneId === '' || typeof paneId !== 'string') {
120
+ throw new KLineChartError(
121
+ 'INVALID_PARAM',
122
+ `CrosshairSync.register: paneId must be a non-empty string`,
123
+ )
124
+ }
125
+ if (panes.has(paneId)) return
126
+ panes.add(paneId)
127
+ publishPaneCount()
128
+ }
129
+
130
+ function unregister(paneId: string): void {
131
+ if (disposed) return
132
+ if (!panes.has(paneId)) return
133
+ panes.delete(paneId)
134
+ publishPaneCount()
135
+ const cur = position.peek()
136
+ if (cur !== null && cur.source === paneId) {
137
+ position.set(null)
138
+ }
139
+ }
140
+
141
+ function move(paneId: string, index: number): void {
142
+ if (disposed) return
143
+ if (!panes.has(paneId)) {
144
+ throw new KLineChartError(
145
+ 'NOT_REGISTERED',
146
+ `CrosshairSync.move: pane ${JSON.stringify(paneId)} is not registered — call register(${JSON.stringify(paneId)}) before move()`,
147
+ )
148
+ }
149
+ if (!Number.isFinite(index)) {
150
+ throw new KLineChartError(
151
+ 'INVALID_PARAM',
152
+ `CrosshairSync.move: index must be finite, got ${index}`,
153
+ )
154
+ }
155
+ // Normalise to integer. Sub-bar precision lives in the per-pane
156
+ // pixel layer, not in the shared index broadcast.
157
+ const idx = Math.round(index)
158
+ const cur = position.peek()
159
+ // Coalesce identical updates so subscribers don't fire on no-ops.
160
+ if (cur !== null && cur.index === idx && cur.source === paneId) return
161
+ position.set({ index: idx, source: paneId })
162
+ }
163
+
164
+ function clear(): void {
165
+ if (disposed) return
166
+ if (position.peek() === null) return
167
+ position.set(null)
168
+ }
169
+
170
+ function reset(): void {
171
+ if (disposed) return
172
+ panes.clear()
173
+ publishPaneCount()
174
+ if (position.peek() !== null) position.set(null)
175
+ }
176
+
177
+ function dispose(): void {
178
+ if (disposed) return
179
+ disposed = true
180
+ panes.clear()
181
+ // No notify on dispose — subscribers should listen for their own
182
+ // host lifecycle. Setting paneCount/position now would notify
183
+ // dead subscribers in some host frameworks.
184
+ }
185
+
186
+ return { position, paneCount, register, unregister, move, clear, reset, dispose }
187
+ }
@@ -0,0 +1,5 @@
1
+ export {
2
+ createCrosshairSync,
3
+ type CrosshairSync,
4
+ type CrosshairPosition,
5
+ } from './createCrosshairSync'
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Aggressor classifier tests — explicit flag, tick rule, Lee-Ready.
3
+ *
4
+ * Each classifier is tested in isolation; the controller-level fallback chain
5
+ * is tested in controller.test.ts.
6
+ */
7
+
8
+ import { describe, it, expect } from 'vitest'
9
+ import {
10
+ classifyExplicit,
11
+ classifyTickRule,
12
+ classifyLeeReady,
13
+ type TickRuleState,
14
+ type LeeReadyState,
15
+ } from '../aggressor'
16
+
17
+ const trade = (price: number, size = 1, timestamp = 0): { timestamp: number; price: number; size: number } => ({
18
+ timestamp,
19
+ price,
20
+ size,
21
+ })
22
+
23
+ describe('classifyExplicit', () => {
24
+ it('isBuyerMaker=true means seller was the aggressor → sell', () => {
25
+ const r = classifyExplicit({ ...trade(100), isBuyerMaker: true })
26
+ expect(r).toEqual({ side: 'sell', inferred: false })
27
+ })
28
+
29
+ it('isBuyerMaker=false means buyer was the aggressor → buy', () => {
30
+ const r = classifyExplicit({ ...trade(100), isBuyerMaker: false })
31
+ expect(r).toEqual({ side: 'buy', inferred: false })
32
+ })
33
+
34
+ it('isBuyerMaker undefined → null (signals "no data; fall back")', () => {
35
+ expect(classifyExplicit(trade(100))).toBeNull()
36
+ })
37
+ })
38
+
39
+ describe('classifyTickRule', () => {
40
+ it('first trade with prevPrice=null → unknown', () => {
41
+ const state: TickRuleState = { prevPrice: null, prevSide: null }
42
+ const r = classifyTickRule(state, trade(100))
43
+ expect(r.side).toBe('unknown')
44
+ expect(r.inferred).toBe(true)
45
+ })
46
+
47
+ it('rising price → buy (uptick)', () => {
48
+ const state: TickRuleState = { prevPrice: 99, prevSide: null }
49
+ expect(classifyTickRule(state, trade(100)).side).toBe('buy')
50
+ })
51
+
52
+ it('falling price → sell (downtick)', () => {
53
+ const state: TickRuleState = { prevPrice: 101, prevSide: null }
54
+ expect(classifyTickRule(state, trade(100)).side).toBe('sell')
55
+ })
56
+
57
+ it('equal price with prevSide=buy → buy (zero-tick repeats)', () => {
58
+ const state: TickRuleState = { prevPrice: 100, prevSide: 'buy' }
59
+ expect(classifyTickRule(state, trade(100)).side).toBe('buy')
60
+ })
61
+
62
+ it('equal price with prevSide=null → unknown', () => {
63
+ const state: TickRuleState = { prevPrice: 100, prevSide: null }
64
+ expect(classifyTickRule(state, trade(100)).side).toBe('unknown')
65
+ })
66
+
67
+ it('mutates state.prevPrice in place', () => {
68
+ const state: TickRuleState = { prevPrice: 99, prevSide: null }
69
+ classifyTickRule(state, trade(100))
70
+ expect(state.prevPrice).toBe(100)
71
+ expect(state.prevSide).toBe('buy')
72
+ })
73
+
74
+ it('does NOT overwrite prevSide when result is unknown (no poisoning)', () => {
75
+ // Stream: trade1 establishes prevSide=buy, trade2 at same price with
76
+ // prev null → wouldn't happen, but documented intent is to preserve
77
+ // prevSide across an unknown classification.
78
+ const state: TickRuleState = { prevPrice: 99, prevSide: null }
79
+ classifyTickRule(state, trade(100)) // prevSide → 'buy'
80
+ expect(state.prevSide).toBe('buy')
81
+ classifyTickRule(state, trade(100)) // zero-tick, side stays 'buy'
82
+ expect(state.prevSide).toBe('buy')
83
+ })
84
+ })
85
+
86
+ describe('classifyLeeReady', () => {
87
+ it('price above mid → buy', () => {
88
+ const state: LeeReadyState = { prevPrice: null, prevSide: null }
89
+ // mid = (100+101)/2 = 100.5; price 100.6 is above → buy
90
+ expect(classifyLeeReady(state, trade(100.6), 100, 101).side).toBe('buy')
91
+ })
92
+
93
+ it('price below mid → sell', () => {
94
+ const state: LeeReadyState = { prevPrice: null, prevSide: null }
95
+ expect(classifyLeeReady(state, trade(100.3), 100, 101).side).toBe('sell')
96
+ })
97
+
98
+ it('price at mid → falls back to tick rule', () => {
99
+ const state: LeeReadyState = { prevPrice: 99, prevSide: null }
100
+ expect(classifyLeeReady(state, trade(100.5), 100, 101).side).toBe('buy')
101
+ // 100.5 == mid; price > prevPrice (99) → buy via tick rule
102
+ })
103
+
104
+ it('missing bid/ask → falls back to tick rule', () => {
105
+ // NaN bid → quotes unusable → tick-rule path.
106
+ // prevPrice=101, trade.price=100 → downtick → sell.
107
+ const state1: LeeReadyState = { prevPrice: 101, prevSide: null }
108
+ expect(classifyLeeReady(state1, trade(100), NaN, 101).side).toBe('sell')
109
+
110
+ // Infinity ask → also quotes unusable → tick-rule path.
111
+ // prevPrice=99, trade.price=100 → uptick → buy.
112
+ const state2: LeeReadyState = { prevPrice: 99, prevSide: null }
113
+ expect(classifyLeeReady(state2, trade(100), 100, Infinity).side).toBe('buy')
114
+ })
115
+
116
+ it('quote test updates state.prevPrice + prevSide', () => {
117
+ const state: LeeReadyState = { prevPrice: null, prevSide: null }
118
+ classifyLeeReady(state, trade(100.6), 100, 101)
119
+ expect(state.prevPrice).toBe(100.6)
120
+ expect(state.prevSide).toBe('buy')
121
+ })
122
+
123
+ it('marks all results as inferred=true', () => {
124
+ const state: LeeReadyState = { prevPrice: 99, prevSide: null }
125
+ expect(classifyLeeReady(state, trade(100), 99.5, 100.5).inferred).toBe(true)
126
+ })
127
+ })