@devexperts/dxcharts-lite 1.0.1 → 2.0.0
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/README.md +4 -4
- package/dist/chart/animation/canvas-animation.d.ts +1 -1
- package/dist/chart/animation/canvas-animation.js +135 -2
- package/dist/chart/animation/types/animation.d.ts +1 -1
- package/dist/chart/animation/types/animation.js +44 -2
- package/dist/chart/animation/types/color-alpha-animation.d.ts +2 -2
- package/dist/chart/animation/types/color-alpha-animation.js +61 -2
- package/dist/chart/animation/types/color-transition-animation.d.ts +2 -2
- package/dist/chart/animation/types/color-transition-animation.js +87 -2
- package/dist/chart/animation/types/viewport-movement-animation.d.ts +1 -1
- package/dist/chart/animation/types/viewport-movement-animation.js +64 -2
- package/dist/chart/animation/viewport-model-animation.d.ts +1 -1
- package/dist/chart/animation/viewport-model-animation.js +21 -2
- package/dist/chart/bootstrap.d.ts +52 -49
- package/dist/chart/bootstrap.js +450 -2
- package/dist/chart/canvas/canvas-bounds-container.d.ts +1 -6
- package/dist/chart/canvas/canvas-bounds-container.js +816 -2
- package/dist/chart/canvas/canvas-chart-html.d.ts +1 -1
- package/dist/chart/canvas/canvas-chart-html.js +17 -19
- package/dist/chart/canvas/chart-elements.d.ts +2 -5
- package/dist/chart/canvas/chart-elements.js +23 -2
- package/dist/chart/canvas/cursor.handler.d.ts +1 -1
- package/dist/chart/canvas/cursor.handler.js +123 -2
- package/dist/chart/canvas/layout-creator.d.ts +1 -1
- package/dist/chart/canvas/layout-creator.js +35 -2
- package/dist/chart/canvas/y-axis-bounds.container.d.ts +3 -3
- package/dist/chart/canvas/y-axis-bounds.container.js +68 -2
- package/dist/chart/chart-container.d.ts +1 -1
- package/dist/chart/chart-container.js +2 -2
- package/dist/chart/chart.config.d.ts +5 -6
- package/dist/chart/chart.config.js +646 -2
- package/dist/chart/chart.d.ts +62 -0
- package/dist/chart/chart.js +106 -0
- package/dist/chart/components/chart/basic-scale.d.ts +3 -3
- package/dist/chart/components/chart/basic-scale.js +32 -2
- package/dist/chart/components/chart/candle-transformer.functions.d.ts +1 -1
- package/dist/chart/components/chart/candle-transformer.functions.js +16 -2
- package/dist/chart/components/chart/candle-width-calculator.functions.d.ts +1 -1
- package/dist/chart/components/chart/candle-width-calculator.functions.js +3 -2
- package/dist/chart/components/chart/candle.functions.d.ts +1 -1
- package/dist/chart/components/chart/candle.functions.js +45 -2
- package/dist/chart/components/chart/chart-area-pan.handler.d.ts +4 -4
- package/dist/chart/components/chart/chart-area-pan.handler.js +198 -2
- package/dist/chart/components/chart/chart-base.model.d.ts +1 -1
- package/dist/chart/components/chart/chart-base.model.js +67 -2
- package/dist/chart/components/chart/chart.component.d.ts +8 -4
- package/dist/chart/components/chart/chart.component.js +409 -2
- package/dist/chart/components/chart/chart.model.d.ts +5 -12
- package/dist/chart/components/chart/chart.model.js +955 -2
- package/dist/chart/components/chart/data-series.high-low-provider.d.ts +1 -1
- package/dist/chart/components/chart/data-series.high-low-provider.js +35 -2
- package/dist/chart/components/chart/fake-candles.d.ts +1 -1
- package/dist/chart/components/chart/fake-candles.js +98 -2
- package/dist/chart/components/chart/price.formatter.d.ts +1 -1
- package/dist/chart/components/chart/price.formatter.js +36 -2
- package/dist/chart/components/chart/secondary-chart-colors-pool.d.ts +1 -1
- package/dist/chart/components/chart/secondary-chart-colors-pool.js +69 -2
- package/dist/chart/components/cross_tool/cross-tool.component.d.ts +1 -1
- package/dist/chart/components/cross_tool/cross-tool.component.js +86 -2
- package/dist/chart/components/cross_tool/cross-tool.drawer.d.ts +1 -1
- package/dist/chart/components/cross_tool/cross-tool.drawer.js +33 -2
- package/dist/chart/components/cross_tool/cross-tool.model.d.ts +1 -1
- package/dist/chart/components/cross_tool/cross-tool.model.js +108 -2
- package/dist/chart/components/cross_tool/types/cross-and-labels.drawer.d.ts +1 -1
- package/dist/chart/components/cross_tool/types/cross-and-labels.drawer.js +138 -2
- package/dist/chart/components/cross_tool/types/none.drawer.d.ts +1 -1
- package/dist/chart/components/cross_tool/types/none.drawer.js +4 -2
- package/dist/chart/components/dran-n-drop_helper/drag-n-drop-x.component.d.ts +1 -1
- package/dist/chart/components/dran-n-drop_helper/drag-n-drop-x.component.js +29 -2
- package/dist/chart/components/dran-n-drop_helper/drag-n-drop-y.component.d.ts +1 -1
- package/dist/chart/components/dran-n-drop_helper/drag-n-drop-y.component.js +25 -2
- package/dist/chart/components/dran-n-drop_helper/drag-n-drop.component.d.ts +1 -1
- package/dist/chart/components/dran-n-drop_helper/drag-n-drop.component.js +54 -2
- package/dist/chart/components/dynamic-objects/dynamic-objects.component.d.ts +13 -0
- package/dist/chart/components/dynamic-objects/dynamic-objects.component.js +20 -0
- package/dist/chart/components/dynamic-objects/dynamic-objects.drawer.d.ts +18 -0
- package/dist/chart/components/dynamic-objects/dynamic-objects.drawer.js +23 -0
- package/dist/chart/components/dynamic-objects/dynamic-objects.model.d.ts +63 -0
- package/dist/chart/components/dynamic-objects/dynamic-objects.model.js +136 -0
- package/dist/chart/components/events/events-hit-test.drawer.d.ts +1 -1
- package/dist/chart/components/events/events-hit-test.drawer.js +64 -2
- package/dist/chart/components/events/events.component.d.ts +1 -1
- package/dist/chart/components/events/events.component.js +62 -2
- package/dist/chart/components/events/events.drawer.d.ts +1 -1
- package/dist/chart/components/events/events.drawer.js +196 -2
- package/dist/chart/components/events/events.model.d.ts +1 -1
- package/dist/chart/components/events/events.model.js +70 -2
- package/dist/chart/components/grid/grid.component.d.ts +3 -3
- package/dist/chart/components/grid/grid.component.js +32 -2
- package/dist/chart/components/grid/grid.drawer.d.ts +5 -4
- package/dist/chart/components/grid/grid.drawer.js +107 -2
- package/dist/chart/components/high_low/high-low.component.d.ts +1 -1
- package/dist/chart/components/high_low/high-low.component.js +13 -2
- package/dist/chart/components/high_low/high-low.drawer.d.ts +1 -1
- package/dist/chart/components/high_low/high-low.drawer.js +127 -2
- package/dist/chart/components/highlights/highlights.component.d.ts +1 -1
- package/dist/chart/components/highlights/highlights.component.js +47 -2
- package/dist/chart/components/highlights/highlights.drawer.d.ts +1 -1
- package/dist/chart/components/highlights/highlights.drawer.js +155 -2
- package/dist/chart/components/highlights/highlights.model.d.ts +1 -1
- package/dist/chart/components/highlights/highlights.model.js +76 -2
- package/dist/chart/components/labels_generator/numeric-axis-labels.generator.d.ts +2 -2
- package/dist/chart/components/labels_generator/numeric-axis-labels.generator.js +200 -2
- package/dist/chart/components/navigation_map/navigation-map-move.handler.d.ts +3 -3
- package/dist/chart/components/navigation_map/navigation-map-move.handler.js +109 -2
- package/dist/chart/components/navigation_map/navigation-map.component.d.ts +1 -1
- package/dist/chart/components/navigation_map/navigation-map.component.js +113 -2
- package/dist/chart/components/navigation_map/navigation-map.drawer.d.ts +1 -1
- package/dist/chart/components/navigation_map/navigation-map.drawer.js +245 -2
- package/dist/chart/components/navigation_map/navigation-map.model.d.ts +1 -1
- package/dist/chart/components/navigation_map/navigation-map.model.js +34 -3
- package/dist/chart/components/pan/chart-pan.component.d.ts +3 -3
- package/dist/chart/components/pan/chart-pan.component.js +52 -2
- package/dist/chart/components/pane/extent/y-extent-component.d.ts +13 -19
- package/dist/chart/components/pane/extent/y-extent-component.js +121 -2
- package/dist/chart/components/pane/pane-hit-test.controller.d.ts +1 -1
- package/dist/chart/components/pane/pane-hit-test.controller.js +56 -2
- package/dist/chart/components/pane/pane-manager.component.d.ts +11 -6
- package/dist/chart/components/pane/pane-manager.component.js +149 -2
- package/dist/chart/components/pane/pane.component.d.ts +18 -34
- package/dist/chart/components/pane/pane.component.js +279 -2
- package/dist/chart/components/resizer/bar-resizer.component.d.ts +1 -1
- package/dist/chart/components/resizer/bar-resizer.component.js +140 -2
- package/dist/chart/components/resizer/bar-resizer.drawer.d.ts +1 -1
- package/dist/chart/components/resizer/bar-resizer.drawer.js +47 -2
- package/dist/chart/components/snapshot/snapshot.component.d.ts +1 -1
- package/dist/chart/components/snapshot/snapshot.component.js +49 -2
- package/dist/chart/components/volumes/separate-volumes.component.d.ts +7 -11
- package/dist/chart/components/volumes/separate-volumes.component.js +64 -2
- package/dist/chart/components/volumes/volume-color-resolvers.functions.d.ts +1 -1
- package/dist/chart/components/volumes/volume-color-resolvers.functions.js +19 -2
- package/dist/chart/components/volumes/volumes.component.d.ts +9 -9
- package/dist/chart/components/volumes/volumes.component.js +95 -2
- package/dist/chart/components/volumes/volumes.drawer.d.ts +6 -13
- package/dist/chart/components/volumes/volumes.drawer.js +141 -2
- package/dist/chart/components/volumes/volumes.formatter.d.ts +1 -1
- package/dist/chart/components/volumes/volumes.formatter.js +32 -2
- package/dist/chart/components/volumes/volumes.model.d.ts +3 -3
- package/dist/chart/components/volumes/volumes.model.js +44 -2
- package/dist/chart/components/watermark/water-mark.component.d.ts +1 -1
- package/dist/chart/components/watermark/water-mark.component.js +104 -2
- package/dist/chart/components/watermark/water-mark.drawer.d.ts +1 -1
- package/dist/chart/components/watermark/water-mark.drawer.js +188 -2
- package/dist/chart/components/x_axis/numeric-x-axis-labels.generator.d.ts +1 -1
- package/dist/chart/components/x_axis/numeric-x-axis-labels.generator.js +28 -2
- package/dist/chart/components/x_axis/time/parser/time-formats-matchers.functions.d.ts +1 -1
- package/dist/chart/components/x_axis/time/parser/time-formats-matchers.functions.js +188 -2
- package/dist/chart/components/x_axis/time/parser/time-formats-parser.functions.d.ts +1 -1
- package/dist/chart/components/x_axis/time/parser/time-formats-parser.functions.js +77 -2
- package/dist/chart/components/x_axis/time/parser/time-formats-validators.functions.d.ts +1 -1
- package/dist/chart/components/x_axis/time/parser/time-formats-validators.functions.js +80 -2
- package/dist/chart/components/x_axis/time/parser/time-formats.model.d.ts +1 -1
- package/dist/chart/components/x_axis/time/parser/time-formats.model.js +15 -2
- package/dist/chart/components/x_axis/time/x-axis-weights.functions.d.ts +2 -2
- package/dist/chart/components/x_axis/time/x-axis-weights.functions.js +144 -2
- package/dist/chart/components/x_axis/time/x-axis-weights.generator.d.ts +1 -1
- package/dist/chart/components/x_axis/time/x-axis-weights.generator.js +74 -2
- package/dist/chart/components/x_axis/x-axis-draw.functions.d.ts +1 -1
- package/dist/chart/components/x_axis/x-axis-draw.functions.js +50 -2
- package/dist/chart/components/x_axis/x-axis-labels.drawer.d.ts +1 -1
- package/dist/chart/components/x_axis/x-axis-labels.drawer.js +71 -2
- package/dist/chart/components/x_axis/x-axis-labels.generator.d.ts +3 -3
- package/dist/chart/components/x_axis/x-axis-labels.generator.js +333 -2
- package/dist/chart/components/x_axis/x-axis-labels.model.d.ts +1 -1
- package/dist/chart/components/x_axis/x-axis-labels.model.js +36 -2
- package/dist/chart/components/x_axis/x-axis-scale.handler.d.ts +3 -3
- package/dist/chart/components/x_axis/x-axis-scale.handler.js +61 -2
- package/dist/chart/components/x_axis/x-axis-time-labels.drawer.d.ts +1 -1
- package/dist/chart/components/x_axis/x-axis-time-labels.drawer.js +83 -2
- package/dist/chart/components/x_axis/x-axis.component.d.ts +3 -3
- package/dist/chart/components/x_axis/x-axis.component.js +120 -2
- package/dist/chart/components/y_axis/label-color.functions.d.ts +1 -1
- package/dist/chart/components/y_axis/label-color.functions.js +54 -2
- package/dist/chart/components/y_axis/numeric-y-axis-labels.generator.d.ts +5 -5
- package/dist/chart/components/y_axis/numeric-y-axis-labels.generator.js +34 -2
- package/dist/chart/components/y_axis/price_labels/data-series-y-axis-labels.provider.d.ts +4 -4
- package/dist/chart/components/y_axis/price_labels/data-series-y-axis-labels.provider.js +68 -2
- package/dist/chart/components/y_axis/price_labels/labels-positions-calculator.d.ts +1 -1
- package/dist/chart/components/y_axis/price_labels/labels-positions-calculator.js +57 -2
- package/dist/chart/components/y_axis/price_labels/last-candle-labels.provider.d.ts +1 -1
- package/dist/chart/components/y_axis/price_labels/last-candle-labels.provider.js +96 -2
- package/dist/chart/components/y_axis/price_labels/price-label.drawer.d.ts +3 -3
- package/dist/chart/components/y_axis/price_labels/price-label.drawer.js +120 -2
- package/dist/chart/components/y_axis/price_labels/y-axis-labels.model.d.ts +10 -9
- package/dist/chart/components/y_axis/price_labels/y-axis-labels.model.js +202 -2
- package/dist/chart/components/y_axis/price_labels/y-axis-price-labels.drawer.d.ts +7 -8
- package/dist/chart/components/y_axis/price_labels/y-axis-price-labels.drawer.js +59 -2
- package/dist/chart/components/y_axis/y-axis-base-labels.model.d.ts +7 -5
- package/dist/chart/components/y_axis/y-axis-base-labels.model.js +48 -2
- package/dist/chart/components/y_axis/y-axis-labels.drawer.d.ts +5 -5
- package/dist/chart/components/y_axis/y-axis-labels.drawer.js +162 -2
- package/dist/chart/components/y_axis/y-axis-scale.handler.d.ts +3 -3
- package/dist/chart/components/y_axis/y-axis-scale.handler.js +73 -2
- package/dist/chart/components/y_axis/y-axis.component.d.ts +33 -42
- package/dist/chart/components/y_axis/y-axis.component.js +221 -2
- package/dist/chart/components/y_axis/y-axis.drawer.d.ts +10 -13
- package/dist/chart/components/y_axis/y-axis.drawer.js +97 -2
- package/dist/chart/components/y_axis/y-axis.model.d.ts +10 -18
- package/dist/chart/components/y_axis/y-axis.model.js +34 -2
- package/dist/chart/drawers/chart-background.drawer.d.ts +1 -1
- package/dist/chart/drawers/chart-background.drawer.js +70 -2
- package/dist/chart/drawers/chart-type-drawers/area.drawer.d.ts +1 -1
- package/dist/chart/drawers/chart-type-drawers/area.drawer.js +70 -2
- package/dist/chart/drawers/chart-type-drawers/bar.drawer.d.ts +1 -1
- package/dist/chart/drawers/chart-type-drawers/bar.drawer.js +58 -2
- package/dist/chart/drawers/chart-type-drawers/baseline.drawer.d.ts +1 -1
- package/dist/chart/drawers/chart-type-drawers/baseline.drawer.js +85 -2
- package/dist/chart/drawers/chart-type-drawers/candle.drawer.d.ts +1 -1
- package/dist/chart/drawers/chart-type-drawers/candle.drawer.js +163 -2
- package/dist/chart/drawers/chart-type-drawers/histogram.drawer.d.ts +1 -1
- package/dist/chart/drawers/chart-type-drawers/histogram.drawer.js +58 -2
- package/dist/chart/drawers/chart-type-drawers/line.drawer.d.ts +1 -1
- package/dist/chart/drawers/chart-type-drawers/line.drawer.js +39 -2
- package/dist/chart/drawers/chart-type-drawers/scatter-plot.drawer.d.ts +1 -1
- package/dist/chart/drawers/chart-type-drawers/scatter-plot.drawer.js +20 -2
- package/dist/chart/drawers/clear-canvas.drawer.d.ts +1 -1
- package/dist/chart/drawers/clear-canvas.drawer.js +15 -2
- package/dist/chart/drawers/composite.drawer.d.ts +1 -1
- package/dist/chart/drawers/composite.drawer.js +68 -2
- package/dist/chart/drawers/data-series-drawers/candle-series-wrapper.d.ts +1 -1
- package/dist/chart/drawers/data-series-drawers/candle-series-wrapper.js +51 -2
- package/dist/chart/drawers/data-series-drawers/color-candle.drawer.d.ts +1 -1
- package/dist/chart/drawers/data-series-drawers/color-candle.drawer.js +36 -2
- package/dist/chart/drawers/data-series-drawers/data-series-drawers.utils.d.ts +1 -1
- package/dist/chart/drawers/data-series-drawers/data-series-drawers.utils.js +17 -2
- package/dist/chart/drawers/data-series-drawers/difference-cloud.drawer.d.ts +1 -1
- package/dist/chart/drawers/data-series-drawers/difference-cloud.drawer.js +107 -2
- package/dist/chart/drawers/data-series-drawers/histogram.drawer.d.ts +1 -1
- package/dist/chart/drawers/data-series-drawers/histogram.drawer.js +24 -2
- package/dist/chart/drawers/data-series-drawers/linear.drawer.d.ts +1 -1
- package/dist/chart/drawers/data-series-drawers/linear.drawer.js +20 -2
- package/dist/chart/drawers/data-series-drawers/points.drawer.d.ts +1 -1
- package/dist/chart/drawers/data-series-drawers/points.drawer.js +20 -2
- package/dist/chart/drawers/data-series-drawers/rectangular-tool.drawer.d.ts +1 -1
- package/dist/chart/drawers/data-series-drawers/rectangular-tool.drawer.js +8 -2
- package/dist/chart/drawers/data-series-drawers/text.drawer.d.ts +1 -1
- package/dist/chart/drawers/data-series-drawers/text.drawer.js +26 -2
- package/dist/chart/drawers/data-series-drawers/trend-histogram.drawer.d.ts +1 -1
- package/dist/chart/drawers/data-series-drawers/trend-histogram.drawer.js +36 -2
- package/dist/chart/drawers/data-series-drawers/triangle.drawer.d.ts +1 -1
- package/dist/chart/drawers/data-series-drawers/triangle.drawer.js +23 -2
- package/dist/chart/drawers/data-series.drawer.d.ts +6 -8
- package/dist/chart/drawers/data-series.drawer.js +57 -2
- package/dist/chart/drawers/drawing-manager.d.ts +2 -2
- package/dist/chart/drawers/drawing-manager.js +178 -2
- package/dist/chart/drawers/ht-data-series.drawer.d.ts +1 -1
- package/dist/chart/drawers/ht-data-series.drawer.js +38 -2
- package/dist/chart/events/event-bus.d.ts +1 -1
- package/dist/chart/events/event-bus.js +74 -2
- package/dist/chart/events/events.d.ts +1 -1
- package/dist/chart/events/events.js +4 -2
- package/dist/chart/inputhandlers/candle-tap.handler.d.ts +1 -1
- package/dist/chart/inputhandlers/candle-tap.handler.js +47 -2
- package/dist/chart/inputhandlers/chart-resize.handler.d.ts +1 -1
- package/dist/chart/inputhandlers/chart-resize.handler.js +99 -2
- package/dist/chart/inputhandlers/cross-event-producer.component.d.ts +1 -1
- package/dist/chart/inputhandlers/cross-event-producer.component.js +68 -2
- package/dist/chart/inputhandlers/hover-producer.component.d.ts +3 -3
- package/dist/chart/inputhandlers/hover-producer.component.js +229 -2
- package/dist/chart/inputhandlers/main-canvas-touch.handler.d.ts +3 -3
- package/dist/chart/inputhandlers/main-canvas-touch.handler.js +81 -2
- package/dist/chart/inputlisteners/canvas-input-listener.component.d.ts +1 -1
- package/dist/chart/inputlisteners/canvas-input-listener.component.js +630 -2
- package/dist/chart/model/baseline.model.d.ts +1 -1
- package/dist/chart/model/baseline.model.js +89 -2
- package/dist/chart/model/bounds.model.d.ts +1 -1
- package/dist/chart/model/bounds.model.js +2 -2
- package/dist/chart/model/candle-hover.d.ts +1 -1
- package/dist/chart/model/candle-hover.js +71 -2
- package/dist/chart/model/candle-series-high-low.provider.d.ts +1 -1
- package/dist/chart/model/candle-series-high-low.provider.js +46 -2
- package/dist/chart/model/candle-series.model.d.ts +4 -4
- package/dist/chart/model/candle-series.model.js +246 -2
- package/dist/chart/model/candle.model.d.ts +1 -1
- package/dist/chart/model/candle.model.js +70 -2
- package/dist/chart/model/canvas.model.d.ts +1 -1
- package/dist/chart/model/canvas.model.js +229 -2
- package/dist/chart/model/chart-base-element.d.ts +1 -1
- package/dist/chart/model/chart-base-element.js +130 -2
- package/dist/chart/model/compare-series-hover.d.ts +1 -1
- package/dist/chart/model/compare-series-hover.js +26 -2
- package/dist/chart/model/data-series-view.d.ts +6 -6
- package/dist/chart/model/data-series-view.js +112 -2
- package/dist/chart/model/data-series.config.d.ts +1 -1
- package/dist/chart/model/data-series.config.js +15 -2
- package/dist/chart/model/data-series.model.d.ts +5 -5
- package/dist/chart/model/data-series.model.js +282 -2
- package/dist/chart/model/date-time.formatter.d.ts +1 -1
- package/dist/chart/model/date-time.formatter.js +152 -2
- package/dist/chart/model/hit-test-canvas.model.d.ts +2 -2
- package/dist/chart/model/hit-test-canvas.model.js +257 -2
- package/dist/chart/model/main-candle-series.model.d.ts +2 -2
- package/dist/chart/model/main-candle-series.model.js +30 -2
- package/dist/chart/model/scale.model.d.ts +3 -2
- package/dist/chart/model/scale.model.js +317 -2
- package/dist/chart/model/scaling/auto-scale.model.d.ts +1 -1
- package/dist/chart/model/scaling/auto-scale.model.js +98 -2
- package/dist/chart/model/scaling/constrait.functions.d.ts +1 -1
- package/dist/chart/model/scaling/constrait.functions.js +62 -2
- package/dist/chart/model/scaling/lock-ratio.model.d.ts +1 -1
- package/dist/chart/model/scaling/lock-ratio.model.js +18 -2
- package/dist/chart/model/scaling/move-chart.functions.d.ts +1 -1
- package/dist/chart/model/scaling/move-chart.functions.js +23 -2
- package/dist/chart/model/scaling/viewport.model.d.ts +3 -3
- package/dist/chart/model/scaling/viewport.model.js +280 -2
- package/dist/chart/model/scaling/x-zooming.functions.d.ts +1 -1
- package/dist/chart/model/scaling/x-zooming.functions.js +36 -2
- package/dist/chart/model/time-zone.model.d.ts +1 -1
- package/dist/chart/model/time-zone.model.js +110 -2
- package/dist/chart/model/visual-candle.d.ts +1 -1
- package/dist/chart/model/visual-candle.js +135 -2
- package/dist/chart/utils/__tests__/array.utils.test.d.ts +1 -1
- package/dist/chart/utils/__tests__/array.utils.test.js +136 -2
- package/dist/chart/utils/{perfomance/debounce.utils.js → __tests__/math.utils.test.d.ts} +2 -2
- package/dist/chart/utils/__tests__/math.utils.test.js +19 -0
- package/dist/chart/utils/__tests__/object.utils.test.d.ts +1 -1
- package/dist/chart/utils/__tests__/object.utils.test.js +12 -2
- package/dist/chart/utils/__tests__/priceIncrementsUtils.test.d.ts +1 -1
- package/dist/chart/utils/__tests__/priceIncrementsUtils.test.js +30 -2
- package/dist/chart/utils/array.utils.d.ts +2 -2
- package/dist/chart/utils/array.utils.js +270 -2
- package/dist/chart/utils/auto-period-detector.utils.d.ts +1 -1
- package/dist/chart/utils/auto-period-detector.utils.js +40 -2
- package/dist/chart/utils/candles-generator-ts.utils.d.ts +1 -1
- package/dist/chart/utils/candles-generator-ts.utils.js +21 -2
- package/dist/chart/utils/candles-generator.utils.d.ts +1 -1
- package/dist/chart/utils/candles-generator.utils.js +162 -2
- package/dist/chart/utils/candles.utils.d.ts +1 -1
- package/dist/chart/utils/candles.utils.js +43 -2
- package/dist/chart/utils/canvas/canvas-drawing-functions.utils.d.ts +1 -1
- package/dist/chart/utils/canvas/canvas-drawing-functions.utils.js +145 -2
- package/dist/chart/utils/canvas/canvas-font-measure-tool.utils.d.ts +1 -1
- package/dist/chart/utils/canvas/canvas-font-measure-tool.utils.js +52 -2
- package/dist/chart/utils/canvas/canvas-text-functions.utils.d.ts +1 -1
- package/dist/chart/utils/canvas/canvas-text-functions.utils.js +157 -2
- package/dist/chart/utils/color.utils.d.ts +1 -1
- package/dist/chart/utils/color.utils.js +32 -2
- package/dist/chart/utils/device/browser.utils.d.ts +1 -1
- package/dist/chart/utils/device/browser.utils.js +11 -2
- package/dist/chart/utils/device/device-detector.utils.d.ts +1 -1
- package/dist/chart/utils/device/device-detector.utils.js +25 -2
- package/dist/chart/utils/device/device-pixel-ratio.utils.d.ts +1 -1
- package/dist/chart/utils/device/device-pixel-ratio.utils.js +22 -2
- package/dist/chart/utils/device/touchpad.utils.d.ts +1 -1
- package/dist/chart/utils/device/touchpad.utils.js +92 -2
- package/dist/chart/utils/dom.utils.d.ts +1 -1
- package/dist/chart/utils/dom.utils.js +16 -2
- package/dist/chart/utils/function.utils.d.ts +1 -1
- package/dist/chart/utils/function.utils.js +17 -2
- package/dist/chart/utils/linkedList.utils.d.ts +28 -0
- package/dist/chart/utils/linkedList.utils.js +138 -0
- package/dist/chart/utils/math.utils.d.ts +1 -1
- package/dist/chart/utils/math.utils.js +116 -2
- package/dist/chart/utils/merge.utils.d.ts +1 -1
- package/dist/chart/utils/merge.utils.js +57 -2
- package/dist/chart/utils/object.utils.d.ts +1 -1
- package/dist/chart/utils/object.utils.js +44 -2
- package/dist/chart/utils/{perfomance → performance}/animation-frame-cache.utils.d.ts +1 -1
- package/dist/chart/utils/performance/animation-frame-cache.utils.js +77 -0
- package/dist/chart/utils/{perfomance → performance}/debounce.utils.d.ts +1 -1
- package/dist/chart/utils/performance/debounce.utils.js +12 -0
- package/dist/chart/utils/{perfomance → performance}/memoize.utils.d.ts +1 -1
- package/dist/chart/utils/performance/memoize.utils.js +74 -0
- package/dist/chart/utils/{perfomance → performance}/request-animation-frame-throttle.utils.d.ts +1 -1
- package/dist/chart/utils/performance/request-animation-frame-throttle.utils.js +43 -0
- package/dist/chart/utils/{perfomance → performance}/throttle.utils.d.ts +1 -1
- package/dist/chart/utils/performance/throttle.utils.js +26 -0
- package/dist/chart/utils/price-increments.utils.d.ts +2 -1
- package/dist/chart/utils/price-increments.utils.js +128 -2
- package/dist/chart/utils/uuid.utils.d.ts +1 -1
- package/dist/chart/utils/uuid.utils.js +5 -2
- package/dist/dxchart.min.js +10 -13
- package/dist/index.d.ts +4 -4
- package/dist/index.js +21 -2
- package/package.json +4 -4
- package/dist/chart/utils/perfomance/animation-frame-cache.utils.js +0 -6
- package/dist/chart/utils/perfomance/memoize.utils.js +0 -6
- package/dist/chart/utils/perfomance/request-animation-frame-throttle.utils.js +0 -6
- package/dist/chart/utils/perfomance/throttle.utils.js +0 -6
|
@@ -1,6 +1,959 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (C)
|
|
2
|
+
* Copyright (C) 2019 - 2023 Devexperts Solutions IE Limited
|
|
3
3
|
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
|
4
4
|
* If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
5
5
|
*/
|
|
6
|
-
import{Subject as p,merge as U}from"rxjs";import{distinctUntilChanged as A}from"rxjs/operators";import{ChartBaseElement as D}from"../../model/chart-base-element";import{CHART_UUID as C,CanvasElement as f,areBoundsChanged as W}from"../../canvas/canvas-bounds-container";import{getDefaultConfig as j}from"../../chart.config";import{MIN_SUPPORTED_CANVAS_SIZE as M}from"../../model/canvas.model";import{CandleSeriesModel as _}from"../../model/candle-series.model";import{copyCandle as O}from"../../model/candle.model";import{MainCandleSeriesModel as V}from"../../model/main-candle-series.model";import{candleEdgesConstrait as L}from"../../model/scaling/constrait.functions";import{pixelsToUnits as b}from"../../model/scaling/viewport.model";import{merge as X}from"../../utils/merge.utils";import{createBasicScaleViewportTransformer as Y,createTimeFrameViewportTransformer as R}from"./basic-scale";import{calculateCandleWidth as N}from"./candle-width-calculator.functions";import{deleteCandlesIndex as H,prepareCandle as S,reindexCandles as P}from"./candle.functions";import{ChartInstrument as k}from"./chart.component";import{fakeCandle as g}from"./fake-candles";import{SecondaryChartColorsPool as z}from"./secondary-chart-colors-pool";import{binarySearch as T,lastOf as w}from"../../utils/array.utils";import{searchCandleIndex as v}from"../../utils/candles.utils";import{floor as Z,round as K}from"../../utils/math.utils";export class ChartModel extends D{get mainCandleSeries(){return this.candleSeries[0]}get secondaryCandleSeries(){return this.candleSeries.filter(e=>e!==this.mainCandleSeries)}constructor(e,t,s,i,a,n,d,l,r,o){super(),this.chartBaseModel=e,this.paneManager=t,this.bus=s,this.canvasModel=i,this.config=a,this.scaleModel=n,this.formatterFactory=d,this.mainCanvasParent=l,this.canvasBoundsContainer=r,this.chartResizeHandler=o,this.prevChartWidth=0,this.prevYWidth=0,this.candleSeries=[],this.nextCandleTimeStampSubject=new p,this.axisTypeSetSubject=new p,this.chartTypeChanged=new p,this.mainInstrumentChangedSubject=new p,this.scaleInversedSubject=new p,this.offsetsChanged=new p,this.candlesTransformersByChartType={},this.lastCandleLabelsByChartType={},this.candleWidthByChartType={},this.lastTimeFrame=[0,0],this.FAKE_CANDLES_DEFAULT=100,this.toY=u=>this.mainCandleSeries.view.toY(u),this.chartTypeChanged.next(this.config.components.chart.type),this.secondaryChartColors=new z(this.config);const c=new V(this.chartBaseModel,this.paneManager.paneComponents[C].mainYExtentComponent,this.paneManager.hitTestController.getNewDataSeriesHitTestId(),this.bus,this.scaleModel,new k,this.candlesTransformersByChartType,this.candleWidthByChartType,Object.assign({},this.config.colors));c.config.type=this.config.components.chart.type,this.candleSeries.push(c),n.addXConstraint((u,m)=>L(m,this.mainCandleSeries.visualPoints,this.config.components.chart.minCandlesOffset,n.getBounds())),this.basicScaleViewportTransformer=Y(n),this.timeFrameViewportTransformer=R(n,this),this.pane=this.paneManager.paneComponents[C]}get candlesUpdatedSubject(){return this.chartBaseModel.dataUpdatedSubject}get candlesSetSubject(){return this.chartBaseModel.dataSetSubject}get candlesRemovedSubject(){return this.chartBaseModel.dataRemovedSubject}get candlesPrependSubject(){return this.chartBaseModel.dataPrependSubject}doActivate(){super.doActivate(),this.addRxSubscription(this.canvasBoundsContainer.observeBoundsChanged(f.PANE_UUID(C)).pipe(A(W)).subscribe(e=>{this.handleChartResize(e)}))}handleChartResize(e){if(e.width>M.width&&e.height>M.height){const t=this.getEffectiveChartWidth(),s=this.getEffectiveYAxisWidth();if(this.prevChartWidth===0){this.scaleModel.isViewportValid()?this.scaleModel.recalculateZoom():this.doBasicScale(),this.prevChartWidth=t,this.prevYWidth=s;return}if(s!==this.prevYWidth){if(this.config.scale.keepZoomXOnYAxisChange){const a=b(s-this.prevYWidth,this.scaleModel.zoomX);this.scaleModel.setXScale(this.scaleModel.xStart,this.scaleModel.xEnd-a)}else this.scaleModel.recalculateZoomX();this.prevYWidth=s,this.prevChartWidth=t;return}const i=b(t-this.prevChartWidth,this.scaleModel.zoomX);this.scaleModel.setXScale(this.scaleModel.xStart-i,this.scaleModel.xEnd),this.scaleModel.recalculateZoomY(),this.prevYWidth=s,this.prevChartWidth=t}}setMainCandleSeries(e,t){this.mainCandleSeries.instrument=t,this.setAllSeries({candles:e,instrument:t},this.getSecondarySeries().map(s=>{const i=s.dataPoints;return H(i),{candles:i,instrument:s.instrument}}))}setSecondaryCandleSeries(e,t=this.mainCandleSeries.instrument,s=!0){const i=y(e.map(S)),a=this.reindexCandlesBasedOnSeries(this.mainCandleSeries.dataPoints,i),n=this.secondarySeriesAdjustments(this.mainCandleSeries.dataPoints,a),l=this.secondaryCandleSeries.some(r=>r.instrument.symbol===t.symbol)?this.secondaryCandleSeries.find(r=>r.instrument.symbol===t.symbol):this.createSecondaryCandleSeriesModel(t);if(l)return l.dataPoints=n,s&&(this.scaleModel.doAutoScale(),l.recalculateVisualPoints(),this.candlesSetSubject.next(),this.bus.fireDraw([this.canvasModel.canvasId])),l}setAllSeries(e,t=[]){var s;this.mainCandleSeries.instrument=(s=e.instrument)!==null&&s!==void 0?s:this.mainCandleSeries.instrument,e.instrument&&this.mainInstrumentChangedSubject.next(e.instrument),this.rememberCurrentTimeframe();const i=y(e.candles.map(S));this.mainCandleSeries.clearData(),P(i),this.mainCandleSeries.dataPoints=i,this.secondaryCandleSeries.filter(a=>t.filter(n=>{var d;return((d=n.instrument)===null||d===void 0?void 0:d.symbol)===a.instrument.symbol}).length===0).forEach(a=>this.removeSecondaryCandleSeries(a)),t.map(a=>this.setSecondaryCandleSeries(a.candles,a.instrument,!1)),this.candleSeries.forEach(a=>{a.recalculateDataViewportIndexes(),a.recalculateVisualPoints()}),this.chartBaseModel.recalculatePeriod(),this.autoScaleOnCandles(),this.scaleModel.doAutoScale(),this.candlesSetSubject.next(),this.bus.fireDraw([this.canvasModel.canvasId])}autoScaleOnCandles(){this.scaleModel.state.autoScaleOnCandles&&(this.doBasicScale(),this.scaleModel.autoScale(!0))}doBasicScale(){this.basicScaleViewportTransformer(this.mainCandleSeries.visualPoints),this.bus.fireDraw()}doPreviousTimeFrameScale(e=null){this.timeFrameViewportTransformer(this.lastTimeFrame,e),this.scaleModel.state.autoScaleOnCandles&&this.scaleModel.doAutoScale(!0),this.bus.fireDraw()}rememberCurrentTimeframe(){this.mainCandleSeries.visualPoints.length!==0&&(this.lastTimeFrame=[this.candleFromX(this.scaleModel.toX(this.scaleModel.xStart),!0).timestamp,this.candleFromX(this.scaleModel.toX(this.scaleModel.xEnd),!0).timestamp])}updateAllSeries(e,t=[]){e=Object.assign({},e),e.instrument||(e.instrument=this.mainCandleSeries.instrument);const s=[e,...t];if(!this.candleSeries.every(l=>s.find(r=>{var o;return((o=r.instrument)===null||o===void 0?void 0:o.symbol)===l.instrument.symbol}))||t.length!==this.secondaryCandleSeries.length){console.error("All series update failed. Instruments for series are different.");return}const i=y(e.candles.map(S)),a=I(this.mainCandleSeries.dataPoints,i),n=a.candles;P(n),this.mainCandleSeries.dataPoints=n,t.map(l=>{var r,o,c,u;const m=y(l.candles.map(S)),x=I((u=(c=this.findSecondarySeriesBySymbol((o=(r=l.instrument)===null||r===void 0?void 0:r.symbol)!==null&&o!==void 0?o:""))===null||c===void 0?void 0:c.dataPoints)!==null&&u!==void 0?u:[],m).candles;return this.setSecondaryCandleSeries(x,l.instrument,!1)}),this.candleSeries.forEach(l=>{l.recalculateVisualPoints(),l.recalculateDataViewportIndexes()});const d=this.chartBaseModel.mainVisualPoints.slice(0,a.prepended).reduce((l,r)=>l+r.width,0);this.scaleModel.moveXStart(this.scaleModel.xStart+d),this.candlesPrependSubject.next({prependedCandlesWidth:d,preparedCandles:i}),this.chartBaseModel.recalculatePeriod(),this.candlesUpdatedSubject.next(),this.bus.fireDraw()}removeDataFrom(e){Object.values(this.paneManager.paneComponents).forEach(t=>{t.dataSeries.forEach(s=>{const i=T(s.dataPoints,e,n=>n.timestamp),a=i.exact?i.index:i.index+1;s.dataPoints=s.dataPoints.slice(0,a)})}),this.candlesRemovedSubject.next(),this.candlesUpdatedSubject.next(),this.canvasModel.fireDraw()}createSecondaryCandleSeriesModel(e){const t=Object.assign(Object.assign({},this.config.colors),this.secondaryChartColors.takeColorFromPool(e.symbol));return this.createCandleSeriesModel(e,t)}createCandleSeriesModel(e,t){const s=new _(this.paneManager.paneComponents[C].mainYExtentComponent,this.paneManager.hitTestController.getNewDataSeriesHitTestId(),this.bus,this.scaleModel,e,this.candlesTransformersByChartType,this.candleWidthByChartType,t);return s.config.type="line",this.candleSeries.push(s),s}removeSecondaryCandleSeries(e){return this.secondaryChartColors.addColorToPool(e.instrument.symbol),this.candleSeries=this.candleSeries.filter(t=>t!==e),e.deactivate(),this.paneManager.paneComponents[C].removeDataSeries(e),this.scaleModel.doAutoScale(),e.colors}secondarySeriesAdjustments(e,t){const s=[];return e.forEach(i=>{var a;const n=(a=i.idx)!==null&&a!==void 0?a:0,d=t[n];if(d)s.push(d);else{let l=B(t,n,-1);if(l||(l=B(t,n,1)),l){const r=O(l,n,!0);s.push(r)}}}),s}updateSecondaryCandleSeriesConfig(e,t,s){const i=X(e,j().colors);this.secondaryChartColors.updateColorConfig(t,i);const a=this.findSecondarySeriesBySymbol(t);a&&(a.config.type=s,a.updateCandleSeriesColors(i),this.bus.fireDraw([this.canvasModel.canvasId])),this.bus.fireDraw([this.canvasModel.canvasId])}setAutoScale(e){this.scaleModel.autoScale(e)}getEffectiveYAxisWidth(){return this.canvasBoundsContainer.getBounds(f.PANE_UUID_Y_AXIS(C)).width}getEffectiveChartWidth(){return this.canvasBoundsContainer.getBounds(f.PANE_UUID(C)).width}getEffectiveChartHeight(){return this.canvasBoundsContainer.getBounds(f.PANE_UUID(C)).height}setOffsets(e){this.scaleModel.updateOffsets(e);const t=this.getCandlesCountWithRightOffset(),s=this.mainCandleSeries.visualPoints;this.hasCandles()&&t>=s.length&&this.scaleModel.setXScale(this.scaleModel.xStart,this.candleFromIdx(t).startUnit),this.offsetsChanged.next(),this.bus.fireDraw()}getOffsets(){return this.scaleModel.getOffsets()}fromY(e){return this.scaleModel.fromY(e)}priceFromY(e,t){return(t!=null?t:this.mainCandleSeries).view.priceFromY(e)}fromX(e){return this.scaleModel.fromX(e)}toX(e){const t=this.candleFromIdx(e);return this.scaleModel.toX(t.centerUnit)}getBaseLine(){return this.mainCandleSeries.getBaseLine()}candleFromX(e,t=!1,s=this.mainCandleSeries){const i=this.scaleModel.fromX(e);return this.candleFromUnit(i,t,s)}candleFromUnit(e,t=!1,s=this.mainCandleSeries){var i,a;const n=T(s.visualPoints,e,c=>c.startUnit),d=s.visualPoints,l=this.getLastVisualCandle();d[n.index]!==void 0&&(e<0||l&&e>l.startUnit+l.width)&&(n.index+=K((e-d[n.index].centerUnit)/this.mainCandleSeries.meanCandleWidth));const r=n.index,o=Math.max(Math.min(d.length-1,r),0);return(r<0||r>=d.length)&&t?g(this.mainCandleSeries.dataPoints,r,this.chartBaseModel.period):(a=(i=d[o])===null||i===void 0?void 0:i.candle)!==null&&a!==void 0?a:g(this.mainCandleSeries.dataPoints,o,this.chartBaseModel.period)}candleFromTimestamp(e,t=!0){return this.chartBaseModel.dataFromTimestamp(e,t)}candleFromIdx(e){return this.chartBaseModel.dataFromIdx(e)}isReady(){return this.canvasModel.isReady()&&this.hasCandles()}registerCandlesTransformer(e,t){this.candlesTransformersByChartType[e]=t}registerCandlesWidthCalculator(e,t){this.candleWidthByChartType[e]=t}registerLastCandleLabelHandler(e,t){this.lastCandleLabelsByChartType[e]=t}hasCandles(){return this.mainCandleSeries.visualPoints.length!==0}getVisualCandle(e){var t,s,i;const a=e-((i=(s=(t=this.mainCandleSeries.visualPoints[0])===null||t===void 0?void 0:t.candle)===null||s===void 0?void 0:s.idx)!==null&&i!==void 0?i:0);return this.mainCandleSeries.visualPoints[a]}getFirstIdx(){return this.mainCandleSeries.dataIdxStart}getLastIdx(){return this.mainCandleSeries.dataIdxEnd}getFirstTimestamp(e=!0){return e?this.candleFromIdx(this.getFirstIdx()).candle.timestamp:this.candleFromUnit(this.scaleModel.xStart,!0).timestamp}getLastTimestamp(e=!0){return e?this.candleFromIdx(this.getLastIdx()).candle.timestamp:this.candleFromUnit(this.scaleModel.xEnd,!0).timestamp}setTimestampRange(e,t){const s=this.candleFromTimestamp(e).startUnit,i=this.candleFromTimestamp(t),a=i.startUnit+i.width;return this.scaleModel.setXScale(s,a)}getLastCandle(){const e=this.mainCandleSeries.dataPoints;return w(e)}getLastVisualCandle(){const e=this.mainCandleSeries.visualPoints;return w(e)}getMaxCandlesFitLength(){return Z(this.getEffectiveChartWidth()/this.config.components.chart.minWidth)}getSecondarySeries(){return this.secondaryCandleSeries}isSeriesInstrument(e,t){return e.instrument.symbol===t}findSeriesBySymbol(e){return this.candleSeries.filter(t=>this.isSeriesInstrument(t,e))}findSecondarySeriesBySymbol(e){return this.secondaryCandleSeries.find(t=>this.isSeriesInstrument(t,e))}reindexCandlesBasedOnSeries(e,t){return t.reduce((s,i)=>{const a=i.timestamp,n=v(a,!1,e,this.chartBaseModel.period);return n.index>=0&&n.index<e.length&&(i.idx=n.index,s[n.index]=i),s},[])}getPeriod(){return this.chartBaseModel.period}isCandleInViewport(e){return this.getFirstIdx()<=e&&e<=this.getLastIdx()}updateCandles(e,t=this.mainCandleSeries.instrument.symbol){const s=this.mainCandleSeries.instrument.symbol===t,i=this.findSeriesBySymbol(t);if(i.length===0){console.warn("updateCandles failed. Can't find series",t);return}i.forEach(a=>{const n=a.dataPoints;let d=!1,l=!1,r;e.forEach(o=>{var c;if(!o)return;const u=v(o.timestamp,!0,n,this.getPeriod()),m=Math.min(u.index,n.length);d=d||m===n.length,n[m]=o,o.idx=m,a.dataPoints=n,a.recalculateDataViewportIndexes(),l=l||this.isCandleInViewport(m);const x=m>=n.length-1,E=r&&o.timestamp<r.timestamp;if(x&&!E&&(r=o),d&&r&&s&&l){const F=(c=this.candleWidthByChartType[this.config.components.chart.type])!==null&&c!==void 0?c:N;this.scaleModel.moveXStart(this.scaleModel.xStart+F(r))}}),r&&a.updateCurrentPrice(r.close),l&&r&&e.length===1?this.bus.fireDraw([this.canvasModel.canvasId]):a.recalculateVisualPoints()}),this.scaleModel.doAutoScale(),this.candlesUpdatedSubject.next()}observeCandlesChanged(){return U(this.candlesSetSubject,this.candlesUpdatedSubject)}getCandles(){return this.mainCandleSeries.dataPoints}getCandlesWithFake(e=0,t){let s=this.getCandles().slice();const i=this.getCandlesCount(),a=t!=null?t:i+this.FAKE_CANDLES_DEFAULT;s=s.slice(Math.max(0,e),Math.min(i,a));const n=[],d=[];for(let r=i;r<a;r++)n.push(g(this.mainCandleSeries.dataPoints,r,this.getPeriod()));const l=Math.min(0,a);for(let r=e;r<l;r++)d.push(g(this.mainCandleSeries.dataPoints,r,this.getPeriod()));return[...d,...s,...n]}getCandlesCount(){return this.mainCandleSeries.dataPoints.length}getCandlesCountWithRightOffset(){return this.getCandlesCount()+this.getOffsets().right}clearData(){this.candleSeries.forEach(e=>e.clearData())}prependCandles(e,t){const s=e.slice(),i=[];return t.forEach(a=>{const d=v(a.timestamp,!1,e).index;d<0?i.push(a):e[d].timestamp===a.timestamp?s[d]=a:console.warn(`Couldn't update candle with timestamp ${a.timestamp}`)}),{prepended:i.length,candles:[...i,...s]}}addLastCandle(e,t=this.mainCandleSeries.instrument.symbol){this.updateCandles([e],t)}updateLastCandle(e,t=this.mainCandleSeries.instrument.symbol){this.updateCandles([e],t)}}const y=h=>h.slice().sort((e,t)=>e.timestamp===t.timestamp?0:e.timestamp>t.timestamp?1:-1),B=(h,e,t)=>{if(e>=h.length)return h[h.length-1];for(let s=e;s<h.length&&s>=0;s+=t){const i=h[s];if(i)return i}},I=(h,e)=>{const t=h.slice(),s=[],i=[];return e.forEach(a=>{const d=v(a.timestamp,!0,h).index;d<0?s.push(a):d>=h.length?i.push(a):h[d].timestamp===a.timestamp?t[d]=a:console.warn(`Couldn't update candle with timestamp ${a.timestamp}`)}),{prepended:s.length,appended:i.length,candles:[...s,...t,...i]}};
|
|
6
|
+
import { Subject, merge } from 'rxjs';
|
|
7
|
+
import { distinctUntilChanged } from 'rxjs/operators';
|
|
8
|
+
import { CHART_UUID, CanvasElement, areBoundsChanged, } from '../../canvas/canvas-bounds-container';
|
|
9
|
+
import { getDefaultConfig } from '../../chart.config';
|
|
10
|
+
import { CandleSeriesModel } from '../../model/candle-series.model';
|
|
11
|
+
import { copyCandle } from '../../model/candle.model';
|
|
12
|
+
import { MIN_SUPPORTED_CANVAS_SIZE } from '../../model/canvas.model';
|
|
13
|
+
import { ChartBaseElement } from '../../model/chart-base-element';
|
|
14
|
+
import { MainCandleSeriesModel } from '../../model/main-candle-series.model';
|
|
15
|
+
import { candleEdgesConstrait } from '../../model/scaling/constrait.functions';
|
|
16
|
+
import { pixelsToUnits } from '../../model/scaling/viewport.model';
|
|
17
|
+
import { binarySearch, lastOf } from '../../utils/array.utils';
|
|
18
|
+
import { searchCandleIndex } from '../../utils/candles.utils';
|
|
19
|
+
import { floor, round } from '../../utils/math.utils';
|
|
20
|
+
import { merge as mergeObj } from '../../utils/merge.utils';
|
|
21
|
+
import { createBasicScaleViewportTransformer, createTimeFrameViewportTransformer } from './basic-scale';
|
|
22
|
+
import { calculateCandleWidth } from './candle-width-calculator.functions';
|
|
23
|
+
import { deleteCandlesIndex, prepareCandle, reindexCandles } from './candle.functions';
|
|
24
|
+
import { ChartInstrument } from './chart.component';
|
|
25
|
+
import { fakeCandle } from './fake-candles';
|
|
26
|
+
import { SecondaryChartColorsPool } from './secondary-chart-colors-pool';
|
|
27
|
+
export class ChartModel extends ChartBaseElement {
|
|
28
|
+
get mainCandleSeries() {
|
|
29
|
+
return this.candleSeries[0];
|
|
30
|
+
}
|
|
31
|
+
get secondaryCandleSeries() {
|
|
32
|
+
return this.candleSeries.filter(s => s !== this.mainCandleSeries);
|
|
33
|
+
}
|
|
34
|
+
constructor(chartBaseModel, paneManager, bus, canvasModel, config, scale, formatterFactory, mainCanvasParent, canvasBoundsContainer, chartResizeHandler) {
|
|
35
|
+
super();
|
|
36
|
+
this.chartBaseModel = chartBaseModel;
|
|
37
|
+
this.paneManager = paneManager;
|
|
38
|
+
this.bus = bus;
|
|
39
|
+
this.canvasModel = canvasModel;
|
|
40
|
+
this.config = config;
|
|
41
|
+
this.scale = scale;
|
|
42
|
+
this.formatterFactory = formatterFactory;
|
|
43
|
+
this.mainCanvasParent = mainCanvasParent;
|
|
44
|
+
this.canvasBoundsContainer = canvasBoundsContainer;
|
|
45
|
+
this.chartResizeHandler = chartResizeHandler;
|
|
46
|
+
this.prevChartWidth = 0;
|
|
47
|
+
this.prevYWidth = 0;
|
|
48
|
+
this.candleSeries = [];
|
|
49
|
+
this.nextCandleTimeStampSubject = new Subject();
|
|
50
|
+
this.chartTypeChanged = new Subject();
|
|
51
|
+
this.mainInstrumentChangedSubject = new Subject();
|
|
52
|
+
this.scaleInversedSubject = new Subject();
|
|
53
|
+
this.offsetsChanged = new Subject();
|
|
54
|
+
this.candlesTransformersByChartType = {};
|
|
55
|
+
this.lastCandleLabelsByChartType = {};
|
|
56
|
+
this.candleWidthByChartType = {};
|
|
57
|
+
// TODO try to remove this state
|
|
58
|
+
this.lastTimeFrame = [0, 0];
|
|
59
|
+
this.FAKE_CANDLES_DEFAULT = 100;
|
|
60
|
+
this.toY = (value) => {
|
|
61
|
+
return this.mainCandleSeries.view.toY(value);
|
|
62
|
+
};
|
|
63
|
+
this.chartTypeChanged.next(this.config.components.chart.type);
|
|
64
|
+
this.secondaryChartColors = new SecondaryChartColorsPool(this.config);
|
|
65
|
+
const candleSeries = new MainCandleSeriesModel(this.chartBaseModel, this.paneManager.panes[CHART_UUID].mainExtent, this.paneManager.hitTestController.getNewDataSeriesHitTestId(), this.bus, this.scale, new ChartInstrument(), this.candlesTransformersByChartType, this.candleWidthByChartType, Object.assign({}, this.config.colors));
|
|
66
|
+
candleSeries.config.type = this.config.components.chart.type;
|
|
67
|
+
this.candleSeries.push(candleSeries);
|
|
68
|
+
scale.addXConstraint((_, state) => candleEdgesConstrait(state, this.mainCandleSeries.visualPoints, this.config.components.chart.minCandlesOffset, scale.getBounds()));
|
|
69
|
+
this.basicScaleViewportTransformer = createBasicScaleViewportTransformer(scale);
|
|
70
|
+
this.timeFrameViewportTransformer = createTimeFrameViewportTransformer(scale, this);
|
|
71
|
+
this.pane = this.paneManager.panes[CHART_UUID];
|
|
72
|
+
}
|
|
73
|
+
get candlesUpdatedSubject() {
|
|
74
|
+
return this.chartBaseModel.dataUpdatedSubject;
|
|
75
|
+
}
|
|
76
|
+
get candlesSetSubject() {
|
|
77
|
+
return this.chartBaseModel.dataSetSubject;
|
|
78
|
+
}
|
|
79
|
+
get candlesRemovedSubject() {
|
|
80
|
+
return this.chartBaseModel.dataRemovedSubject;
|
|
81
|
+
}
|
|
82
|
+
get candlesPrependSubject() {
|
|
83
|
+
return this.chartBaseModel.dataPrependSubject;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Method that activates the canvas bounds container and subscribes to its bounds changes and bar resizer changes.
|
|
87
|
+
* @protected
|
|
88
|
+
* @returns {void}
|
|
89
|
+
*/
|
|
90
|
+
doActivate() {
|
|
91
|
+
super.doActivate();
|
|
92
|
+
this.addRxSubscription(this.canvasBoundsContainer
|
|
93
|
+
.observeBoundsChanged(CanvasElement.PANE_UUID(CHART_UUID))
|
|
94
|
+
.pipe(distinctUntilChanged(areBoundsChanged))
|
|
95
|
+
.subscribe(bounds => {
|
|
96
|
+
this.handleChartResize(bounds);
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* This method keeps the same candle width on chart canvas resize, so chart candles aren't squeezed when canvas becomes smaller
|
|
101
|
+
* If chart width was changed after Yaxis width changed, scale will be adjusted to right/left, and visually nothing will be changed
|
|
102
|
+
* @private
|
|
103
|
+
* @param nextCB - Next chart bounds
|
|
104
|
+
*/
|
|
105
|
+
handleChartResize(nextCB) {
|
|
106
|
+
if (nextCB.width > MIN_SUPPORTED_CANVAS_SIZE.width && nextCB.height > MIN_SUPPORTED_CANVAS_SIZE.height) {
|
|
107
|
+
const nextChartWidth = this.getEffectiveChartWidth();
|
|
108
|
+
const nextYAxisWidth = this.getEffectiveYAxisWidth();
|
|
109
|
+
if (this.prevChartWidth === 0) {
|
|
110
|
+
this.scale.isViewportValid() ? this.scale.recalculateZoom() : this.doBasicScale();
|
|
111
|
+
this.prevChartWidth = nextChartWidth;
|
|
112
|
+
this.prevYWidth = nextYAxisWidth;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// YAxis width changed, so chart width will be changed too, but we need adjust scale to right/left
|
|
116
|
+
if (nextYAxisWidth !== this.prevYWidth) {
|
|
117
|
+
if (this.config.scale.keepZoomXOnYAxisChange) {
|
|
118
|
+
const unitDiff = pixelsToUnits(nextYAxisWidth - this.prevYWidth, this.scale.zoomX);
|
|
119
|
+
this.scale.setXScale(this.scale.xStart, this.scale.xEnd - unitDiff);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
this.scale.recalculateZoomX();
|
|
123
|
+
}
|
|
124
|
+
this.prevYWidth = nextYAxisWidth;
|
|
125
|
+
this.prevChartWidth = nextChartWidth;
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
// YAxis has the same width, so keep the same candle width on chart resize
|
|
129
|
+
const unitDiff = pixelsToUnits(nextChartWidth - this.prevChartWidth, this.scale.zoomX);
|
|
130
|
+
this.scale.setXScale(this.scale.xStart - unitDiff, this.scale.xEnd);
|
|
131
|
+
// height also could be changed - we need correct zoomY
|
|
132
|
+
this.scale.recalculateZoomY();
|
|
133
|
+
this.prevYWidth = nextYAxisWidth;
|
|
134
|
+
this.prevChartWidth = nextChartWidth;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Sets the main candle series with the provided candles and instrument.
|
|
139
|
+
* @param {Array<Candle>} candles - The array of candles to set as the main candle series.
|
|
140
|
+
* @param {ChartInstrument} instrument - The instrument to set for the main candle series.
|
|
141
|
+
* @returns {void}
|
|
142
|
+
*/
|
|
143
|
+
setMainCandleSeries(candles, instrument) {
|
|
144
|
+
this.mainCandleSeries.instrument = instrument;
|
|
145
|
+
this.setAllSeries({
|
|
146
|
+
candles,
|
|
147
|
+
instrument,
|
|
148
|
+
}, this.getSecondarySeries().map(series => {
|
|
149
|
+
const originalCandles = series.dataPoints;
|
|
150
|
+
// reset the idx of candles to calculate them again
|
|
151
|
+
deleteCandlesIndex(originalCandles);
|
|
152
|
+
return {
|
|
153
|
+
candles: originalCandles,
|
|
154
|
+
instrument: series.instrument,
|
|
155
|
+
};
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Sets a secondary candle series based on the provided candles array and instrument.
|
|
160
|
+
* @param {Array<Candle>} candles - An array of candles to set as the secondary candle series.
|
|
161
|
+
* @param {ChartInstrument} instrument - The instrument to set as the secondary candle series. Defaults to the main candle series instrument.
|
|
162
|
+
* @param {boolean} recalculateAndUpdate - A boolean indicating whether to recalculate and update the candle series. Defaults to true.
|
|
163
|
+
* @returns {CandleSeriesModel | undefined} - The newly created secondary candle series model or undefined if it could not be created.
|
|
164
|
+
*/
|
|
165
|
+
setSecondaryCandleSeries(candles, instrument = this.mainCandleSeries.instrument, recalculateAndUpdate = true) {
|
|
166
|
+
const prepareCandleCandles = sortCandles(candles.map(prepareCandle));
|
|
167
|
+
// set correct indexes based on main candles timestamp
|
|
168
|
+
const reindexCandles = this.reindexCandlesBasedOnSeries(this.mainCandleSeries.dataPoints, prepareCandleCandles);
|
|
169
|
+
// ensure there are no gaps in new candles
|
|
170
|
+
const secondaryCandles = this.secondarySeriesAdjustments(this.mainCandleSeries.dataPoints, reindexCandles);
|
|
171
|
+
// create a new secondary series model if it doesn't already exist
|
|
172
|
+
const isSymbolExist = this.secondaryCandleSeries.some(candleSeries => candleSeries.instrument.symbol === instrument.symbol);
|
|
173
|
+
const candleSeriesModel = isSymbolExist
|
|
174
|
+
? this.secondaryCandleSeries.find(candleSeries => candleSeries.instrument.symbol === instrument.symbol)
|
|
175
|
+
: this.createSecondaryCandleSeriesModel(instrument);
|
|
176
|
+
if (!candleSeriesModel) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
candleSeriesModel.dataPoints = secondaryCandles;
|
|
180
|
+
if (recalculateAndUpdate) {
|
|
181
|
+
// calculate X and Y bounds
|
|
182
|
+
this.scale.doAutoScale();
|
|
183
|
+
// now the visual candles
|
|
184
|
+
candleSeriesModel.recalculateVisualPoints();
|
|
185
|
+
this.candlesSetSubject.next();
|
|
186
|
+
this.bus.fireDraw([this.canvasModel.canvasId]);
|
|
187
|
+
}
|
|
188
|
+
return candleSeriesModel;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Shouldn't be called outside chart-core, use ChartComponent#setAllSeries instead
|
|
192
|
+
* @param mainSeries
|
|
193
|
+
* @param secondarySeries
|
|
194
|
+
*/
|
|
195
|
+
setAllSeries(mainSeries, secondarySeries = []) {
|
|
196
|
+
var _a;
|
|
197
|
+
this.mainCandleSeries.instrument = (_a = mainSeries.instrument) !== null && _a !== void 0 ? _a : this.mainCandleSeries.instrument;
|
|
198
|
+
if (mainSeries.instrument) {
|
|
199
|
+
this.mainInstrumentChangedSubject.next(mainSeries.instrument);
|
|
200
|
+
}
|
|
201
|
+
this.rememberCurrentTimeframe();
|
|
202
|
+
const prepareCandleCandles = sortCandles(mainSeries.candles.map(prepareCandle));
|
|
203
|
+
this.mainCandleSeries.clearData();
|
|
204
|
+
reindexCandles(prepareCandleCandles);
|
|
205
|
+
this.mainCandleSeries.dataPoints = prepareCandleCandles;
|
|
206
|
+
// deactivate deleted series
|
|
207
|
+
this.secondaryCandleSeries
|
|
208
|
+
.filter(series => {
|
|
209
|
+
return secondarySeries.filter(s => { var _a; return ((_a = s.instrument) === null || _a === void 0 ? void 0 : _a.symbol) === series.instrument.symbol; }).length === 0;
|
|
210
|
+
})
|
|
211
|
+
.forEach(series => this.removeSecondaryCandleSeries(series));
|
|
212
|
+
// re-create series
|
|
213
|
+
secondarySeries.map(series => this.setSecondaryCandleSeries(series.candles, series.instrument, false));
|
|
214
|
+
// do visual recalculations
|
|
215
|
+
this.candleSeries.forEach(series => {
|
|
216
|
+
series.recalculateDataViewportIndexes();
|
|
217
|
+
series.recalculateVisualPoints();
|
|
218
|
+
});
|
|
219
|
+
this.chartBaseModel.recalculatePeriod();
|
|
220
|
+
this.autoScaleOnCandles();
|
|
221
|
+
this.scale.doAutoScale();
|
|
222
|
+
this.candlesSetSubject.next();
|
|
223
|
+
this.bus.fireDraw([this.canvasModel.canvasId]);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* This function checks if the autoScaleOnCandles state is true. If it is, it calls the doBasicScale() function and then calls the autoScale() function with a true parameter.
|
|
227
|
+
* @function
|
|
228
|
+
* @name autoScaleOnCandles
|
|
229
|
+
* @memberof ClassName
|
|
230
|
+
* @returns {void}
|
|
231
|
+
*/
|
|
232
|
+
autoScaleOnCandles() {
|
|
233
|
+
if (this.scale.state.autoScaleOnCandles) {
|
|
234
|
+
this.doBasicScale();
|
|
235
|
+
this.scale.autoScale(true);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Applies the basic scale viewport transformer to the visual points of the main candle series and fires a draw event.
|
|
240
|
+
*/
|
|
241
|
+
doBasicScale() {
|
|
242
|
+
this.basicScaleViewportTransformer(this.mainCandleSeries.visualPoints);
|
|
243
|
+
this.bus.fireDraw();
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Changes the time frame scale to the previous one and redraws the chart.
|
|
247
|
+
* @param {boolean | null} zoomIn - If true, zooms in the chart, if false, zooms out the chart, if null, does not zoom.
|
|
248
|
+
* @returns {void}
|
|
249
|
+
*/
|
|
250
|
+
doPreviousTimeFrameScale(zoomIn = null) {
|
|
251
|
+
this.timeFrameViewportTransformer(this.lastTimeFrame, zoomIn);
|
|
252
|
+
if (this.scale.state.autoScaleOnCandles) {
|
|
253
|
+
this.scale.doAutoScale(true);
|
|
254
|
+
}
|
|
255
|
+
this.bus.fireDraw();
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Saves the current timeframe by getting the start and end timestamps of the visual candles in the main candle series.
|
|
259
|
+
* If there are no visual candles, the last timeframe is not updated.
|
|
260
|
+
*/
|
|
261
|
+
rememberCurrentTimeframe() {
|
|
262
|
+
const visualCandles = this.mainCandleSeries.visualPoints;
|
|
263
|
+
if (visualCandles.length !== 0) {
|
|
264
|
+
this.lastTimeFrame = [
|
|
265
|
+
this.candleFromX(this.scale.toX(this.scale.xStart), true).timestamp,
|
|
266
|
+
this.candleFromX(this.scale.toX(this.scale.xEnd), true).timestamp,
|
|
267
|
+
];
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Shouldn't be called outside chart-core, use ChartComponent#updateAllSeries instead
|
|
272
|
+
* @param mainSeries
|
|
273
|
+
* @param secondarySeries
|
|
274
|
+
*/
|
|
275
|
+
updateAllSeries(mainSeries, secondarySeries = []) {
|
|
276
|
+
mainSeries = Object.assign({}, mainSeries);
|
|
277
|
+
if (!mainSeries.instrument) {
|
|
278
|
+
mainSeries.instrument = this.mainCandleSeries.instrument;
|
|
279
|
+
}
|
|
280
|
+
const allSeries = [mainSeries, ...secondarySeries];
|
|
281
|
+
if (!this.candleSeries.every(s => allSeries.find(ss => { var _a; return ((_a = ss.instrument) === null || _a === void 0 ? void 0 : _a.symbol) === s.instrument.symbol; })) ||
|
|
282
|
+
secondarySeries.length !== this.secondaryCandleSeries.length) {
|
|
283
|
+
console.error('All series update failed. Instruments for series are different.');
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const preparedCandles = sortCandles(mainSeries.candles.map(prepareCandle));
|
|
287
|
+
const updateResult = updateCandles(this.mainCandleSeries.dataPoints, preparedCandles);
|
|
288
|
+
const updatedCandles = updateResult.candles;
|
|
289
|
+
reindexCandles(updatedCandles);
|
|
290
|
+
this.mainCandleSeries.dataPoints = updatedCandles;
|
|
291
|
+
// re-create series
|
|
292
|
+
secondarySeries.map(series => {
|
|
293
|
+
var _a, _b, _c, _d;
|
|
294
|
+
const preparedCandles = sortCandles(series.candles.map(prepareCandle));
|
|
295
|
+
const updatedCandles = updateCandles((_d = (_c = this.findSecondarySeriesBySymbol((_b = (_a = series.instrument) === null || _a === void 0 ? void 0 : _a.symbol) !== null && _b !== void 0 ? _b : '')) === null || _c === void 0 ? void 0 : _c.dataPoints) !== null && _d !== void 0 ? _d : [], preparedCandles).candles;
|
|
296
|
+
return this.setSecondaryCandleSeries(updatedCandles, series.instrument, false);
|
|
297
|
+
});
|
|
298
|
+
// do visual recalculations
|
|
299
|
+
this.candleSeries.forEach(series => {
|
|
300
|
+
series.recalculateVisualPoints();
|
|
301
|
+
series.recalculateDataViewportIndexes();
|
|
302
|
+
});
|
|
303
|
+
// caclulate offset width for prepanded candles
|
|
304
|
+
const prependedCandlesWidth = this.chartBaseModel.mainVisualPoints
|
|
305
|
+
.slice(0, updateResult.prepended)
|
|
306
|
+
.reduce((acc, cur) => acc + cur.width, 0);
|
|
307
|
+
this.scale.moveXStart(this.scale.xStart + prependedCandlesWidth);
|
|
308
|
+
this.candlesPrependSubject.next({
|
|
309
|
+
prependedCandlesWidth,
|
|
310
|
+
preparedCandles,
|
|
311
|
+
});
|
|
312
|
+
this.chartBaseModel.recalculatePeriod();
|
|
313
|
+
this.candlesUpdatedSubject.next();
|
|
314
|
+
this.bus.fireDraw();
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Removes all data points from the main candle series that are newer than the given timestamp.
|
|
318
|
+
* Can be useful for data replay.
|
|
319
|
+
* @param startTimestamp
|
|
320
|
+
*/
|
|
321
|
+
removeDataFrom(startTimestamp) {
|
|
322
|
+
Object.values(this.paneManager.panes).forEach(pane => {
|
|
323
|
+
pane.dataSeries.forEach(series => {
|
|
324
|
+
const searchResult = binarySearch(series.dataPoints, startTimestamp, p => p.timestamp);
|
|
325
|
+
const index = searchResult.exact ? searchResult.index : searchResult.index + 1;
|
|
326
|
+
series.dataPoints = series.dataPoints.slice(0, index);
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
this.candlesRemovedSubject.next();
|
|
330
|
+
this.candlesUpdatedSubject.next();
|
|
331
|
+
this.canvasModel.fireDraw();
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Creates a secondary candle series model for a given instrument.
|
|
335
|
+
* @param {ChartInstrument} instrument - The instrument for which the secondary candle series model is created.
|
|
336
|
+
* @returns {CandleSeriesModel | undefined} - The created secondary candle series model or undefined if it cannot be created.
|
|
337
|
+
*/
|
|
338
|
+
createSecondaryCandleSeriesModel(instrument) {
|
|
339
|
+
const candleSeriesConfig = Object.assign(Object.assign({}, this.config.colors), this.secondaryChartColors.takeColorFromPool(instrument.symbol));
|
|
340
|
+
return this.createCandleSeriesModel(instrument, candleSeriesConfig);
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Creates a new CandleSeriesModel object and adds it to the chart.
|
|
344
|
+
* @param {ChartInstrument} instrument - The instrument to be displayed in the chart.
|
|
345
|
+
* @param {CandleSeriesColors} [colors] - Optional colors for the candle series.
|
|
346
|
+
* @returns {CandleSeriesModel} - The newly created CandleSeriesModel object.
|
|
347
|
+
*/
|
|
348
|
+
createCandleSeriesModel(instrument, colors) {
|
|
349
|
+
const candleSeries = new CandleSeriesModel(this.paneManager.panes[CHART_UUID].mainExtent, this.paneManager.hitTestController.getNewDataSeriesHitTestId(), this.bus, this.scale, instrument, this.candlesTransformersByChartType, this.candleWidthByChartType, colors);
|
|
350
|
+
candleSeries.config.type = 'line';
|
|
351
|
+
this.candleSeries.push(candleSeries);
|
|
352
|
+
return candleSeries;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Removes a secondary candle series from the chart and returns its colors.
|
|
356
|
+
* @param {CandleSeriesModel} series - The candle series to be removed.
|
|
357
|
+
* @returns {CandleSeriesColors | undefined} - The colors of the removed series or undefined if the series was not found.
|
|
358
|
+
*/
|
|
359
|
+
removeSecondaryCandleSeries(series) {
|
|
360
|
+
this.secondaryChartColors.addColorToPool(series.instrument.symbol);
|
|
361
|
+
this.candleSeries = this.candleSeries.filter(s => s !== series);
|
|
362
|
+
series.deactivate();
|
|
363
|
+
this.paneManager.panes[CHART_UUID].removeDataSeries(series);
|
|
364
|
+
this.scale.doAutoScale();
|
|
365
|
+
return series.colors;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Adjusts secondary series to present them nicely with main series:
|
|
369
|
+
* - fill gaps in secondarySeries with fake candles OHLC of same price (=close price)
|
|
370
|
+
* - do not add any candles which are not in main series
|
|
371
|
+
* @param mainSeries - main series
|
|
372
|
+
* @param secondarySeries - secondarySeries to adjust
|
|
373
|
+
*/
|
|
374
|
+
secondarySeriesAdjustments(mainSeries, secondarySeries) {
|
|
375
|
+
const result = [];
|
|
376
|
+
mainSeries.forEach(mainCandle => {
|
|
377
|
+
var _a;
|
|
378
|
+
const idx = (_a = mainCandle.idx) !== null && _a !== void 0 ? _a : 0;
|
|
379
|
+
const compareCandle = secondarySeries[idx];
|
|
380
|
+
if (!compareCandle) {
|
|
381
|
+
// take first candle to left or right
|
|
382
|
+
// check left direction first
|
|
383
|
+
let candle = findFirstNotEmptyCandle(secondarySeries, idx, -1);
|
|
384
|
+
if (!candle) {
|
|
385
|
+
candle = findFirstNotEmptyCandle(secondarySeries, idx, 1);
|
|
386
|
+
}
|
|
387
|
+
if (candle) {
|
|
388
|
+
// copy the candle and simplify it's OHLC
|
|
389
|
+
const fakeCandle = copyCandle(candle, idx, true);
|
|
390
|
+
result.push(fakeCandle);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
result.push(compareCandle);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
return result;
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Updates the configuration of the secondary candle series with the provided colors and instrument symbol.
|
|
401
|
+
* @param {PartialCandleSeriesColors} config - The partial configuration object containing the colors to update.
|
|
402
|
+
* @param {string} instrumentSymbol - The symbol of the instrument to update the colors for.
|
|
403
|
+
* @param {DataSeriesType} chartType - The type of chart to update the series to.
|
|
404
|
+
* @returns {void}
|
|
405
|
+
*/
|
|
406
|
+
updateSecondaryCandleSeriesConfig(config, instrumentSymbol, chartType) {
|
|
407
|
+
const candleSeriesConfig = mergeObj(config, getDefaultConfig().colors);
|
|
408
|
+
this.secondaryChartColors.updateColorConfig(instrumentSymbol, candleSeriesConfig);
|
|
409
|
+
const seriesToUpdate = this.findSecondarySeriesBySymbol(instrumentSymbol);
|
|
410
|
+
if (seriesToUpdate) {
|
|
411
|
+
seriesToUpdate.config.type = chartType;
|
|
412
|
+
seriesToUpdate.updateCandleSeriesColors(candleSeriesConfig);
|
|
413
|
+
this.bus.fireDraw([this.canvasModel.canvasId]);
|
|
414
|
+
}
|
|
415
|
+
this.bus.fireDraw([this.canvasModel.canvasId]);
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Sets the autoScale property of the scaleModel object.
|
|
419
|
+
* @param {boolean} auto - The value to be set for the autoScale property.
|
|
420
|
+
* @returns {void}
|
|
421
|
+
*/
|
|
422
|
+
setAutoScale(auto) {
|
|
423
|
+
this.scale.autoScale(auto);
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Returns the effective width of the Y axis.
|
|
427
|
+
*
|
|
428
|
+
* @function
|
|
429
|
+
* @name getEffectiveYAxisWidth
|
|
430
|
+
* @returns {number} The effective width of the Y axis.
|
|
431
|
+
*/
|
|
432
|
+
getEffectiveYAxisWidth() {
|
|
433
|
+
const yAxis = this.canvasBoundsContainer.getBounds(CanvasElement.PANE_UUID_Y_AXIS(CHART_UUID));
|
|
434
|
+
return yAxis.width;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Returns the effective width of the chart.
|
|
438
|
+
*
|
|
439
|
+
* @function
|
|
440
|
+
* @returns {number} The effective width of the chart.
|
|
441
|
+
*/
|
|
442
|
+
getEffectiveChartWidth() {
|
|
443
|
+
const chart = this.canvasBoundsContainer.getBounds(CanvasElement.PANE_UUID(CHART_UUID));
|
|
444
|
+
return chart.width;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Returns the effective height of the chart.
|
|
448
|
+
*
|
|
449
|
+
* @returns {number} The effective height of the chart.
|
|
450
|
+
*/
|
|
451
|
+
getEffectiveChartHeight() {
|
|
452
|
+
const chart = this.canvasBoundsContainer.getBounds(CanvasElement.PANE_UUID(CHART_UUID));
|
|
453
|
+
return chart.height;
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Updates the offsets of the chart components and redraws the chart.
|
|
457
|
+
* @param {Partial<ChartConfigComponentsOffsets>} offsets - The new offsets to be applied to the chart components.
|
|
458
|
+
* @returns {void}
|
|
459
|
+
*/
|
|
460
|
+
setOffsets(offsets) {
|
|
461
|
+
this.scale.updateOffsets(offsets);
|
|
462
|
+
const lastIdx = this.getCandlesCountWithRightOffset();
|
|
463
|
+
const visualCandles = this.mainCandleSeries.visualPoints;
|
|
464
|
+
if (this.hasCandles() && lastIdx >= visualCandles.length) {
|
|
465
|
+
this.scale.setXScale(this.scale.xStart, this.candleFromIdx(lastIdx).startUnit);
|
|
466
|
+
}
|
|
467
|
+
this.offsetsChanged.next();
|
|
468
|
+
this.bus.fireDraw();
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Returns the offsets of the chart components.
|
|
472
|
+
* @returns {ChartConfigComponentsOffsets} The offsets of the chart components.
|
|
473
|
+
*/
|
|
474
|
+
getOffsets() {
|
|
475
|
+
return this.scale.getOffsets();
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Converts "y" in pixels to: price / percent / other value.
|
|
479
|
+
* @param y - source value in pixels
|
|
480
|
+
*/
|
|
481
|
+
fromY(y) {
|
|
482
|
+
return this.scale.fromY(y);
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Converts "y" in pixels to price
|
|
486
|
+
* @param y - source value in pixels
|
|
487
|
+
* @param candleSeriesModel - (optional) candle series
|
|
488
|
+
*/
|
|
489
|
+
priceFromY(y, candleSeriesModel) {
|
|
490
|
+
const series = candleSeriesModel !== null && candleSeriesModel !== void 0 ? candleSeriesModel : this.mainCandleSeries;
|
|
491
|
+
return series.view.priceFromY(y);
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Converts "x" in pixels to timestamp
|
|
495
|
+
* @param x - source value in pixels
|
|
496
|
+
*/
|
|
497
|
+
fromX(x) {
|
|
498
|
+
return this.scale.fromX(x);
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Converts index to pixel.
|
|
502
|
+
* @param idx - index of candle
|
|
503
|
+
*/
|
|
504
|
+
toX(idx) {
|
|
505
|
+
const visualCandle = this.candleFromIdx(idx);
|
|
506
|
+
// lineX is the middle of candle - it's correct
|
|
507
|
+
return this.scale.toX(visualCandle.centerUnit);
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Transforms X coordinate (relative to canvas element) to Candle object.
|
|
511
|
+
* If extrapolate = false, then it takes leftmost/rightmost existing candle
|
|
512
|
+
*/
|
|
513
|
+
candleFromX(x, extrapolate = false, selectedCandleSeries = this.mainCandleSeries) {
|
|
514
|
+
const unit = this.scale.fromX(x);
|
|
515
|
+
return this.candleFromUnit(unit, extrapolate, selectedCandleSeries);
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Returns a candle object based on the provided unit, which is searched for in the visualPoints array of the selectedCandleSeries.
|
|
519
|
+
* If extrapolate is true and the unit is outside the visualPoints array, a fake candle is returned.
|
|
520
|
+
* If extrapolate is false and the unit is outside the visualPoints array, null is returned.
|
|
521
|
+
* If the unit is within the visualPoints array, the corresponding candle object is returned.
|
|
522
|
+
*
|
|
523
|
+
* @param {Unit} unit - The unit to search for in the visualPoints array.
|
|
524
|
+
* @param {boolean} extrapolate - Whether to extrapolate a fake candle if the unit is outside the visualPoints array.
|
|
525
|
+
* @param {CandleSeriesModel} selectedCandleSeries - The candle series to search in. Defaults to the mainCandleSeries.
|
|
526
|
+
* @returns {Candle} - The corresponding candle object, or null if extrapolate is false and the unit is outside the visualPoints array.
|
|
527
|
+
*/
|
|
528
|
+
candleFromUnit(unit, extrapolate = false, selectedCandleSeries = this.mainCandleSeries) {
|
|
529
|
+
var _a, _b;
|
|
530
|
+
const result = binarySearch(selectedCandleSeries.visualPoints, unit, i => i.startUnit);
|
|
531
|
+
const visualCandleSource = selectedCandleSeries.visualPoints;
|
|
532
|
+
const lastVisualCandle = this.getLastVisualCandle();
|
|
533
|
+
if (visualCandleSource[result.index] !== undefined &&
|
|
534
|
+
(unit < 0 || (lastVisualCandle && unit > lastVisualCandle.startUnit + lastVisualCandle.width))) {
|
|
535
|
+
result.index += round((unit - visualCandleSource[result.index].centerUnit) / this.mainCandleSeries.meanCandleWidth);
|
|
536
|
+
}
|
|
537
|
+
const candleIdx = result.index;
|
|
538
|
+
const safeIdx = Math.max(Math.min(visualCandleSource.length - 1, candleIdx), 0);
|
|
539
|
+
if ((candleIdx < 0 || candleIdx >= visualCandleSource.length) && extrapolate) {
|
|
540
|
+
// fake candle
|
|
541
|
+
return fakeCandle(this.mainCandleSeries.dataPoints, candleIdx, this.chartBaseModel.period);
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
// real candle
|
|
545
|
+
return ((_b = (_a = visualCandleSource[safeIdx]) === null || _a === void 0 ? void 0 : _a.candle) !== null && _b !== void 0 ? _b : fakeCandle(this.mainCandleSeries.dataPoints, safeIdx, this.chartBaseModel.period));
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* For given timestamp finds the closest candle in dataset.
|
|
550
|
+
* @param timestamp
|
|
551
|
+
*/
|
|
552
|
+
candleFromTimestamp(timestamp, shouldExtrapolate = true) {
|
|
553
|
+
return this.chartBaseModel.dataFromTimestamp(timestamp, shouldExtrapolate);
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* For given index returns the closest visual candle, or fake candle with correct X coordinate.
|
|
557
|
+
* @param idx - index of candle
|
|
558
|
+
*/
|
|
559
|
+
candleFromIdx(idx) {
|
|
560
|
+
return this.chartBaseModel.dataFromIdx(idx);
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Check whether model is ready for drawing.
|
|
564
|
+
*/
|
|
565
|
+
isReady() {
|
|
566
|
+
// Do not check canvas size: we should check width only when resize event has occurred
|
|
567
|
+
return this.canvasModel.isReady() && this.hasCandles();
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* You can use this method to determine logic of visual candle transformation for specified chart type.
|
|
571
|
+
* @param chartType
|
|
572
|
+
* @param calculator
|
|
573
|
+
*/
|
|
574
|
+
registerCandlesTransformer(chartType, calculator) {
|
|
575
|
+
this.candlesTransformersByChartType[chartType] = calculator;
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* You can use this method to determine chart width calculation for specified chart type.
|
|
579
|
+
* @param chartType
|
|
580
|
+
* @param calculator
|
|
581
|
+
*/
|
|
582
|
+
registerCandlesWidthCalculator(chartType, calculator) {
|
|
583
|
+
this.candleWidthByChartType[chartType] = calculator;
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* You can use this method to modify labels for last candle.
|
|
587
|
+
* @param chartType
|
|
588
|
+
* @param handler
|
|
589
|
+
*/
|
|
590
|
+
registerLastCandleLabelHandler(chartType, handler) {
|
|
591
|
+
this.lastCandleLabelsByChartType[chartType] = handler;
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Checks if the main candle series has visual points.
|
|
595
|
+
* @returns {boolean} Returns true if the main candle series has visual points, otherwise false.
|
|
596
|
+
*/
|
|
597
|
+
hasCandles() {
|
|
598
|
+
return this.mainCandleSeries.visualPoints.length !== 0;
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Returns a visual candle object from the main candle series based on the provided index.
|
|
602
|
+
* @param {Index} idx - The index of the visual candle to retrieve.
|
|
603
|
+
* @returns {VisualCandle|undefined} - The visual candle object or undefined if it doesn't exist.
|
|
604
|
+
*/
|
|
605
|
+
getVisualCandle(idx) {
|
|
606
|
+
var _a, _b, _c;
|
|
607
|
+
const indexToGrab = idx - ((_c = (_b = (_a = this.mainCandleSeries.visualPoints[0]) === null || _a === void 0 ? void 0 : _a.candle) === null || _b === void 0 ? void 0 : _b.idx) !== null && _c !== void 0 ? _c : 0);
|
|
608
|
+
return this.mainCandleSeries.visualPoints[indexToGrab];
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Returns the index of the first data point in the main candle series.
|
|
612
|
+
*
|
|
613
|
+
* @returns {number} The index of the first data point in the main candle series.
|
|
614
|
+
*/
|
|
615
|
+
getFirstIdx() {
|
|
616
|
+
return this.mainCandleSeries.dataIdxStart;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Returns the last index of the data in the mainCandleSeries.
|
|
620
|
+
*
|
|
621
|
+
* @returns {number} - The last index of the data in the mainCandleSeries.
|
|
622
|
+
*/
|
|
623
|
+
getLastIdx() {
|
|
624
|
+
return this.mainCandleSeries.dataIdxEnd;
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Returns the timestamp of the first candle in the viewport dataset
|
|
628
|
+
* @param dataBased - if false will return timestamp which matches first viewport point even if candle is not available
|
|
629
|
+
* @returns {Timestamp} The timestamp of the first candle
|
|
630
|
+
*/
|
|
631
|
+
getFirstTimestamp(dataBased = true) {
|
|
632
|
+
if (dataBased) {
|
|
633
|
+
return this.candleFromIdx(this.getFirstIdx()).candle.timestamp;
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
return this.candleFromUnit(this.scale.xStart, true).timestamp;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Returns the timestamp of the last candle in the viewport candle array
|
|
641
|
+
* @param dataBased - if false will return timestamp which matches first viewport point even if candle is not available
|
|
642
|
+
* @returns {Timestamp} The timestamp of the last candle
|
|
643
|
+
*/
|
|
644
|
+
getLastTimestamp(dataBased = true) {
|
|
645
|
+
if (dataBased) {
|
|
646
|
+
return this.candleFromIdx(this.getLastIdx()).candle.timestamp;
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
return this.candleFromUnit(this.scale.xEnd, true).timestamp;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Sets the timestamp range of the chart by setting the x-axis scale.
|
|
654
|
+
* @param {Timestamp} start - The start timestamp of the range.
|
|
655
|
+
* @param {Timestamp} end - The end timestamp of the range.
|
|
656
|
+
* @returns {void}
|
|
657
|
+
*/
|
|
658
|
+
setTimestampRange(start, end) {
|
|
659
|
+
const startUnit = this.candleFromTimestamp(start).startUnit;
|
|
660
|
+
const endCandle = this.candleFromTimestamp(end);
|
|
661
|
+
const endUnit = endCandle.startUnit + endCandle.width;
|
|
662
|
+
return this.scale.setXScale(startUnit, endUnit);
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Returns the last candle of the main candle series.
|
|
666
|
+
* @returns {Candle | undefined} The last candle of the main candle series or undefined if the series is empty.
|
|
667
|
+
*/
|
|
668
|
+
getLastCandle() {
|
|
669
|
+
const candles = this.mainCandleSeries.dataPoints;
|
|
670
|
+
return lastOf(candles);
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Returns the last visual candle of the main candle series.
|
|
674
|
+
* @returns {VisualCandle | undefined} The last visual candle of the main candle series or undefined if the series is empty.
|
|
675
|
+
|
|
676
|
+
*/
|
|
677
|
+
getLastVisualCandle() {
|
|
678
|
+
const candles = this.mainCandleSeries.visualPoints;
|
|
679
|
+
return lastOf(candles);
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Calculates the maximum number of candles that can fit in the chart width based on the minimum candle width
|
|
683
|
+
* @returns {number} - The maximum number of candles that can fit in the chart width
|
|
684
|
+
*/
|
|
685
|
+
getMaxCandlesFitLength() {
|
|
686
|
+
return floor(this.getEffectiveChartWidth() / this.config.components.chart.minWidth);
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Returns an array of CandleSeriesModel objects representing the secondary candle series.
|
|
690
|
+
* @returns {Array<CandleSeriesModel>} An array of CandleSeriesModel objects representing the secondary candle series.
|
|
691
|
+
*/
|
|
692
|
+
getSecondarySeries() {
|
|
693
|
+
return this.secondaryCandleSeries;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Checks if a given symbol matches the symbol of a given CandleSeriesModel's instrument.
|
|
697
|
+
* @param {CandleSeriesModel} series - The CandleSeriesModel to check.
|
|
698
|
+
* @param {ChartInstrument['symbol']} symbol - The symbol to compare with the instrument's symbol.
|
|
699
|
+
* @returns {boolean} - Returns true if the symbol matches the instrument's symbol, false otherwise.
|
|
700
|
+
*/
|
|
701
|
+
isSeriesInstrument(series, symbol) {
|
|
702
|
+
return series.instrument.symbol === symbol;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Returns an array of CandleSeriesModel objects that match the provided symbol.
|
|
706
|
+
*
|
|
707
|
+
* @param {string} symbol - The symbol to search for.
|
|
708
|
+
* @returns {CandleSeriesModel[]} - An array of CandleSeriesModel objects that match the provided symbol.
|
|
709
|
+
*/
|
|
710
|
+
findSeriesBySymbol(symbol) {
|
|
711
|
+
return this.candleSeries.filter(series => this.isSeriesInstrument(series, symbol));
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Finds a secondary candle series by symbol
|
|
715
|
+
*
|
|
716
|
+
* @param {string} symbol - The symbol of the chart instrument
|
|
717
|
+
* @returns {CandleSeriesModel | undefined} - The secondary candle series or undefined if not found
|
|
718
|
+
*/
|
|
719
|
+
findSecondarySeriesBySymbol(symbol) {
|
|
720
|
+
return this.secondaryCandleSeries.find(series => this.isSeriesInstrument(series, symbol));
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Reindexes candles based on a given series.
|
|
724
|
+
* @private
|
|
725
|
+
* @param {Array<Candle>} baseSeries - The base series to search for candles.
|
|
726
|
+
* @param {Array<Candle>} series - The series to reindex.
|
|
727
|
+
* @returns {Array<Candle>} - The reindexed series.
|
|
728
|
+
*/
|
|
729
|
+
reindexCandlesBasedOnSeries(baseSeries, series) {
|
|
730
|
+
return series.reduce((candles, candle) => {
|
|
731
|
+
const timestamp = candle.timestamp;
|
|
732
|
+
// find index of candle in baseSeries
|
|
733
|
+
const result = searchCandleIndex(timestamp, false, baseSeries, this.chartBaseModel.period);
|
|
734
|
+
if (result.index >= 0 && result.index < baseSeries.length) {
|
|
735
|
+
candle.idx = result.index;
|
|
736
|
+
candles[result.index] = candle;
|
|
737
|
+
}
|
|
738
|
+
return candles;
|
|
739
|
+
}, []);
|
|
740
|
+
}
|
|
741
|
+
getPeriod() {
|
|
742
|
+
return this.chartBaseModel.period;
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Checks if a given candle is within the viewport.
|
|
746
|
+
* @param {number} idx - The index of the candle to check.
|
|
747
|
+
* @returns {boolean} - True if the candle is within the viewport, false otherwise.
|
|
748
|
+
*/
|
|
749
|
+
isCandleInViewport(idx) {
|
|
750
|
+
return this.getFirstIdx() <= idx && idx <= this.getLastIdx();
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Updates candles in series. Default is main series.
|
|
754
|
+
* Any number of candles may be provided - they would be matched by their index and updated.
|
|
755
|
+
* @param candles - any list of new (updated) candles
|
|
756
|
+
* @param instrument - name of instrument to update
|
|
757
|
+
*/
|
|
758
|
+
// I'd like to keep this method, for me one generic method is more convenient than 3 (updateLastCandle, addLastCandle, prepend...)
|
|
759
|
+
updateCandles(candles, instrumentSymbol = this.mainCandleSeries.instrument.symbol) {
|
|
760
|
+
const isMainSymbol = this.mainCandleSeries.instrument.symbol === instrumentSymbol;
|
|
761
|
+
const seriesList = this.findSeriesBySymbol(instrumentSymbol);
|
|
762
|
+
if (seriesList.length === 0) {
|
|
763
|
+
console.warn("updateCandles failed. Can't find series", instrumentSymbol);
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
seriesList.forEach(series => {
|
|
767
|
+
const curCandles = series.dataPoints;
|
|
768
|
+
let isNewCandle = false;
|
|
769
|
+
let isInView = false;
|
|
770
|
+
let lastCandle;
|
|
771
|
+
candles.forEach(candle => {
|
|
772
|
+
var _a;
|
|
773
|
+
if (!candle) {
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
// detect index of updating candle
|
|
777
|
+
const result = searchCandleIndex(candle.timestamp, true, curCandles, this.getPeriod());
|
|
778
|
+
const idx = Math.min(result.index, curCandles.length);
|
|
779
|
+
isNewCandle = isNewCandle || idx === curCandles.length;
|
|
780
|
+
// update the candle and index
|
|
781
|
+
curCandles[idx] = candle;
|
|
782
|
+
candle.idx = idx;
|
|
783
|
+
// set serie with new candle
|
|
784
|
+
series.dataPoints = curCandles;
|
|
785
|
+
series.recalculateDataViewportIndexes();
|
|
786
|
+
isInView = isInView || this.isCandleInViewport(idx);
|
|
787
|
+
const isOutRangeCurrentCandles = idx >= curCandles.length - 1;
|
|
788
|
+
const isBeforeCurrentLastCandle = lastCandle && candle.timestamp < lastCandle.timestamp;
|
|
789
|
+
if (isOutRangeCurrentCandles && !isBeforeCurrentLastCandle) {
|
|
790
|
+
lastCandle = candle;
|
|
791
|
+
}
|
|
792
|
+
// we can move chart only when update is for main symbol
|
|
793
|
+
// TODO we can have two series with the same instruments - this logic doesn't cover this case
|
|
794
|
+
if (isNewCandle && lastCandle && isMainSymbol && isInView) {
|
|
795
|
+
const widthCalculator = (_a = this.candleWidthByChartType[this.config.components.chart.type]) !== null && _a !== void 0 ? _a : calculateCandleWidth;
|
|
796
|
+
this.scale.moveXStart(this.scale.xStart + widthCalculator(lastCandle));
|
|
797
|
+
}
|
|
798
|
+
});
|
|
799
|
+
if (lastCandle) {
|
|
800
|
+
series.updateCurrentPrice(lastCandle.close);
|
|
801
|
+
}
|
|
802
|
+
if (isInView && lastCandle && candles.length === 1) {
|
|
803
|
+
// can apply some optimization
|
|
804
|
+
// series.recalculateOnlyLastVisualCandle();
|
|
805
|
+
// TODO apply optimization
|
|
806
|
+
this.bus.fireDraw([this.canvasModel.canvasId]);
|
|
807
|
+
}
|
|
808
|
+
else {
|
|
809
|
+
series.recalculateVisualPoints();
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
this.scale.doAutoScale();
|
|
813
|
+
this.candlesUpdatedSubject.next();
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Triggers when candles set or updated
|
|
817
|
+
*/
|
|
818
|
+
observeCandlesChanged() {
|
|
819
|
+
return merge(this.candlesSetSubject, this.candlesUpdatedSubject);
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Returns an array of Candle objects representing the data points of the main candle series.
|
|
823
|
+
* @returns {Array<Candle>} An array of Candle objects.
|
|
824
|
+
*/
|
|
825
|
+
getCandles() {
|
|
826
|
+
return this.mainCandleSeries.dataPoints;
|
|
827
|
+
}
|
|
828
|
+
/***
|
|
829
|
+
* Returns candles array which consists of real candles and fake candles if from and to can't be satisfied with real ones.
|
|
830
|
+
* @param from - if not specified set to first candle timestamp
|
|
831
|
+
* @param to - if not specified set to last candle timestamp + right offset
|
|
832
|
+
*/
|
|
833
|
+
getCandlesWithFake(from = 0, to) {
|
|
834
|
+
let candles = this.getCandles().slice();
|
|
835
|
+
const candlesCount = this.getCandlesCount();
|
|
836
|
+
const _to = to !== null && to !== void 0 ? to : candlesCount + this.FAKE_CANDLES_DEFAULT;
|
|
837
|
+
candles = candles.slice(Math.max(0, from), Math.min(candlesCount, _to));
|
|
838
|
+
// add fake candles if needed
|
|
839
|
+
const append = [];
|
|
840
|
+
const prepend = [];
|
|
841
|
+
for (let i = candlesCount; i < _to; i++) {
|
|
842
|
+
append.push(fakeCandle(this.mainCandleSeries.dataPoints, i, this.getPeriod()));
|
|
843
|
+
}
|
|
844
|
+
const toPrepend = Math.min(0, _to);
|
|
845
|
+
for (let i = from; i < toPrepend; i++) {
|
|
846
|
+
prepend.push(fakeCandle(this.mainCandleSeries.dataPoints, i, this.getPeriod()));
|
|
847
|
+
}
|
|
848
|
+
return [...prepend, ...candles, ...append];
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Returns the number of candles in the main candle series.
|
|
852
|
+
*
|
|
853
|
+
* @returns {number} The number of candles in the main candle series.
|
|
854
|
+
*/
|
|
855
|
+
getCandlesCount() {
|
|
856
|
+
return this.mainCandleSeries.dataPoints.length;
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* Returns the number of candles with the right offset.
|
|
860
|
+
* @returns {number} - The number of candles with the right offset.
|
|
861
|
+
*/
|
|
862
|
+
getCandlesCountWithRightOffset() {
|
|
863
|
+
return this.getCandlesCount() + this.getOffsets().right;
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* Clears the data of all candle series in the chart.
|
|
867
|
+
*/
|
|
868
|
+
clearData() {
|
|
869
|
+
this.candleSeries.forEach(candleSeries => candleSeries.clearData());
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* Adds new candles array to the existing one at the start, mostly used in lazy loading
|
|
873
|
+
* @param target - initial candles array
|
|
874
|
+
* @param prependUpdate - additional candles array, which will be added to the target array
|
|
875
|
+
*/
|
|
876
|
+
prependCandles(target, prependUpdate) {
|
|
877
|
+
const targetCopy = target.slice();
|
|
878
|
+
const prepend = [];
|
|
879
|
+
prependUpdate.forEach(c => {
|
|
880
|
+
const result = searchCandleIndex(c.timestamp, false, target);
|
|
881
|
+
const idx = result.index;
|
|
882
|
+
if (idx < 0) {
|
|
883
|
+
prepend.push(c);
|
|
884
|
+
}
|
|
885
|
+
else if (target[idx].timestamp === c.timestamp) {
|
|
886
|
+
targetCopy[idx] = c;
|
|
887
|
+
}
|
|
888
|
+
else {
|
|
889
|
+
console.warn(`Couldn't update candle with timestamp ${c.timestamp}`);
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
return {
|
|
893
|
+
prepended: prepend.length,
|
|
894
|
+
candles: [...prepend, ...targetCopy],
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Adds new candle to the chart
|
|
899
|
+
* @param candle - new candle
|
|
900
|
+
* @param instrument - name of the instrument to update
|
|
901
|
+
*/
|
|
902
|
+
addLastCandle(candle, instrumentSymbol = this.mainCandleSeries.instrument.symbol) {
|
|
903
|
+
this.updateCandles([candle], instrumentSymbol);
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Updates last candle value
|
|
907
|
+
* @param candle - updated candle
|
|
908
|
+
* @param instrument - name of the instrument to update
|
|
909
|
+
*/
|
|
910
|
+
updateLastCandle(candle, instrumentSymbol = this.mainCandleSeries.instrument.symbol) {
|
|
911
|
+
this.updateCandles([candle], instrumentSymbol);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
const sortCandles = (candles) => candles.slice().sort((a, b) => (a.timestamp === b.timestamp ? 0 : a.timestamp > b.timestamp ? 1 : -1));
|
|
915
|
+
const findFirstNotEmptyCandle = (candles, startIdx, iterateStep) => {
|
|
916
|
+
if (startIdx >= candles.length) {
|
|
917
|
+
return candles[candles.length - 1];
|
|
918
|
+
}
|
|
919
|
+
for (let i = startIdx; i < candles.length && i >= 0; i += iterateStep) {
|
|
920
|
+
const candle = candles[i];
|
|
921
|
+
if (candle) {
|
|
922
|
+
return candle;
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
/**
|
|
927
|
+
* Updates target array using update array.
|
|
928
|
+
* If there is a candle with the same timestamp as in target array then it will be replaced
|
|
929
|
+
* If the candle timestamp from update is somewhere between target candles then it will be ignored
|
|
930
|
+
* If the update candle timestamp is beyond target, then it will be prepended/appended to target
|
|
931
|
+
* @param target {Candle[]} - sorted candles
|
|
932
|
+
* @param update {Candle[]} - sorted candles
|
|
933
|
+
*/
|
|
934
|
+
const updateCandles = (target, update) => {
|
|
935
|
+
const targetCopy = target.slice();
|
|
936
|
+
const prepend = [];
|
|
937
|
+
const append = [];
|
|
938
|
+
update.forEach(c => {
|
|
939
|
+
const result = searchCandleIndex(c.timestamp, true, target);
|
|
940
|
+
const idx = result.index;
|
|
941
|
+
if (idx < 0) {
|
|
942
|
+
prepend.push(c);
|
|
943
|
+
}
|
|
944
|
+
else if (idx >= target.length) {
|
|
945
|
+
append.push(c);
|
|
946
|
+
}
|
|
947
|
+
else if (target[idx].timestamp === c.timestamp) {
|
|
948
|
+
targetCopy[idx] = c;
|
|
949
|
+
}
|
|
950
|
+
else {
|
|
951
|
+
console.warn(`Couldn't update candle with timestamp ${c.timestamp}`);
|
|
952
|
+
}
|
|
953
|
+
});
|
|
954
|
+
return {
|
|
955
|
+
prepended: prepend.length,
|
|
956
|
+
appended: append.length,
|
|
957
|
+
candles: [...prepend, ...targetCopy, ...append],
|
|
958
|
+
};
|
|
959
|
+
};
|