@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.
- package/dist/alerts/createAlertController.d.ts +30 -0
- package/dist/alerts/createAlertController.d.ts.map +1 -0
- package/dist/alerts/createAlertController.js +261 -0
- package/dist/alerts/createAlertController.js.map +1 -0
- package/dist/alerts/index.d.ts +5 -0
- package/dist/alerts/index.d.ts.map +1 -0
- package/dist/alerts/index.js +4 -0
- package/dist/alerts/index.js.map +1 -0
- package/dist/alerts/predicates.d.ts +28 -0
- package/dist/alerts/predicates.d.ts.map +1 -0
- package/dist/alerts/predicates.js +211 -0
- package/dist/alerts/predicates.js.map +1 -0
- package/dist/alerts/rollingVolume.d.ts +15 -0
- package/dist/alerts/rollingVolume.d.ts.map +1 -0
- package/dist/alerts/rollingVolume.js +42 -0
- package/dist/alerts/rollingVolume.js.map +1 -0
- package/dist/alerts/ruleSchema.d.ts +42 -0
- package/dist/alerts/ruleSchema.d.ts.map +1 -0
- package/dist/alerts/ruleSchema.js +240 -0
- package/dist/alerts/ruleSchema.js.map +1 -0
- package/dist/alerts/types.d.ts +169 -0
- package/dist/alerts/types.d.ts.map +1 -0
- package/dist/alerts/types.js +24 -0
- package/dist/alerts/types.js.map +1 -0
- package/dist/chartTypes/heikinAshi.d.ts +49 -0
- package/dist/chartTypes/heikinAshi.d.ts.map +1 -0
- package/dist/chartTypes/heikinAshi.js +94 -0
- package/dist/chartTypes/heikinAshi.js.map +1 -0
- package/dist/chartTypes/index.d.ts +6 -0
- package/dist/chartTypes/index.d.ts.map +1 -0
- package/dist/chartTypes/index.js +5 -0
- package/dist/chartTypes/index.js.map +1 -0
- package/dist/chartTypes/pointAndFigure.d.ts +83 -0
- package/dist/chartTypes/pointAndFigure.d.ts.map +1 -0
- package/dist/chartTypes/pointAndFigure.js +159 -0
- package/dist/chartTypes/pointAndFigure.js.map +1 -0
- package/dist/chartTypes/rangeBars.d.ts +61 -0
- package/dist/chartTypes/rangeBars.d.ts.map +1 -0
- package/dist/chartTypes/rangeBars.js +181 -0
- package/dist/chartTypes/rangeBars.js.map +1 -0
- package/dist/chartTypes/renko.d.ts +94 -0
- package/dist/chartTypes/renko.d.ts.map +1 -0
- package/dist/chartTypes/renko.js +207 -0
- package/dist/chartTypes/renko.js.map +1 -0
- package/dist/chartTypes/types.d.ts +97 -0
- package/dist/chartTypes/types.d.ts.map +1 -0
- package/dist/chartTypes/types.js +39 -0
- package/dist/chartTypes/types.js.map +1 -0
- package/dist/components/anchoredVwap/computeAnchoredVwap.d.ts +76 -0
- package/dist/components/anchoredVwap/computeAnchoredVwap.d.ts.map +1 -0
- package/dist/components/anchoredVwap/computeAnchoredVwap.js +155 -0
- package/dist/components/anchoredVwap/computeAnchoredVwap.js.map +1 -0
- package/dist/components/anchoredVwap/createAnchoredVwapController.d.ts +28 -0
- package/dist/components/anchoredVwap/createAnchoredVwapController.d.ts.map +1 -0
- package/dist/components/anchoredVwap/createAnchoredVwapController.js +282 -0
- package/dist/components/anchoredVwap/createAnchoredVwapController.js.map +1 -0
- package/dist/components/anchoredVwap/index.d.ts +11 -0
- package/dist/components/anchoredVwap/index.d.ts.map +1 -0
- package/dist/components/anchoredVwap/index.js +10 -0
- package/dist/components/anchoredVwap/index.js.map +1 -0
- package/dist/components/anchoredVwap/types.d.ts +171 -0
- package/dist/components/anchoredVwap/types.d.ts.map +1 -0
- package/dist/components/anchoredVwap/types.js +25 -0
- package/dist/components/anchoredVwap/types.js.map +1 -0
- package/dist/components/crosshairSync/createCrosshairSync.d.ts +94 -0
- package/dist/components/crosshairSync/createCrosshairSync.d.ts.map +1 -0
- package/dist/components/crosshairSync/createCrosshairSync.js +118 -0
- package/dist/components/crosshairSync/createCrosshairSync.js.map +1 -0
- package/dist/components/crosshairSync/index.d.ts +2 -0
- package/dist/components/crosshairSync/index.d.ts.map +1 -0
- package/dist/components/crosshairSync/index.js +2 -0
- package/dist/components/crosshairSync/index.js.map +1 -0
- package/dist/components/footprint/aggressor.d.ts +89 -0
- package/dist/components/footprint/aggressor.d.ts.map +1 -0
- package/dist/components/footprint/aggressor.js +106 -0
- package/dist/components/footprint/aggressor.js.map +1 -0
- package/dist/components/footprint/createFootprintController.d.ts +29 -0
- package/dist/components/footprint/createFootprintController.d.ts.map +1 -0
- package/dist/components/footprint/createFootprintController.js +264 -0
- package/dist/components/footprint/createFootprintController.js.map +1 -0
- package/dist/components/footprint/index.d.ts +7 -0
- package/dist/components/footprint/index.d.ts.map +1 -0
- package/dist/components/footprint/index.js +4 -0
- package/dist/components/footprint/index.js.map +1 -0
- package/dist/components/footprint/perBarStats.d.ts +63 -0
- package/dist/components/footprint/perBarStats.d.ts.map +1 -0
- package/dist/components/footprint/perBarStats.js +123 -0
- package/dist/components/footprint/perBarStats.js.map +1 -0
- package/dist/components/footprint/types.d.ts +197 -0
- package/dist/components/footprint/types.d.ts.map +1 -0
- package/dist/components/footprint/types.js +23 -0
- package/dist/components/footprint/types.js.map +1 -0
- package/dist/components/mtfOverlay/alignToBaseIndex.d.ts +45 -0
- package/dist/components/mtfOverlay/alignToBaseIndex.d.ts.map +1 -0
- package/dist/components/mtfOverlay/alignToBaseIndex.js +92 -0
- package/dist/components/mtfOverlay/alignToBaseIndex.js.map +1 -0
- package/dist/components/mtfOverlay/createMtfController.d.ts +7 -0
- package/dist/components/mtfOverlay/createMtfController.d.ts.map +1 -0
- package/dist/components/mtfOverlay/createMtfController.js +134 -0
- package/dist/components/mtfOverlay/createMtfController.js.map +1 -0
- package/dist/components/mtfOverlay/index.d.ts +19 -0
- package/dist/components/mtfOverlay/index.d.ts.map +1 -0
- package/dist/components/mtfOverlay/index.js +18 -0
- package/dist/components/mtfOverlay/index.js.map +1 -0
- package/dist/components/mtfOverlay/resampleBars.d.ts +37 -0
- package/dist/components/mtfOverlay/resampleBars.d.ts.map +1 -0
- package/dist/components/mtfOverlay/resampleBars.js +93 -0
- package/dist/components/mtfOverlay/resampleBars.js.map +1 -0
- package/dist/components/mtfOverlay/types.d.ts +132 -0
- package/dist/components/mtfOverlay/types.d.ts.map +1 -0
- package/dist/components/mtfOverlay/types.js +22 -0
- package/dist/components/mtfOverlay/types.js.map +1 -0
- package/dist/components/orderBookHeatmap/createHeatmapController.d.ts +21 -0
- package/dist/components/orderBookHeatmap/createHeatmapController.d.ts.map +1 -0
- package/dist/components/orderBookHeatmap/createHeatmapController.js +234 -0
- package/dist/components/orderBookHeatmap/createHeatmapController.js.map +1 -0
- package/dist/components/orderBookHeatmap/createOrderBookState.d.ts +18 -0
- package/dist/components/orderBookHeatmap/createOrderBookState.d.ts.map +1 -0
- package/dist/components/orderBookHeatmap/createOrderBookState.js +102 -0
- package/dist/components/orderBookHeatmap/createOrderBookState.js.map +1 -0
- package/dist/components/orderBookHeatmap/deltaArchive.d.ts +25 -0
- package/dist/components/orderBookHeatmap/deltaArchive.d.ts.map +1 -0
- package/dist/components/orderBookHeatmap/deltaArchive.js +106 -0
- package/dist/components/orderBookHeatmap/deltaArchive.js.map +1 -0
- package/dist/components/orderBookHeatmap/index.d.ts +13 -0
- package/dist/components/orderBookHeatmap/index.d.ts.map +1 -0
- package/dist/components/orderBookHeatmap/index.js +12 -0
- package/dist/components/orderBookHeatmap/index.js.map +1 -0
- package/dist/components/orderBookHeatmap/logColorScale.d.ts +20 -0
- package/dist/components/orderBookHeatmap/logColorScale.d.ts.map +1 -0
- package/dist/components/orderBookHeatmap/logColorScale.js +71 -0
- package/dist/components/orderBookHeatmap/logColorScale.js.map +1 -0
- package/dist/components/orderBookHeatmap/snapshotRing.d.ts +16 -0
- package/dist/components/orderBookHeatmap/snapshotRing.d.ts.map +1 -0
- package/dist/components/orderBookHeatmap/snapshotRing.js +61 -0
- package/dist/components/orderBookHeatmap/snapshotRing.js.map +1 -0
- package/dist/components/orderBookHeatmap/types.d.ts +128 -0
- package/dist/components/orderBookHeatmap/types.d.ts.map +1 -0
- package/dist/components/orderBookHeatmap/types.js +16 -0
- package/dist/components/orderBookHeatmap/types.js.map +1 -0
- package/dist/components/volumeProfile/binning.d.ts +54 -0
- package/dist/components/volumeProfile/binning.d.ts.map +1 -0
- package/dist/components/volumeProfile/binning.js +123 -0
- package/dist/components/volumeProfile/binning.js.map +1 -0
- package/dist/components/volumeProfile/createVolumeProfileController.d.ts +31 -0
- package/dist/components/volumeProfile/createVolumeProfileController.d.ts.map +1 -0
- package/dist/components/volumeProfile/createVolumeProfileController.js +179 -0
- package/dist/components/volumeProfile/createVolumeProfileController.js.map +1 -0
- package/dist/components/volumeProfile/index.d.ts +6 -0
- package/dist/components/volumeProfile/index.d.ts.map +1 -0
- package/dist/components/volumeProfile/index.js +5 -0
- package/dist/components/volumeProfile/index.js.map +1 -0
- package/dist/components/volumeProfile/poc.d.ts +35 -0
- package/dist/components/volumeProfile/poc.d.ts.map +1 -0
- package/dist/components/volumeProfile/poc.js +51 -0
- package/dist/components/volumeProfile/poc.js.map +1 -0
- package/dist/components/volumeProfile/types.d.ts +138 -0
- package/dist/components/volumeProfile/types.d.ts.map +1 -0
- package/dist/components/volumeProfile/types.js +19 -0
- package/dist/components/volumeProfile/types.js.map +1 -0
- package/dist/components/volumeProfile/valueArea.d.ts +58 -0
- package/dist/components/volumeProfile/valueArea.d.ts.map +1 -0
- package/dist/components/volumeProfile/valueArea.js +172 -0
- package/dist/components/volumeProfile/valueArea.js.map +1 -0
- package/dist/controllers/createChartController.d.ts.map +1 -1
- package/dist/controllers/createChartController.js +22 -11
- package/dist/controllers/createChartController.js.map +1 -1
- package/dist/controllers/types.d.ts +3 -1
- package/dist/controllers/types.d.ts.map +1 -1
- package/dist/data-fetchers/baostock.d.ts.map +1 -1
- package/dist/data-fetchers/baostock.js +2 -1
- package/dist/data-fetchers/baostock.js.map +1 -1
- package/dist/data-fetchers/dataBuffer.effects.d.ts.map +1 -1
- package/dist/data-fetchers/dataBuffer.effects.js +2 -1
- package/dist/data-fetchers/dataBuffer.effects.js.map +1 -1
- package/dist/data-fetchers/fetcherDefinitionRegistry.d.ts.map +1 -1
- package/dist/data-fetchers/fetcherDefinitionRegistry.js +2 -1
- package/dist/data-fetchers/fetcherDefinitionRegistry.js.map +1 -1
- package/dist/data-fetchers/gotdx.js +4 -3
- package/dist/data-fetchers/gotdx.js.map +1 -1
- package/dist/data-fetchers/router.d.ts.map +1 -1
- package/dist/data-fetchers/router.js +4 -3
- package/dist/data-fetchers/router.js.map +1 -1
- package/dist/data-fetchers/tradingview.js +3 -2
- package/dist/data-fetchers/tradingview.js.map +1 -1
- package/dist/engine/chart.d.ts +13 -0
- package/dist/engine/chart.d.ts.map +1 -1
- package/dist/engine/chart.js +109 -0
- package/dist/engine/chart.js.map +1 -1
- package/dist/engine/data/chartDataManager.d.ts +1 -0
- package/dist/engine/data/chartDataManager.d.ts.map +1 -1
- package/dist/engine/data/chartDataManager.js +27 -4
- package/dist/engine/data/chartDataManager.js.map +1 -1
- package/dist/engine/drawing/toolConfig.d.ts.map +1 -1
- package/dist/engine/drawing/toolConfig.js +2 -1
- package/dist/engine/drawing/toolConfig.js.map +1 -1
- package/dist/engine/indicators/chartIndicatorManager.d.ts.map +1 -1
- package/dist/engine/indicators/chartIndicatorManager.js +2 -1
- package/dist/engine/indicators/chartIndicatorManager.js.map +1 -1
- package/dist/engine/indicators/indicatorDefinitionRegistry.d.ts.map +1 -1
- package/dist/engine/indicators/indicatorDefinitionRegistry.js +2 -1
- package/dist/engine/indicators/indicatorDefinitionRegistry.js.map +1 -1
- package/dist/engine/indicators/indicatorMetadata.d.ts.map +1 -1
- package/dist/engine/indicators/indicatorMetadata.js +2 -1
- package/dist/engine/indicators/indicatorMetadata.js.map +1 -1
- package/dist/engine/indicators/indicatorRegistry.d.ts.map +1 -1
- package/dist/engine/indicators/indicatorRegistry.js +6 -5
- package/dist/engine/indicators/indicatorRegistry.js.map +1 -1
- package/dist/engine/indicators/registerBuiltins.d.ts.map +1 -1
- package/dist/engine/indicators/registerBuiltins.js +2 -1
- package/dist/engine/indicators/registerBuiltins.js.map +1 -1
- package/dist/engine/indicators/scheduler.d.ts +6 -0
- package/dist/engine/indicators/scheduler.d.ts.map +1 -1
- package/dist/engine/indicators/scheduler.js +12 -0
- package/dist/engine/indicators/scheduler.js.map +1 -1
- package/dist/engine/indicators/soa.d.ts.map +1 -1
- package/dist/engine/indicators/soa.js +2 -1
- package/dist/engine/indicators/soa.js.map +1 -1
- package/dist/engine/indicators/stateComposer.d.ts.map +1 -1
- package/dist/engine/indicators/stateComposer.js +2 -1
- package/dist/engine/indicators/stateComposer.js.map +1 -1
- package/dist/engine/subPaneManager.d.ts.map +1 -1
- package/dist/engine/subPaneManager.js +2 -1
- package/dist/engine/subPaneManager.js.map +1 -1
- package/dist/engine/utils/chartZoomController.d.ts +2 -0
- package/dist/engine/utils/chartZoomController.d.ts.map +1 -1
- package/dist/engine/utils/chartZoomController.js +5 -1
- package/dist/engine/utils/chartZoomController.js.map +1 -1
- package/dist/errors-help.d.ts +72 -0
- package/dist/errors-help.d.ts.map +1 -0
- package/dist/errors-help.js +149 -0
- package/dist/errors-help.js.map +1 -0
- package/dist/errors.d.ts +64 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +61 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -1
- package/dist/indicators/alma.d.ts +27 -0
- package/dist/indicators/alma.d.ts.map +1 -0
- package/dist/indicators/alma.js +36 -0
- package/dist/indicators/alma.js.map +1 -0
- package/dist/indicators/awesomeOscillator.d.ts +19 -0
- package/dist/indicators/awesomeOscillator.d.ts.map +1 -0
- package/dist/indicators/awesomeOscillator.js +36 -0
- package/dist/indicators/awesomeOscillator.js.map +1 -0
- package/dist/indicators/dpo.d.ts +17 -0
- package/dist/indicators/dpo.d.ts.map +1 -0
- package/dist/indicators/dpo.js +24 -0
- package/dist/indicators/dpo.js.map +1 -0
- package/dist/indicators/fisherTransform.d.ts +26 -0
- package/dist/indicators/fisherTransform.d.ts.map +1 -0
- package/dist/indicators/fisherTransform.js +44 -0
- package/dist/indicators/fisherTransform.js.map +1 -0
- package/dist/indicators/frama.d.ts +25 -0
- package/dist/indicators/frama.d.ts.map +1 -0
- package/dist/indicators/frama.js +61 -0
- package/dist/indicators/frama.js.map +1 -0
- package/dist/indicators/index.d.ts +24 -0
- package/dist/indicators/index.d.ts.map +1 -0
- package/dist/indicators/index.js +26 -0
- package/dist/indicators/index.js.map +1 -0
- package/dist/indicators/lsma.d.ts +21 -0
- package/dist/indicators/lsma.d.ts.map +1 -0
- package/dist/indicators/lsma.js +37 -0
- package/dist/indicators/lsma.js.map +1 -0
- package/dist/indicators/schaffTrendCycle.d.ts +24 -0
- package/dist/indicators/schaffTrendCycle.d.ts.map +1 -0
- package/dist/indicators/schaffTrendCycle.js +70 -0
- package/dist/indicators/schaffTrendCycle.js.map +1 -0
- package/dist/indicators/stochRSI.d.ts +24 -0
- package/dist/indicators/stochRSI.d.ts.map +1 -0
- package/dist/indicators/stochRSI.js +86 -0
- package/dist/indicators/stochRSI.js.map +1 -0
- package/dist/indicators/t3.d.ts +31 -0
- package/dist/indicators/t3.d.ts.map +1 -0
- package/dist/indicators/t3.js +63 -0
- package/dist/indicators/t3.js.map +1 -0
- package/dist/indicators/ultimateOscillator.d.ts +26 -0
- package/dist/indicators/ultimateOscillator.d.ts.map +1 -0
- package/dist/indicators/ultimateOscillator.js +59 -0
- package/dist/indicators/ultimateOscillator.js.map +1 -0
- package/dist/indicators/vidya.d.ts +24 -0
- package/dist/indicators/vidya.d.ts.map +1 -0
- package/dist/indicators/vidya.js +54 -0
- package/dist/indicators/vidya.js.map +1 -0
- package/dist/indicators/zlema.d.ts +16 -0
- package/dist/indicators/zlema.d.ts.map +1 -0
- package/dist/indicators/zlema.js +26 -0
- package/dist/indicators/zlema.js.map +1 -0
- package/dist/input/gesture.d.ts +125 -0
- package/dist/input/gesture.d.ts.map +1 -0
- package/dist/input/gesture.js +249 -0
- package/dist/input/gesture.js.map +1 -0
- package/dist/input/index.d.ts +9 -0
- package/dist/input/index.d.ts.map +1 -0
- package/dist/input/index.js +9 -0
- package/dist/input/index.js.map +1 -0
- package/dist/input/keyboard.d.ts +140 -0
- package/dist/input/keyboard.d.ts.map +1 -0
- package/dist/input/keyboard.js +260 -0
- package/dist/input/keyboard.js.map +1 -0
- package/dist/mcp/chartBridge.d.ts +2 -2
- package/dist/mcp/chartBridge.d.ts.map +1 -1
- package/dist/mcp/chartBridge.js +27 -21
- package/dist/mcp/chartBridge.js.map +1 -1
- package/dist/plugin/PluginHost.d.ts +0 -3
- package/dist/plugin/PluginHost.d.ts.map +1 -1
- package/dist/plugin/PluginHost.js +7 -3
- package/dist/plugin/PluginHost.js.map +1 -1
- package/dist/plugin/PluginRegistry.d.ts +0 -3
- package/dist/plugin/PluginRegistry.d.ts.map +1 -1
- package/dist/plugin/PluginRegistry.js +5 -1
- package/dist/plugin/PluginRegistry.js.map +1 -1
- package/dist/render/Renderer.d.ts +116 -0
- package/dist/render/Renderer.d.ts.map +1 -0
- package/dist/render/Renderer.js +31 -0
- package/dist/render/Renderer.js.map +1 -0
- package/dist/render/SurfaceBackend.d.ts +83 -0
- package/dist/render/SurfaceBackend.d.ts.map +1 -0
- package/dist/render/SurfaceBackend.js +27 -0
- package/dist/render/SurfaceBackend.js.map +1 -0
- package/dist/render/index.d.ts +12 -0
- package/dist/render/index.d.ts.map +1 -0
- package/dist/render/index.js +11 -0
- package/dist/render/index.js.map +1 -0
- package/dist/renderer-tier/detectRendererTier.d.ts +57 -0
- package/dist/renderer-tier/detectRendererTier.d.ts.map +1 -0
- package/dist/renderer-tier/detectRendererTier.js +143 -0
- package/dist/renderer-tier/detectRendererTier.js.map +1 -0
- package/dist/renderer-tier/index.d.ts +12 -0
- package/dist/renderer-tier/index.d.ts.map +1 -0
- package/dist/renderer-tier/index.js +12 -0
- package/dist/renderer-tier/index.js.map +1 -0
- package/dist/renderer-tier/selectBackend.d.ts +106 -0
- package/dist/renderer-tier/selectBackend.d.ts.map +1 -0
- package/dist/renderer-tier/selectBackend.js +113 -0
- package/dist/renderer-tier/selectBackend.js.map +1 -0
- package/dist/renderer-tier/types.d.ts +50 -0
- package/dist/renderer-tier/types.d.ts.map +1 -0
- package/dist/renderer-tier/types.js +24 -0
- package/dist/renderer-tier/types.js.map +1 -0
- package/dist/replay/createReplayController.d.ts +3 -0
- package/dist/replay/createReplayController.d.ts.map +1 -0
- package/dist/replay/createReplayController.js +251 -0
- package/dist/replay/createReplayController.js.map +1 -0
- package/dist/replay/index.d.ts +5 -0
- package/dist/replay/index.d.ts.map +1 -0
- package/dist/replay/index.js +3 -0
- package/dist/replay/index.js.map +1 -0
- package/dist/replay/timeline.d.ts +50 -0
- package/dist/replay/timeline.d.ts.map +1 -0
- package/dist/replay/timeline.js +104 -0
- package/dist/replay/timeline.js.map +1 -0
- package/dist/replay/types.d.ts +94 -0
- package/dist/replay/types.d.ts.map +1 -0
- package/dist/replay/types.js +21 -0
- package/dist/replay/types.js.map +1 -0
- package/dist/scale/anchoredZoom.d.ts +64 -0
- package/dist/scale/anchoredZoom.d.ts.map +1 -0
- package/dist/scale/anchoredZoom.js +67 -0
- package/dist/scale/anchoredZoom.js.map +1 -0
- package/dist/scale/createPriceScale.d.ts +49 -0
- package/dist/scale/createPriceScale.d.ts.map +1 -0
- package/dist/scale/createPriceScale.js +175 -0
- package/dist/scale/createPriceScale.js.map +1 -0
- package/dist/scale/createTimeScale.d.ts +27 -0
- package/dist/scale/createTimeScale.d.ts.map +1 -0
- package/dist/scale/createTimeScale.js +139 -0
- package/dist/scale/createTimeScale.js.map +1 -0
- package/dist/scale/index.d.ts +6 -0
- package/dist/scale/index.d.ts.map +1 -0
- package/dist/scale/index.js +5 -0
- package/dist/scale/index.js.map +1 -0
- package/dist/scale/originShift.d.ts +69 -0
- package/dist/scale/originShift.d.ts.map +1 -0
- package/dist/scale/originShift.js +41 -0
- package/dist/scale/originShift.js.map +1 -0
- package/dist/scale/types.d.ts +90 -0
- package/dist/scale/types.d.ts.map +1 -0
- package/dist/scale/types.js +17 -0
- package/dist/scale/types.js.map +1 -0
- package/dist/scene/createScene.d.ts +22 -0
- package/dist/scene/createScene.d.ts.map +1 -0
- package/dist/scene/createScene.js +114 -0
- package/dist/scene/createScene.js.map +1 -0
- package/dist/scene/index.d.ts +13 -0
- package/dist/scene/index.d.ts.map +1 -0
- package/dist/scene/index.js +11 -0
- package/dist/scene/index.js.map +1 -0
- package/dist/scene/layerRegistry.d.ts +83 -0
- package/dist/scene/layerRegistry.d.ts.map +1 -0
- package/dist/scene/layerRegistry.js +43 -0
- package/dist/scene/layerRegistry.js.map +1 -0
- package/dist/scene/types.d.ts +128 -0
- package/dist/scene/types.d.ts.map +1 -0
- package/dist/scene/types.js +21 -0
- package/dist/scene/types.js.map +1 -0
- package/dist/scheduler/createFrameBudget.d.ts +91 -0
- package/dist/scheduler/createFrameBudget.d.ts.map +1 -0
- package/dist/scheduler/createFrameBudget.js +232 -0
- package/dist/scheduler/createFrameBudget.js.map +1 -0
- package/dist/scheduler/index.d.ts +2 -0
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/scheduler/index.js +2 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/tokens/themeToCssVars.d.ts.map +1 -1
- package/dist/tokens/themeToCssVars.js +2 -28
- package/dist/tokens/themeToCssVars.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
- package/src/__bench__/indicators.bench.ts +215 -0
- package/src/__bench__/orderBookHeatmap.bench.ts +68 -0
- package/src/__bench__/scale.bench.ts +80 -0
- package/src/__bench__/signal.bench.ts +41 -0
- package/src/__bench__/volumeProfile.bench.ts +66 -0
- package/src/__tests__/errors-help.test.ts +184 -0
- package/src/__tests__/errors.test.ts +187 -0
- package/src/alerts/__tests__/controller.test.ts +231 -0
- package/src/alerts/__tests__/predicates.test.ts +374 -0
- package/src/alerts/__tests__/ruleSchema.test.ts +180 -0
- package/src/alerts/createAlertController.ts +312 -0
- package/src/alerts/index.ts +18 -0
- package/src/alerts/predicates.ts +231 -0
- package/src/alerts/rollingVolume.ts +51 -0
- package/src/alerts/ruleSchema.ts +278 -0
- package/src/alerts/types.ts +177 -0
- package/src/chartTypes/__tests__/heikinAshi.test.ts +122 -0
- package/src/chartTypes/__tests__/pointAndFigure.test.ts +167 -0
- package/src/chartTypes/__tests__/rangeBars.test.ts +146 -0
- package/src/chartTypes/__tests__/renko.test.ts +160 -0
- package/src/chartTypes/heikinAshi.ts +116 -0
- package/src/chartTypes/index.ts +5 -0
- package/src/chartTypes/pointAndFigure.ts +278 -0
- package/src/chartTypes/rangeBars.ts +303 -0
- package/src/chartTypes/renko.ts +311 -0
- package/src/chartTypes/types.ts +99 -0
- package/src/components/anchoredVwap/__tests__/computeAnchoredVwap.test.ts +331 -0
- package/src/components/anchoredVwap/__tests__/controller.test.ts +430 -0
- package/src/components/anchoredVwap/computeAnchoredVwap.ts +174 -0
- package/src/components/anchoredVwap/createAnchoredVwapController.ts +358 -0
- package/src/components/anchoredVwap/index.ts +17 -0
- package/src/components/anchoredVwap/types.ts +187 -0
- package/src/components/crosshairSync/__tests__/crosshairSync.test.ts +261 -0
- package/src/components/crosshairSync/createCrosshairSync.ts +187 -0
- package/src/components/crosshairSync/index.ts +5 -0
- package/src/components/footprint/__tests__/aggressor.test.ts +127 -0
- package/src/components/footprint/__tests__/controller.test.ts +130 -0
- package/src/components/footprint/__tests__/perBarStats.test.ts +114 -0
- package/src/components/footprint/aggressor.ts +165 -0
- package/src/components/footprint/createFootprintController.ts +338 -0
- package/src/components/footprint/index.ts +21 -0
- package/src/components/footprint/perBarStats.ts +137 -0
- package/src/components/footprint/types.ts +232 -0
- package/src/components/mtfOverlay/__tests__/alignToBaseIndex.test.ts +103 -0
- package/src/components/mtfOverlay/__tests__/controller.test.ts +172 -0
- package/src/components/mtfOverlay/__tests__/resampleBars.test.ts +106 -0
- package/src/components/mtfOverlay/alignToBaseIndex.ts +108 -0
- package/src/components/mtfOverlay/createMtfController.ts +180 -0
- package/src/components/mtfOverlay/index.ts +26 -0
- package/src/components/mtfOverlay/resampleBars.ts +134 -0
- package/src/components/mtfOverlay/types.ts +148 -0
- package/src/components/orderBookHeatmap/__tests__/controller.test.ts +237 -0
- package/src/components/orderBookHeatmap/__tests__/deltaArchive.test.ts +88 -0
- package/src/components/orderBookHeatmap/__tests__/logColorScale.test.ts +69 -0
- package/src/components/orderBookHeatmap/__tests__/orderBookState.test.ts +113 -0
- package/src/components/orderBookHeatmap/__tests__/snapshotRing.test.ts +53 -0
- package/src/components/orderBookHeatmap/computeShader.wgsl.md +130 -0
- package/src/components/orderBookHeatmap/createHeatmapController.ts +273 -0
- package/src/components/orderBookHeatmap/createOrderBookState.ts +116 -0
- package/src/components/orderBookHeatmap/deltaArchive.ts +113 -0
- package/src/components/orderBookHeatmap/index.ts +25 -0
- package/src/components/orderBookHeatmap/logColorScale.ts +77 -0
- package/src/components/orderBookHeatmap/snapshotRing.ts +65 -0
- package/src/components/orderBookHeatmap/types.ts +168 -0
- package/src/components/volumeProfile/__tests__/binning.test.ts +133 -0
- package/src/components/volumeProfile/__tests__/poc.test.ts +44 -0
- package/src/components/volumeProfile/__tests__/valueArea.test.ts +177 -0
- package/src/components/volumeProfile/binning.ts +133 -0
- package/src/components/volumeProfile/createVolumeProfileController.ts +213 -0
- package/src/components/volumeProfile/index.ts +12 -0
- package/src/components/volumeProfile/poc.ts +53 -0
- package/src/components/volumeProfile/types.ts +148 -0
- package/src/components/volumeProfile/valueArea.ts +187 -0
- package/src/controllers/createChartController.ts +23 -10
- package/src/controllers/types.ts +5 -1
- package/src/data-fetchers/baostock.ts +2 -1
- package/src/data-fetchers/dataBuffer.effects.ts +3 -1
- package/src/data-fetchers/fetcherDefinitionRegistry.ts +2 -1
- package/src/data-fetchers/gotdx.ts +4 -3
- package/src/data-fetchers/router.ts +6 -3
- package/src/data-fetchers/tradingview.ts +3 -2
- package/src/engine/chart.ts +126 -3
- package/src/engine/data/chartDataManager.ts +26 -4
- package/src/engine/drawing/toolConfig.ts +2 -1
- package/src/engine/indicators/chartIndicatorManager.ts +2 -1
- package/src/engine/indicators/indicatorDefinitionRegistry.ts +2 -1
- package/src/engine/indicators/indicatorMetadata.ts +2 -1
- package/src/engine/indicators/indicatorRegistry.ts +6 -5
- package/src/engine/indicators/registerBuiltins.ts +2 -1
- package/src/engine/indicators/scheduler.ts +15 -0
- package/src/engine/indicators/soa.ts +2 -1
- package/src/engine/indicators/stateComposer.ts +2 -1
- package/src/engine/subPaneManager.ts +2 -1
- package/src/engine/utils/chartZoomController.ts +7 -1
- package/src/errors-help.ts +218 -0
- package/src/errors.ts +128 -0
- package/src/index.ts +37 -0
- package/src/indicators/__tests__/maFamily.test.ts +159 -0
- package/src/indicators/__tests__/oscillators.test.ts +150 -0
- package/src/indicators/alma.ts +61 -0
- package/src/indicators/awesomeOscillator.ts +54 -0
- package/src/indicators/dpo.ts +37 -0
- package/src/indicators/fisherTransform.ts +63 -0
- package/src/indicators/frama.ts +87 -0
- package/src/indicators/index.ts +39 -0
- package/src/indicators/lsma.ts +58 -0
- package/src/indicators/schaffTrendCycle.ts +92 -0
- package/src/indicators/stochRSI.ts +108 -0
- package/src/indicators/t3.ts +96 -0
- package/src/indicators/ultimateOscillator.ts +83 -0
- package/src/indicators/vidya.ts +80 -0
- package/src/indicators/zlema.ts +41 -0
- package/src/input/__tests__/gesture.test.ts +271 -0
- package/src/input/__tests__/keyboard.test.ts +317 -0
- package/src/input/gesture.ts +357 -0
- package/src/input/index.ts +26 -0
- package/src/input/keyboard.ts +373 -0
- package/src/mcp/chartBridge.ts +98 -72
- package/src/plugin/PluginHost.ts +4 -3
- package/src/plugin/PluginRegistry.ts +2 -1
- package/src/render/Renderer.ts +127 -0
- package/src/render/SurfaceBackend.ts +94 -0
- package/src/render/__tests__/contract.test.ts +176 -0
- package/src/render/index.ts +27 -0
- package/src/renderer-tier/__tests__/detectRendererTier.test.ts +180 -0
- package/src/renderer-tier/__tests__/selectBackend.test.ts +253 -0
- package/src/renderer-tier/detectRendererTier.ts +168 -0
- package/src/renderer-tier/index.ts +30 -0
- package/src/renderer-tier/selectBackend.ts +201 -0
- package/src/renderer-tier/types.ts +59 -0
- package/src/replay/__tests__/controller.test.ts +309 -0
- package/src/replay/__tests__/timeline.test.ts +99 -0
- package/src/replay/createReplayController.ts +302 -0
- package/src/replay/index.ts +15 -0
- package/src/replay/timeline.ts +106 -0
- package/src/replay/types.ts +107 -0
- package/src/scale/__tests__/anchoredZoom.test.ts +125 -0
- package/src/scale/__tests__/originShift.test.ts +97 -0
- package/src/scale/__tests__/priceScale.test.ts +219 -0
- package/src/scale/__tests__/timeScale.test.ts +119 -0
- package/src/scale/anchoredZoom.ts +104 -0
- package/src/scale/createPriceScale.ts +226 -0
- package/src/scale/createTimeScale.ts +150 -0
- package/src/scale/index.ts +9 -0
- package/src/scale/originShift.ts +111 -0
- package/src/scale/types.ts +100 -0
- package/src/scene/__tests__/layerRegistry.test.ts +207 -0
- package/src/scene/__tests__/scene.test.ts +411 -0
- package/src/scene/createScene.ts +117 -0
- package/src/scene/index.ts +29 -0
- package/src/scene/layerRegistry.ts +117 -0
- package/src/scene/types.ts +143 -0
- package/src/scheduler/__tests__/createFrameBudget.test.ts +315 -0
- package/src/scheduler/createFrameBudget.ts +316 -0
- package/src/scheduler/index.ts +7 -0
- package/src/tokens/themeToCssVars.ts +3 -1
- package/src/version.ts +1 -1
- package/dist/data-fetchers/gotdx/gotdx.d.ts +0 -7
- package/dist/data-fetchers/gotdx/gotdx.d.ts.map +0 -1
- package/dist/data-fetchers/gotdx/gotdx.js +0 -7
- package/dist/data-fetchers/gotdx/gotdx.js.map +0 -1
- package/src/data-fetchers/gotdx/gotdx.ts +0 -6
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* L2 Order Book Heatmap — public types.
|
|
3
|
+
*
|
|
4
|
+
* Implements the data model described in `docs/ROADMAP.md` §3.2.
|
|
5
|
+
*
|
|
6
|
+
* Critique addressed (PR23): the live render path uses fixed-cadence
|
|
7
|
+
* snapshots (cheap to upload to a GPU storage buffer), while a parallel
|
|
8
|
+
* append-only delta archive preserves the full event stream so that
|
|
9
|
+
* sub-snapshot "flash orders" can be replayed exactly. Snapshot ring is
|
|
10
|
+
* the render source; delta archive is the source of truth.
|
|
11
|
+
*
|
|
12
|
+
* No file in this module imports from `src/` — this component is part of
|
|
13
|
+
* the additive `packages/core/` work.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { Signal } from '../../reactivity'
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// Inbound stream
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* One exchange-pushed delta. `size === 0` means the price level is removed.
|
|
24
|
+
*
|
|
25
|
+
* Timestamp is the **exchange** wall-clock in milliseconds — we use it as
|
|
26
|
+
* the controller's clock so the system is deterministic for tests and
|
|
27
|
+
* correct under bursty traffic (no setInterval).
|
|
28
|
+
*/
|
|
29
|
+
export interface OrderBookDelta {
|
|
30
|
+
side: 'bid' | 'ask'
|
|
31
|
+
price: number
|
|
32
|
+
size: number
|
|
33
|
+
timestamp: number
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Snapshot
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Immutable view of the book at one instant. Prices in this object are
|
|
42
|
+
* always returned in their **dequantized** (real-world) form — clients
|
|
43
|
+
* never see the integer tick index.
|
|
44
|
+
*
|
|
45
|
+
* Bids are sorted descending (best bid first), asks ascending (best ask
|
|
46
|
+
* first). Each entry is `[price, size]`.
|
|
47
|
+
*/
|
|
48
|
+
export interface BookSnapshot {
|
|
49
|
+
readonly bids: ReadonlyArray<readonly [number, number]>
|
|
50
|
+
readonly asks: ReadonlyArray<readonly [number, number]>
|
|
51
|
+
readonly timestamp: number
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// State accumulator
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
export interface OrderBookStateOptions {
|
|
59
|
+
/** quantize all input prices to this tick (e.g. 0.01 = cents) */
|
|
60
|
+
tickSize: number
|
|
61
|
+
/** keep at most this many levels per side; oldest-by-distance dropped */
|
|
62
|
+
maxLevels?: number
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface OrderBookState {
|
|
66
|
+
/** Apply one delta. `size === 0` removes the level. */
|
|
67
|
+
applyDelta(delta: OrderBookDelta): void
|
|
68
|
+
/** Take a fresh snapshot (prices dequantized). */
|
|
69
|
+
snapshot(): BookSnapshot
|
|
70
|
+
/** Empty both sides. */
|
|
71
|
+
clear(): void
|
|
72
|
+
/** Latest timestamp seen via {@link applyDelta}. */
|
|
73
|
+
lastTimestamp(): number
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Snapshot ring (live render path)
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
export interface SnapshotRing {
|
|
81
|
+
readonly capacity: number
|
|
82
|
+
push(snapshot: BookSnapshot): void
|
|
83
|
+
/** snapshots oldest → newest; never contains gaps */
|
|
84
|
+
toArray(): ReadonlyArray<BookSnapshot>
|
|
85
|
+
size(): number
|
|
86
|
+
clear(): void
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// Delta archive (replay path)
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
export interface DeltaArchive {
|
|
94
|
+
append(delta: OrderBookDelta): void
|
|
95
|
+
/** inclusive range; deltas with `from <= ts <= to` */
|
|
96
|
+
range(fromTimestamp: number, toTimestamp: number): ReadonlyArray<OrderBookDelta>
|
|
97
|
+
size(): number
|
|
98
|
+
clear(): void
|
|
99
|
+
/** drop oldest deltas until total size ≤ maxSize */
|
|
100
|
+
trim(maxSize: number): void
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface DeltaArchiveOptions {
|
|
104
|
+
/** when set, the archive self-trims after each {@link DeltaArchive.append} */
|
|
105
|
+
maxSize?: number
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Log color scale
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
export interface LogColorScale {
|
|
113
|
+
/** returns intensity in [0,1] for the given size */
|
|
114
|
+
intensity(size: number): number
|
|
115
|
+
/** swap the size range (does not retroactively rescale anything) */
|
|
116
|
+
setRange(sizeMin: number, sizeMax: number): void
|
|
117
|
+
/** current range (for callers that need to mirror it into GPU uniforms) */
|
|
118
|
+
range(): { sizeMin: number; sizeMax: number }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Top-level controller
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
export interface HeatmapControllerConfig {
|
|
126
|
+
tickSize: number
|
|
127
|
+
snapshotIntervalMs: number
|
|
128
|
+
snapshotRingCapacity: number
|
|
129
|
+
deltaArchiveMaxSize: number
|
|
130
|
+
logColorRange: { sizeMin: number; sizeMax: number }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface HeatmapState {
|
|
134
|
+
readonly latestSnapshot: BookSnapshot | null
|
|
135
|
+
readonly snapshotCount: number
|
|
136
|
+
readonly deltaCount: number
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface HeatmapController {
|
|
140
|
+
readonly state: Signal<HeatmapState>
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Ingest one L2 delta — canonical method, aligned with the cross-controller
|
|
144
|
+
* `ingest()` convention (VolumeProfile, Footprint). Closes API audit
|
|
145
|
+
* BLOCKER-001 (5-verb intake proliferation).
|
|
146
|
+
*/
|
|
147
|
+
ingest(delta: OrderBookDelta): void
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* @deprecated since 0.1.0-alpha.1 — use {@link HeatmapController.ingest}.
|
|
151
|
+
* Kept as a non-removing alias for at least 6 months for migration.
|
|
152
|
+
*/
|
|
153
|
+
ingestDelta(delta: OrderBookDelta): void
|
|
154
|
+
/** Force a snapshot now using the controller's current state. */
|
|
155
|
+
forceSnapshot(): void
|
|
156
|
+
/**
|
|
157
|
+
* Reconstruct snapshots at a regular cadence by replaying the delta archive.
|
|
158
|
+
* `from` is the first snapshot timestamp, `to` is exclusive upper bound.
|
|
159
|
+
*/
|
|
160
|
+
replay(
|
|
161
|
+
fromTimestamp: number,
|
|
162
|
+
toTimestamp: number,
|
|
163
|
+
snapshotIntervalMs: number,
|
|
164
|
+
): ReadonlyArray<BookSnapshot>
|
|
165
|
+
|
|
166
|
+
setConfig(next: Partial<HeatmapControllerConfig>): void
|
|
167
|
+
dispose(): void
|
|
168
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `binBarToBuckets` — bar → bucket volume distribution.
|
|
3
|
+
*
|
|
4
|
+
* Modes covered:
|
|
5
|
+
* - `typical-price`: drop entire bar volume into the bucket holding
|
|
6
|
+
* `(high+low+close)/3`; silently skip if the typical price falls outside
|
|
7
|
+
* `[binMin, binMin + binCount*binSize)`.
|
|
8
|
+
* - `proportional`: split the bar's volume across every bucket that
|
|
9
|
+
* overlaps `[low, high]`, weighted by per-bucket overlap.
|
|
10
|
+
*
|
|
11
|
+
* All assertions are anchored to the actual implementation in
|
|
12
|
+
* `binning.ts`; if the algorithm diverges from the brief, the test reflects
|
|
13
|
+
* the implementation (not the brief).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { describe, it, expect } from 'vitest'
|
|
17
|
+
|
|
18
|
+
import { binBarToBuckets } from '../binning'
|
|
19
|
+
import type { VolumeProfileBar } from '../types'
|
|
20
|
+
|
|
21
|
+
function makeBuckets(n: number): Float64Array {
|
|
22
|
+
return new Float64Array(n)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe('binBarToBuckets — typical-price mode', () => {
|
|
26
|
+
it('drops the entire bar volume into the bucket containing the typical price', () => {
|
|
27
|
+
// high=100, low=90, close=95 → typical = (100+90+95)/3 = 95
|
|
28
|
+
const bar: VolumeProfileBar = { high: 100, low: 90, close: 95, volume: 1000 }
|
|
29
|
+
// Bucket grid: binMin=90, binSize=1, binCount=10 → buckets cover [90, 100).
|
|
30
|
+
// Typical price 95 → idx = floor((95-90)/1) = 5 → buckets[5].
|
|
31
|
+
const buckets = makeBuckets(10)
|
|
32
|
+
binBarToBuckets(bar, buckets, 90, 1, 10, 'typical-price')
|
|
33
|
+
expect(buckets[5]).toBe(1000)
|
|
34
|
+
// No other buckets touched.
|
|
35
|
+
for (let i = 0; i < 10; i++) {
|
|
36
|
+
if (i !== 5) expect(buckets[i]).toBe(0)
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('silently drops a bar whose typical price is below binMin', () => {
|
|
41
|
+
// bar fully below the grid: high/low/close all < binMin → typical < binMin.
|
|
42
|
+
const bar: VolumeProfileBar = { high: 10, low: 5, close: 7, volume: 500 }
|
|
43
|
+
const buckets = makeBuckets(10)
|
|
44
|
+
// Grid covers [100, 110).
|
|
45
|
+
binBarToBuckets(bar, buckets, 100, 1, 10, 'typical-price')
|
|
46
|
+
// Nothing should have been written.
|
|
47
|
+
for (let i = 0; i < 10; i++) expect(buckets[i]).toBe(0)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('silently drops a bar whose typical price is at/above binMin + binCount*binSize', () => {
|
|
51
|
+
// bar fully above the grid: typical >> binMin + binCount*binSize.
|
|
52
|
+
const bar: VolumeProfileBar = { high: 1000, low: 500, close: 700, volume: 999 }
|
|
53
|
+
const buckets = makeBuckets(10)
|
|
54
|
+
// Grid covers [0, 10).
|
|
55
|
+
binBarToBuckets(bar, buckets, 0, 1, 10, 'typical-price')
|
|
56
|
+
for (let i = 0; i < 10; i++) expect(buckets[i]).toBe(0)
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe('binBarToBuckets — proportional mode', () => {
|
|
61
|
+
it('splits a bar covering exactly 2 buckets 500/500 on equal overlap', () => {
|
|
62
|
+
// Grid: binMin=0, binSize=1, binCount=10 → buckets [0,1), [1,2), ...
|
|
63
|
+
// Bar spans [3.0, 5.0) — exactly buckets 3 and 4, each contributing
|
|
64
|
+
// half the range. volume=1000 → 500 / 500.
|
|
65
|
+
const bar: VolumeProfileBar = { high: 5, low: 3, close: 4, volume: 1000 }
|
|
66
|
+
const buckets = makeBuckets(10)
|
|
67
|
+
binBarToBuckets(bar, buckets, 0, 1, 10, 'proportional')
|
|
68
|
+
expect(buckets[3]).toBeCloseTo(500, 9)
|
|
69
|
+
expect(buckets[4]).toBeCloseTo(500, 9)
|
|
70
|
+
// Everything else zero.
|
|
71
|
+
for (let i = 0; i < 10; i++) {
|
|
72
|
+
if (i !== 3 && i !== 4) expect(buckets[i]).toBe(0)
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('puts all volume into a single bucket when the bar exactly spans one bucket', () => {
|
|
77
|
+
// Bar [4.0, 5.0) exactly equals bucket 4 → all volume there.
|
|
78
|
+
const bar: VolumeProfileBar = { high: 5, low: 4, close: 4.5, volume: 750 }
|
|
79
|
+
const buckets = makeBuckets(10)
|
|
80
|
+
binBarToBuckets(bar, buckets, 0, 1, 10, 'proportional')
|
|
81
|
+
// Implementation note: `hi = 5`, ceil((5-0)/1) - 1 = 4 → lastIdx = 4.
|
|
82
|
+
// firstIdx = floor((4-0)/1) = 4. So only bucket 4 gets volume.
|
|
83
|
+
expect(buckets[4]).toBeCloseTo(750, 9)
|
|
84
|
+
for (let i = 0; i < 10; i++) {
|
|
85
|
+
if (i !== 4) expect(buckets[i]).toBe(0)
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('splits across 3 buckets proportional to per-bucket overlap', () => {
|
|
90
|
+
// Bar [2.5, 5.5), grid binSize=1: overlaps with buckets 2, 3, 4, 5.
|
|
91
|
+
// bucket 2 [2, 3): overlap [2.5, 3.0) = 0.5
|
|
92
|
+
// bucket 3 [3, 4): overlap [3.0, 4.0) = 1.0
|
|
93
|
+
// bucket 4 [4, 5): overlap [4.0, 5.0) = 1.0
|
|
94
|
+
// bucket 5 [5, 6): overlap [5.0, 5.5) = 0.5
|
|
95
|
+
// Total overlap span = 3.0; barHigh - barLow = 3.0; volPerUnit = 900/3 = 300.
|
|
96
|
+
// Distribution: 150 / 300 / 300 / 150.
|
|
97
|
+
const bar: VolumeProfileBar = { high: 5.5, low: 2.5, close: 4, volume: 900 }
|
|
98
|
+
const buckets = makeBuckets(10)
|
|
99
|
+
binBarToBuckets(bar, buckets, 0, 1, 10, 'proportional')
|
|
100
|
+
expect(buckets[2]).toBeCloseTo(150, 9)
|
|
101
|
+
expect(buckets[3]).toBeCloseTo(300, 9)
|
|
102
|
+
expect(buckets[4]).toBeCloseTo(300, 9)
|
|
103
|
+
expect(buckets[5]).toBeCloseTo(150, 9)
|
|
104
|
+
// Sum equals the bar volume (conservation).
|
|
105
|
+
const sum = buckets.reduce((a, b) => a + b, 0)
|
|
106
|
+
expect(sum).toBeCloseTo(900, 9)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('drops a bar fully outside the configured price range (both sides)', () => {
|
|
110
|
+
// Below the grid: binMin=100, binCount=10, binSize=1 → grid [100, 110).
|
|
111
|
+
const below: VolumeProfileBar = { high: 50, low: 40, close: 45, volume: 500 }
|
|
112
|
+
const b1 = makeBuckets(10)
|
|
113
|
+
binBarToBuckets(below, b1, 100, 1, 10, 'proportional')
|
|
114
|
+
expect(b1.reduce((a, b) => a + b, 0)).toBe(0)
|
|
115
|
+
|
|
116
|
+
// Above the grid.
|
|
117
|
+
const above: VolumeProfileBar = { high: 500, low: 400, close: 450, volume: 500 }
|
|
118
|
+
const b2 = makeBuckets(10)
|
|
119
|
+
binBarToBuckets(above, b2, 100, 1, 10, 'proportional')
|
|
120
|
+
expect(b2.reduce((a, b) => a + b, 0)).toBe(0)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('treats a zero-range bar (high === low) like typical-price (lands in one bucket)', () => {
|
|
124
|
+
// Doji: high == low == close = 4.5 → falls in bucket 4.
|
|
125
|
+
const bar: VolumeProfileBar = { high: 4.5, low: 4.5, close: 4.5, volume: 800 }
|
|
126
|
+
const buckets = makeBuckets(10)
|
|
127
|
+
binBarToBuckets(bar, buckets, 0, 1, 10, 'proportional')
|
|
128
|
+
expect(buckets[4]).toBe(800)
|
|
129
|
+
for (let i = 0; i < 10; i++) {
|
|
130
|
+
if (i !== 4) expect(buckets[i]).toBe(0)
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `findPOCIndex` — argmax over the bucket histogram with a
|
|
3
|
+
* deterministic lowest-index tie-breaker.
|
|
4
|
+
*
|
|
5
|
+
* Implementation behaviour (see `poc.ts` header for rationale):
|
|
6
|
+
* - Strictly-greater wins → earlier index is preserved on ties.
|
|
7
|
+
* - Empty array returns `-1`.
|
|
8
|
+
* - All zeros returns `0` (first bucket "wins" because no strictly-greater
|
|
9
|
+
* value is ever found).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { describe, it, expect } from 'vitest'
|
|
13
|
+
|
|
14
|
+
import { findPOCIndex } from '../poc'
|
|
15
|
+
|
|
16
|
+
function f(arr: number[]): Float64Array {
|
|
17
|
+
return Float64Array.from(arr)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('findPOCIndex', () => {
|
|
21
|
+
it('returns the index of the largest bucket (plain argmax)', () => {
|
|
22
|
+
// Buckets [10, 50, 30, 20] → max=50 at index 1.
|
|
23
|
+
expect(findPOCIndex(f([10, 50, 30, 20]))).toBe(1)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('resolves ties to the lowest index', () => {
|
|
27
|
+
// All equal → first index wins because comparator is strict `>`.
|
|
28
|
+
expect(findPOCIndex(f([40, 40, 40]))).toBe(0)
|
|
29
|
+
// Tie between index 2 and 5; the lower (2) wins.
|
|
30
|
+
expect(findPOCIndex(f([1, 2, 99, 3, 4, 99, 5]))).toBe(2)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('returns 0 for an all-zero histogram (documented edge case)', () => {
|
|
34
|
+
// bestVol starts at buckets[0] = 0; no later value is strictly greater,
|
|
35
|
+
// so the index sticks at 0. The controller treats this state via the
|
|
36
|
+
// Value-Area total-volume zero branch, not by inspecting the POC.
|
|
37
|
+
expect(findPOCIndex(f([0, 0, 0, 0, 0]))).toBe(0)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('returns -1 for an empty array', () => {
|
|
41
|
+
// Sanity for the explicit guard at the top of `findPOCIndex`.
|
|
42
|
+
expect(findPOCIndex(new Float64Array(0))).toBe(-1)
|
|
43
|
+
})
|
|
44
|
+
})
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `computeValueArea` — greedy expansion around POC.
|
|
3
|
+
*
|
|
4
|
+
* Algorithm summary (see `valueArea.ts` header for full rationale):
|
|
5
|
+
* - Seed the VA with the POC bucket.
|
|
6
|
+
* - Each step compares `buckets[vahIdx+1]` vs `buckets[valIdx-1]` and
|
|
7
|
+
* annexes the larger side.
|
|
8
|
+
* - Ties → "expand toward POC" rule: pick the side whose CURRENT boundary
|
|
9
|
+
* is closer to POC. Equidistant ties → upper side wins.
|
|
10
|
+
* - Edge POC, boundary exhaustion, zero-volume, percent clamping all
|
|
11
|
+
* covered by dedicated tests below.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { describe, it, expect } from 'vitest'
|
|
15
|
+
|
|
16
|
+
import { computeValueArea } from '../valueArea'
|
|
17
|
+
import { findPOCIndex } from '../poc'
|
|
18
|
+
|
|
19
|
+
function f(arr: number[]): Float64Array {
|
|
20
|
+
return Float64Array.from(arr)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('computeValueArea — symmetric / skewed distributions', () => {
|
|
24
|
+
it('centres the VA around the middle bucket on a symmetric distribution at target=0.70', () => {
|
|
25
|
+
// Buckets: [5,10,20,30,40,30,20,10,5], n=9, total=170, POC=4.
|
|
26
|
+
// Target = 119. Trace:
|
|
27
|
+
// start vaVol=40 (POC)
|
|
28
|
+
// step1 up=30, down=30 tie, equidist → goUp → vahIdx=5, vaVol=70
|
|
29
|
+
// step2 up=20, down=30 → down wins → valIdx=3, vaVol=100
|
|
30
|
+
// step3 up=20, down=20 tie, equidist (distUp=1,distDown=1) → goUp
|
|
31
|
+
// → vahIdx=6, vaVol=120
|
|
32
|
+
// 120 >= 119 → stop.
|
|
33
|
+
const buckets = f([5, 10, 20, 30, 40, 30, 20, 10, 5])
|
|
34
|
+
const poc = findPOCIndex(buckets) // 4
|
|
35
|
+
const res = computeValueArea(buckets, poc, 0.7)
|
|
36
|
+
expect(res.pocIndex).toBe(4)
|
|
37
|
+
expect(res.valIndex).toBe(3)
|
|
38
|
+
expect(res.vahIndex).toBe(6)
|
|
39
|
+
expect(res.totalVolume).toBe(170)
|
|
40
|
+
expect(res.vaVolume).toBe(120)
|
|
41
|
+
expect(res.vaPercent).toBeCloseTo(120 / 170, 9)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('captures the heavy right tail on a right-skewed distribution; tie pulls back toward POC', () => {
|
|
45
|
+
// [1,1,1,2,3,5,10,40,20,10], n=10, total=92, POC=7 (value 40).
|
|
46
|
+
// Target = 64.4. Trace:
|
|
47
|
+
// vaVol=40 (valIdx=7, vahIdx=7)
|
|
48
|
+
// step1 up=buckets[8]=20, down=buckets[6]=10 → up wins → vahIdx=8, vaVol=60
|
|
49
|
+
// step2 up=buckets[9]=10, down=buckets[6]=10 → TIE.
|
|
50
|
+
// distUp = vahIdx - poc = 1; distDown = poc - valIdx = 0.
|
|
51
|
+
// distDown < distUp → extend the side closer to POC (down).
|
|
52
|
+
// → valIdx=6, vaVol=70
|
|
53
|
+
// 70 >= 64.4 → stop.
|
|
54
|
+
// VA = [6, 8]. The heavy right tail is captured in step 1 (the dominant
|
|
55
|
+
// upper neighbor); the subsequent tie rebalances toward POC per the
|
|
56
|
+
// CME-traditional "expand toward POC" tie-break — see valueArea.ts header.
|
|
57
|
+
const buckets = f([1, 1, 1, 2, 3, 5, 10, 40, 20, 10])
|
|
58
|
+
const poc = findPOCIndex(buckets) // 7
|
|
59
|
+
const res = computeValueArea(buckets, poc, 0.7)
|
|
60
|
+
expect(res.pocIndex).toBe(7)
|
|
61
|
+
expect(res.valIndex).toBe(6)
|
|
62
|
+
expect(res.vahIndex).toBe(8)
|
|
63
|
+
expect(res.vaVolume).toBe(70)
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
describe('computeValueArea — edge POC (only one expansion direction)', () => {
|
|
68
|
+
it('expands only upward when POC is at index 0', () => {
|
|
69
|
+
// POC at the bottom — `canDown` is always false; expansion is forced up.
|
|
70
|
+
// Buckets: [100, 50, 25, 10, 5], total=190, target=133.
|
|
71
|
+
// Start vaVol=100; canDown=false branch picks vahIdx=1 → vaVol=150 >= 133.
|
|
72
|
+
const buckets = f([100, 50, 25, 10, 5])
|
|
73
|
+
const res = computeValueArea(buckets, 0, 0.7)
|
|
74
|
+
expect(res.pocIndex).toBe(0)
|
|
75
|
+
expect(res.valIndex).toBe(0)
|
|
76
|
+
expect(res.vahIndex).toBe(1)
|
|
77
|
+
expect(res.vaVolume).toBe(150)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('expands only downward when POC is at index N-1', () => {
|
|
81
|
+
// POC at the top — `canUp` is always false.
|
|
82
|
+
// Buckets: [5, 10, 25, 50, 100], total=190, target=133.
|
|
83
|
+
// Start vaVol=100; canUp=false branch picks valIdx=3 → vaVol=150 >= 133.
|
|
84
|
+
const buckets = f([5, 10, 25, 50, 100])
|
|
85
|
+
const res = computeValueArea(buckets, 4, 0.7)
|
|
86
|
+
expect(res.pocIndex).toBe(4)
|
|
87
|
+
expect(res.vahIndex).toBe(4) // N-1
|
|
88
|
+
expect(res.valIndex).toBe(3)
|
|
89
|
+
expect(res.vaVolume).toBe(150)
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
describe('computeValueArea — tie-breaker behaviour (CME-traditional)', () => {
|
|
94
|
+
it('on an upper==lower tie with asymmetric distances, extends the side whose current boundary is closer to POC', () => {
|
|
95
|
+
// Construct a state where after two forced upward steps a tie occurs
|
|
96
|
+
// with distDown < distUp:
|
|
97
|
+
//
|
|
98
|
+
// Buckets: [5,5,5,5,30,10,8,5,5], n=9, POC=4, total=78, target=0.7*78=54.6
|
|
99
|
+
//
|
|
100
|
+
// start vaVol=30 (valIdx=4, vahIdx=4)
|
|
101
|
+
// step1 up=10, down=5 → up wins → vahIdx=5, vaVol=40
|
|
102
|
+
// step2 up=buckets[6]=8, down=buckets[3]=5 → up wins → vahIdx=6, vaVol=48
|
|
103
|
+
// (now distUp=2, distDown=0)
|
|
104
|
+
// step3 up=buckets[7]=5, down=buckets[3]=5 → TIE.
|
|
105
|
+
// distUp=2, distDown=0 → distDown < distUp → goDown
|
|
106
|
+
// (closer-to-POC boundary extends away from POC)
|
|
107
|
+
// → valIdx=3, vaVol=53
|
|
108
|
+
// step4 up=buckets[7]=5, down=buckets[2]=5 → TIE.
|
|
109
|
+
// distUp=2, distDown=1 → distDown<distUp → goDown
|
|
110
|
+
// → valIdx=2, vaVol=58 >= 54.6 ✓
|
|
111
|
+
//
|
|
112
|
+
// Final: valIdx=2, vahIdx=6.
|
|
113
|
+
const buckets = f([5, 5, 5, 5, 30, 10, 8, 5, 5])
|
|
114
|
+
const res = computeValueArea(buckets, 4, 0.7)
|
|
115
|
+
expect(res.pocIndex).toBe(4)
|
|
116
|
+
// The tie was resolved by extending the boundary CLOSER to POC —
|
|
117
|
+
// i.e. the down side, which had distance 0 vs the up side's distance 2.
|
|
118
|
+
// This is the implementation's interpretation of "toward POC".
|
|
119
|
+
expect(res.valIndex).toBe(2)
|
|
120
|
+
expect(res.vahIndex).toBe(6)
|
|
121
|
+
expect(res.vaVolume).toBe(58)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('on an equidistant tie, prefers the upper side (documented rule B)', () => {
|
|
125
|
+
// Buckets: [2,5,5,10,5,5,2], n=7, POC=3, total=34, target=23.8.
|
|
126
|
+
// start vaVol=10
|
|
127
|
+
// step1 up=5, down=5 tie, distUp=0, distDown=0 → goUp → vahIdx=4, vaVol=15
|
|
128
|
+
// step2 up=5, down=5 tie, distUp=1, distDown=1 → goUp → vahIdx=5, vaVol=20
|
|
129
|
+
// step3 up=2, down=5 → down wins → valIdx=2, vaVol=25 >= 23.8 ✓
|
|
130
|
+
const buckets = f([2, 5, 5, 10, 5, 5, 2])
|
|
131
|
+
const res = computeValueArea(buckets, 3, 0.7)
|
|
132
|
+
expect(res.pocIndex).toBe(3)
|
|
133
|
+
expect(res.vahIndex).toBe(5)
|
|
134
|
+
expect(res.valIndex).toBe(2)
|
|
135
|
+
expect(res.vaVolume).toBe(25)
|
|
136
|
+
})
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
describe('computeValueArea — percent clamping & target sweeps', () => {
|
|
140
|
+
it('sweeps the entire non-zero range at target=1.0', () => {
|
|
141
|
+
// Buckets: [5,10,5], n=3, POC=1, total=20, target=20.
|
|
142
|
+
// vaVol=10; up=5,down=5 tie equidist → goUp → vahIdx=2, vaVol=15
|
|
143
|
+
// canUp=false, canDown=true → goDown → valIdx=0, vaVol=20 ✓
|
|
144
|
+
const buckets = f([5, 10, 5])
|
|
145
|
+
const res = computeValueArea(buckets, 1, 1.0)
|
|
146
|
+
expect(res.valIndex).toBe(0)
|
|
147
|
+
expect(res.vahIndex).toBe(2)
|
|
148
|
+
expect(res.vaVolume).toBe(20)
|
|
149
|
+
expect(res.vaPercent).toBeCloseTo(1, 9)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('produces a narrower VA at target=0.5 than at target=0.7 (monotonicity)', () => {
|
|
153
|
+
const buckets = f([5, 10, 20, 30, 40, 30, 20, 10, 5])
|
|
154
|
+
const poc = findPOCIndex(buckets) // 4
|
|
155
|
+
const half = computeValueArea(buckets, poc, 0.5)
|
|
156
|
+
const seventy = computeValueArea(buckets, poc, 0.7)
|
|
157
|
+
const halfWidth = half.vahIndex - half.valIndex
|
|
158
|
+
const seventyWidth = seventy.vahIndex - seventy.valIndex
|
|
159
|
+
expect(halfWidth).toBeLessThanOrEqual(seventyWidth)
|
|
160
|
+
// 50% target is 85; 40 + 30 + 30 = 100 reaches it well before 70%
|
|
161
|
+
// does so the difference must be strictly visible.
|
|
162
|
+
expect(halfWidth).toBeLessThan(seventyWidth)
|
|
163
|
+
expect(half.vaPercent).toBeLessThanOrEqual(seventy.vaPercent)
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('returns the degenerate result for an all-zero histogram', () => {
|
|
167
|
+
// Documented zero-volume behaviour: VA collapses to POC, percent 0.
|
|
168
|
+
const buckets = f([0, 0, 0, 0, 0])
|
|
169
|
+
const res = computeValueArea(buckets, 2, 0.7)
|
|
170
|
+
expect(res.vahIndex).toBe(2)
|
|
171
|
+
expect(res.valIndex).toBe(2)
|
|
172
|
+
expect(res.pocIndex).toBe(2)
|
|
173
|
+
expect(res.totalVolume).toBe(0)
|
|
174
|
+
expect(res.vaVolume).toBe(0)
|
|
175
|
+
expect(res.vaPercent).toBe(0)
|
|
176
|
+
})
|
|
177
|
+
})
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bar-to-bucket volume distribution.
|
|
3
|
+
*
|
|
4
|
+
* Two modes, both described in `docs/ROADMAP.md` §3.1:
|
|
5
|
+
*
|
|
6
|
+
* - `typical-price` (fast, default): the bar's entire volume is added to the
|
|
7
|
+
* bucket containing the typical price `(high + low + close) / 3`. O(1) per
|
|
8
|
+
* bar. Good enough for live profiles and the case where each bar is small
|
|
9
|
+
* relative to the bucket size.
|
|
10
|
+
*
|
|
11
|
+
* - `proportional` (accurate): the bar's volume is split across every bucket
|
|
12
|
+
* that overlaps `[low, high]`, weighted by that bucket's price-range
|
|
13
|
+
* overlap with the bar. O(spanned buckets) per bar. Use this when bars
|
|
14
|
+
* span many buckets (e.g. higher timeframes, wide ranges).
|
|
15
|
+
*
|
|
16
|
+
* Both modes mutate the `buckets` array in place — the function is called in
|
|
17
|
+
* a tight loop and we want zero allocations on the hot path. The controller
|
|
18
|
+
* is responsible for zeroing the buckets before the first call.
|
|
19
|
+
*
|
|
20
|
+
* Out-of-range handling:
|
|
21
|
+
* - In `typical-price` mode a bar whose typical price falls outside
|
|
22
|
+
* `[binMin, binMin + binCount*binSize)` is **dropped silently**. The
|
|
23
|
+
* controller is responsible for sizing the price range so this only
|
|
24
|
+
* happens for accidentally bad inputs (e.g. zero-volume bars in a
|
|
25
|
+
* degenerate range).
|
|
26
|
+
* - In `proportional` mode the overlap with `[binMin, binMax]` is the
|
|
27
|
+
* effective contribution; a bar fully outside contributes nothing, a bar
|
|
28
|
+
* partially overlapping contributes proportionally.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import type { BinningMode, VolumeProfileBar } from './types'
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Distribute one bar's volume into `buckets`.
|
|
35
|
+
*
|
|
36
|
+
* Preconditions (not checked, hot path):
|
|
37
|
+
* - `buckets.length === binCount`
|
|
38
|
+
* - `binSize > 0`
|
|
39
|
+
* - `binCount >= 1`
|
|
40
|
+
*
|
|
41
|
+
* @param bar The OHLC bar (only `high`/`low`/`close`/`volume` are read).
|
|
42
|
+
* @param buckets Histogram array — written in place.
|
|
43
|
+
* @param binMin Low edge of bucket 0.
|
|
44
|
+
* @param binSize Price width of one bucket.
|
|
45
|
+
* @param binCount Number of buckets (must equal `buckets.length`).
|
|
46
|
+
* @param mode `typical-price` (fast) or `proportional` (accurate).
|
|
47
|
+
*
|
|
48
|
+
* @internal — building block used by the corresponding controller
|
|
49
|
+
* factory. Reachable today via the top-level `@klinechart-quant/core`
|
|
50
|
+
* barrel but **NOT part of the supported public API**. typedoc / api-
|
|
51
|
+
* extractor hide it from generated docs. Prefer the controller
|
|
52
|
+
* factory (e.g. `createVolumeProfileController`) for stable user code.
|
|
53
|
+
* Closes API audit BLOCKER-002 (export * leakage taxonomy).
|
|
54
|
+
*/
|
|
55
|
+
export function binBarToBuckets(
|
|
56
|
+
bar: VolumeProfileBar,
|
|
57
|
+
buckets: Float64Array,
|
|
58
|
+
binMin: number,
|
|
59
|
+
binSize: number,
|
|
60
|
+
binCount: number,
|
|
61
|
+
mode: BinningMode,
|
|
62
|
+
): void {
|
|
63
|
+
if (bar.volume === 0 || binCount === 0 || binSize <= 0) return
|
|
64
|
+
|
|
65
|
+
if (mode === 'typical-price') {
|
|
66
|
+
const tp = (bar.high + bar.low + bar.close) / 3
|
|
67
|
+
const idx = Math.floor((tp - binMin) / binSize)
|
|
68
|
+
if (idx < 0 || idx >= binCount) return
|
|
69
|
+
buckets[idx] = (buckets[idx] ?? 0) + bar.volume
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Proportional mode — split the bar's volume across [low, high] by
|
|
74
|
+
// per-bucket overlap with the bar's range.
|
|
75
|
+
//
|
|
76
|
+
// The bar's price extent is [low, high]; the buckets cover the price
|
|
77
|
+
// axis at [binMin + i*binSize, binMin + (i+1)*binSize). We compute the
|
|
78
|
+
// overlap of each touched bucket with [low, high] and weight by it.
|
|
79
|
+
//
|
|
80
|
+
// Degenerate-range bars (high === low, e.g. a doji at exactly one
|
|
81
|
+
// tick) fall back to the typical-price behaviour — putting the volume
|
|
82
|
+
// in the bucket containing `low` is the correct limit as
|
|
83
|
+
// `high - low -> 0`.
|
|
84
|
+
const binMax = binMin + binCount * binSize
|
|
85
|
+
const barLow = bar.low
|
|
86
|
+
const barHigh = bar.high
|
|
87
|
+
|
|
88
|
+
// Drop bars fully outside the configured price range — they can't
|
|
89
|
+
// contribute to any bucket.
|
|
90
|
+
if (barHigh < binMin || barLow >= binMax) return
|
|
91
|
+
|
|
92
|
+
if (barHigh === barLow) {
|
|
93
|
+
const idx = Math.floor((barLow - binMin) / binSize)
|
|
94
|
+
if (idx < 0 || idx >= binCount) return
|
|
95
|
+
buckets[idx] = (buckets[idx] ?? 0) + bar.volume
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Clamp the bar's range to the bucket grid so overlap math doesn't
|
|
100
|
+
// include the out-of-range portion.
|
|
101
|
+
const lo = barLow < binMin ? binMin : barLow
|
|
102
|
+
const hi = barHigh > binMax ? binMax : barHigh
|
|
103
|
+
|
|
104
|
+
const firstIdx = Math.floor((lo - binMin) / binSize)
|
|
105
|
+
// For `hi` we use ceiling-of-(hi - binMin)/binSize, minus 1, so the
|
|
106
|
+
// top boundary `hi === binMin + k*binSize` belongs to bucket k-1
|
|
107
|
+
// (consistent with the half-open bucket convention `[lo, hi)`).
|
|
108
|
+
let lastIdx = Math.ceil((hi - binMin) / binSize) - 1
|
|
109
|
+
if (lastIdx < firstIdx) lastIdx = firstIdx
|
|
110
|
+
|
|
111
|
+
// Effective span used as the denominator. We weight against the
|
|
112
|
+
// *clamped* span (the part actually inside the bucket grid) so a bar
|
|
113
|
+
// that hangs off the bottom doesn't get "phantom" volume into the
|
|
114
|
+
// visible buckets. Trade-off: this means a bar partially outside
|
|
115
|
+
// contributes only its inside-range fraction. This matches the
|
|
116
|
+
// intuition of "what fraction of the bar's range fell into the
|
|
117
|
+
// profile window".
|
|
118
|
+
const span = hi - lo
|
|
119
|
+
if (span <= 0) return
|
|
120
|
+
|
|
121
|
+
const volPerUnit = bar.volume / (barHigh - barLow)
|
|
122
|
+
|
|
123
|
+
for (let i = firstIdx; i <= lastIdx; i++) {
|
|
124
|
+
if (i < 0 || i >= binCount) continue
|
|
125
|
+
const bucketLow = binMin + i * binSize
|
|
126
|
+
const bucketHigh = bucketLow + binSize
|
|
127
|
+
const overlapLow = lo > bucketLow ? lo : bucketLow
|
|
128
|
+
const overlapHigh = hi < bucketHigh ? hi : bucketHigh
|
|
129
|
+
const overlap = overlapHigh - overlapLow
|
|
130
|
+
if (overlap <= 0) continue
|
|
131
|
+
buckets[i] = (buckets[i] ?? 0) + overlap * volPerUnit
|
|
132
|
+
}
|
|
133
|
+
}
|