@forgecharts/sdk 1.1.28 → 1.1.30
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/index.js +8100 -48
- package/dist/index.js.map +1 -1
- package/dist/internal.js +8851 -31
- package/dist/internal.js.map +1 -1
- package/dist/react/index.js +11558 -27
- package/dist/react/index.js.map +1 -1
- package/dist/react/internal.js +12147 -43
- package/dist/react/internal.js.map +1 -1
- package/package.json +1 -1
- package/dist/__tests__/backwardCompatibility.test.js +0 -159
- package/dist/__tests__/backwardCompatibility.test.js.map +0 -1
- package/dist/__tests__/candleInvariant.test.js +0 -415
- package/dist/__tests__/candleInvariant.test.js.map +0 -1
- package/dist/__tests__/public-api-surface.js +0 -38
- package/dist/__tests__/public-api-surface.js.map +0 -1
- package/dist/__tests__/timeframeBoundary.test.js +0 -452
- package/dist/__tests__/timeframeBoundary.test.js.map +0 -1
- package/dist/api/DrawingManager.js +0 -190
- package/dist/api/DrawingManager.js.map +0 -1
- package/dist/api/EventBus.js +0 -44
- package/dist/api/EventBus.js.map +0 -1
- package/dist/api/IndicatorDAG.js +0 -316
- package/dist/api/IndicatorDAG.js.map +0 -1
- package/dist/api/IndicatorRegistry.js +0 -39
- package/dist/api/IndicatorRegistry.js.map +0 -1
- package/dist/api/LayoutManager.js +0 -51
- package/dist/api/LayoutManager.js.map +0 -1
- package/dist/api/PaneManager.js +0 -119
- package/dist/api/PaneManager.js.map +0 -1
- package/dist/api/ReferenceAPI.js +0 -153
- package/dist/api/ReferenceAPI.js.map +0 -1
- package/dist/api/TChart.js +0 -765
- package/dist/api/TChart.js.map +0 -1
- package/dist/api/createChart.js +0 -42
- package/dist/api/createChart.js.map +0 -1
- package/dist/api/drawing tools/fib gann menu/fibRetracement.js +0 -22
- package/dist/api/drawing tools/fib gann menu/fibRetracement.js.map +0 -1
- package/dist/api/drawing tools/lines menu/crossLine.js +0 -16
- package/dist/api/drawing tools/lines menu/crossLine.js.map +0 -1
- package/dist/api/drawing tools/lines menu/disjointChannel.js +0 -59
- package/dist/api/drawing tools/lines menu/disjointChannel.js.map +0 -1
- package/dist/api/drawing tools/lines menu/extendedLine.js +0 -17
- package/dist/api/drawing tools/lines menu/extendedLine.js.map +0 -1
- package/dist/api/drawing tools/lines menu/flatTopBottom.js +0 -41
- package/dist/api/drawing tools/lines menu/flatTopBottom.js.map +0 -1
- package/dist/api/drawing tools/lines menu/horizontal.js +0 -19
- package/dist/api/drawing tools/lines menu/horizontal.js.map +0 -1
- package/dist/api/drawing tools/lines menu/horizontalRay.js +0 -20
- package/dist/api/drawing tools/lines menu/horizontalRay.js.map +0 -1
- package/dist/api/drawing tools/lines menu/infoLine.js +0 -107
- package/dist/api/drawing tools/lines menu/infoLine.js.map +0 -1
- package/dist/api/drawing tools/lines menu/insidePitchfork.js +0 -31
- package/dist/api/drawing tools/lines menu/insidePitchfork.js.map +0 -1
- package/dist/api/drawing tools/lines menu/modifiedSchiffPitchfork.js +0 -15
- package/dist/api/drawing tools/lines menu/modifiedSchiffPitchfork.js.map +0 -1
- package/dist/api/drawing tools/lines menu/parallelChannel.js +0 -43
- package/dist/api/drawing tools/lines menu/parallelChannel.js.map +0 -1
- package/dist/api/drawing tools/lines menu/pitchfork.js +0 -12
- package/dist/api/drawing tools/lines menu/pitchfork.js.map +0 -1
- package/dist/api/drawing tools/lines menu/ray.js +0 -23
- package/dist/api/drawing tools/lines menu/ray.js.map +0 -1
- package/dist/api/drawing tools/lines menu/regressionTrend.js +0 -127
- package/dist/api/drawing tools/lines menu/regressionTrend.js.map +0 -1
- package/dist/api/drawing tools/lines menu/schiffPitchfork.js +0 -15
- package/dist/api/drawing tools/lines menu/schiffPitchfork.js.map +0 -1
- package/dist/api/drawing tools/lines menu/trendAngle.js +0 -51
- package/dist/api/drawing tools/lines menu/trendAngle.js.map +0 -1
- package/dist/api/drawing tools/lines menu/trendline.js +0 -11
- package/dist/api/drawing tools/lines menu/trendline.js.map +0 -1
- package/dist/api/drawing tools/lines menu/vertical.js +0 -11
- package/dist/api/drawing tools/lines menu/vertical.js.map +0 -1
- package/dist/api/drawing tools/pointers menu/crosshair.js +0 -16
- package/dist/api/drawing tools/pointers menu/crosshair.js.map +0 -1
- package/dist/api/drawing tools/pointers menu/cursor.js +0 -15
- package/dist/api/drawing tools/pointers menu/cursor.js.map +0 -1
- package/dist/api/drawing tools/pointers menu/demonstration.js +0 -30
- package/dist/api/drawing tools/pointers menu/demonstration.js.map +0 -1
- package/dist/api/drawing tools/pointers menu/dot.js +0 -23
- package/dist/api/drawing tools/pointers menu/dot.js.map +0 -1
- package/dist/api/drawing tools/shapes menu/rectangle.js +0 -19
- package/dist/api/drawing tools/shapes menu/rectangle.js.map +0 -1
- package/dist/api/drawing tools/shapes menu/text.js +0 -25
- package/dist/api/drawing tools/shapes menu/text.js.map +0 -1
- package/dist/api/drawingUtils.js +0 -83
- package/dist/api/drawingUtils.js.map +0 -1
- package/dist/core/CanvasLayer.js +0 -56
- package/dist/core/CanvasLayer.js.map +0 -1
- package/dist/core/Chart.js +0 -839
- package/dist/core/Chart.js.map +0 -1
- package/dist/core/CoordTransform.js +0 -224
- package/dist/core/CoordTransform.js.map +0 -1
- package/dist/core/Crosshair.js +0 -186
- package/dist/core/Crosshair.js.map +0 -1
- package/dist/core/IndicatorEngine.js +0 -181
- package/dist/core/IndicatorEngine.js.map +0 -1
- package/dist/core/InteractionManager.js +0 -698
- package/dist/core/InteractionManager.js.map +0 -1
- package/dist/core/PriceScale.js +0 -113
- package/dist/core/PriceScale.js.map +0 -1
- package/dist/core/Series.js +0 -114
- package/dist/core/Series.js.map +0 -1
- package/dist/core/TimeScale.js +0 -150
- package/dist/core/TimeScale.js.map +0 -1
- package/dist/datafeed/DatafeedConnector.js +0 -268
- package/dist/datafeed/DatafeedConnector.js.map +0 -1
- package/dist/engine/CandleEngine.js +0 -318
- package/dist/engine/CandleEngine.js.map +0 -1
- package/dist/engine/__tests__/CandleEngine.test.js +0 -300
- package/dist/engine/__tests__/CandleEngine.test.js.map +0 -1
- package/dist/engine/candleInvariants.js +0 -134
- package/dist/engine/candleInvariants.js.map +0 -1
- package/dist/engine/mergeUtils.js +0 -64
- package/dist/engine/mergeUtils.js.map +0 -1
- package/dist/engine/timeframeUtils.js +0 -100
- package/dist/engine/timeframeUtils.js.map +0 -1
- package/dist/licensing/ChartRuntimeResolver.js +0 -310
- package/dist/licensing/ChartRuntimeResolver.js.map +0 -1
- package/dist/licensing/LicenseManager.js +0 -114
- package/dist/licensing/LicenseManager.js.map +0 -1
- package/dist/licensing/__tests__/ChartRuntimeResolver.test.js +0 -177
- package/dist/licensing/__tests__/ChartRuntimeResolver.test.js.map +0 -1
- package/dist/licensing/__tests__/LicenseManager.test.js +0 -153
- package/dist/licensing/__tests__/LicenseManager.test.js.map +0 -1
- package/dist/licensing/licenseTypes.js +0 -2
- package/dist/licensing/licenseTypes.js.map +0 -1
- package/dist/pine/PineCompiler.js +0 -44
- package/dist/pine/PineCompiler.js.map +0 -1
- package/dist/pine/diagnostics.js +0 -11
- package/dist/pine/diagnostics.js.map +0 -1
- package/dist/pine/index.js +0 -5
- package/dist/pine/index.js.map +0 -1
- package/dist/pine/pine-ast.js +0 -19
- package/dist/pine/pine-ast.js.map +0 -1
- package/dist/pine/pine-lexer.js +0 -249
- package/dist/pine/pine-lexer.js.map +0 -1
- package/dist/pine/pine-parser.js +0 -416
- package/dist/pine/pine-parser.js.map +0 -1
- package/dist/pine/pine-transpiler.js +0 -260
- package/dist/pine/pine-transpiler.js.map +0 -1
- package/dist/pixi/LayerName.js +0 -35
- package/dist/pixi/LayerName.js.map +0 -1
- package/dist/pixi/PixiCandlestickRenderer.js +0 -107
- package/dist/pixi/PixiCandlestickRenderer.js.map +0 -1
- package/dist/pixi/PixiChart.js +0 -367
- package/dist/pixi/PixiChart.js.map +0 -1
- package/dist/pixi/PixiCrosshairRenderer.js +0 -110
- package/dist/pixi/PixiCrosshairRenderer.js.map +0 -1
- package/dist/pixi/PixiDrawingRenderer.js +0 -111
- package/dist/pixi/PixiDrawingRenderer.js.map +0 -1
- package/dist/pixi/PixiGridRenderer.js +0 -114
- package/dist/pixi/PixiGridRenderer.js.map +0 -1
- package/dist/pixi/PixiLayerManager.js +0 -92
- package/dist/pixi/PixiLayerManager.js.map +0 -1
- package/dist/react/canvas/ChartCanvas.js +0 -604
- package/dist/react/canvas/ChartCanvas.js.map +0 -1
- package/dist/react/canvas/ChartContextMenu.js +0 -5
- package/dist/react/canvas/ChartContextMenu.js.map +0 -1
- package/dist/react/canvas/ChartSettingsDialog.js +0 -28
- package/dist/react/canvas/ChartSettingsDialog.js.map +0 -1
- package/dist/react/canvas/IndicatorLabel.js +0 -196
- package/dist/react/canvas/IndicatorLabel.js.map +0 -1
- package/dist/react/canvas/IndicatorPane.js +0 -395
- package/dist/react/canvas/IndicatorPane.js.map +0 -1
- package/dist/react/canvas/PointerOverlay.js +0 -61
- package/dist/react/canvas/PointerOverlay.js.map +0 -1
- package/dist/react/canvas/toolbars/LeftToolbar.js +0 -407
- package/dist/react/canvas/toolbars/LeftToolbar.js.map +0 -1
- package/dist/react/hooks/useChartCapabilities.js +0 -66
- package/dist/react/hooks/useChartCapabilities.js.map +0 -1
- package/dist/react/shell/ManagedAppShell.js +0 -440
- package/dist/react/shell/ManagedAppShell.js.map +0 -1
- package/dist/react/trading/TradingBridge.js +0 -73
- package/dist/react/trading/TradingBridge.js.map +0 -1
- package/dist/react/workspace/ChartWorkspace.js +0 -42
- package/dist/react/workspace/ChartWorkspace.js.map +0 -1
- package/dist/react/workspace/FloatingPanel.js +0 -82
- package/dist/react/workspace/FloatingPanel.js.map +0 -1
- package/dist/react/workspace/IndicatorsDialog.js +0 -121
- package/dist/react/workspace/IndicatorsDialog.js.map +0 -1
- package/dist/react/workspace/LayoutMenu.js +0 -113
- package/dist/react/workspace/LayoutMenu.js.map +0 -1
- package/dist/react/workspace/SymbolSearchDialog.js +0 -245
- package/dist/react/workspace/SymbolSearchDialog.js.map +0 -1
- package/dist/react/workspace/TabBar.js +0 -29
- package/dist/react/workspace/TabBar.js.map +0 -1
- package/dist/react/workspace/toolbars/BottomToolbar.js +0 -236
- package/dist/react/workspace/toolbars/BottomToolbar.js.map +0 -1
- package/dist/react/workspace/toolbars/RightToolbar.js +0 -18
- package/dist/react/workspace/toolbars/RightToolbar.js.map +0 -1
- package/dist/react/workspace/toolbars/TopToolbar.js +0 -82
- package/dist/react/workspace/toolbars/TopToolbar.js.map +0 -1
- package/dist/renderers/CandlestickRenderer.js +0 -98
- package/dist/renderers/CandlestickRenderer.js.map +0 -1
- package/dist/renderers/HistogramRenderer.js +0 -50
- package/dist/renderers/HistogramRenderer.js.map +0 -1
- package/dist/renderers/LineRenderer.js +0 -64
- package/dist/renderers/LineRenderer.js.map +0 -1
- package/dist/theme/colors.js +0 -19
- package/dist/theme/colors.js.map +0 -1
- package/dist/tools/barDivergenceCheck.js +0 -200
- package/dist/tools/barDivergenceCheck.js.map +0 -1
- package/dist/trading/TradingOverlayStore.js +0 -139
- package/dist/trading/TradingOverlayStore.js.map +0 -1
- package/dist/trading/UnmanagedIngestion.js +0 -114
- package/dist/trading/UnmanagedIngestion.js.map +0 -1
- package/dist/trading/__tests__/ManagedTradingController.test.js +0 -271
- package/dist/trading/__tests__/ManagedTradingController.test.js.map +0 -1
- package/dist/trading/__tests__/TradingOverlayStore.test.js +0 -267
- package/dist/trading/__tests__/TradingOverlayStore.test.js.map +0 -1
- package/dist/trading/__tests__/UnmanagedIngestion.test.js +0 -170
- package/dist/trading/__tests__/UnmanagedIngestion.test.js.map +0 -1
- package/dist/trading/managed/ManagedTradingController.js +0 -247
- package/dist/trading/managed/ManagedTradingController.js.map +0 -1
- package/dist/trading/managed/managedCapabilities.js +0 -79
- package/dist/trading/managed/managedCapabilities.js.map +0 -1
- package/dist/trading/managed/managedTypes.js +0 -13
- package/dist/trading/managed/managedTypes.js.map +0 -1
- package/dist/trading/tradingTypes.js +0 -8
- package/dist/trading/tradingTypes.js.map +0 -1
- package/dist/tscript/TScriptIndicator.js +0 -47
- package/dist/tscript/TScriptIndicator.js.map +0 -1
- package/dist/tscript/ast.js +0 -22
- package/dist/tscript/ast.js.map +0 -1
- package/dist/tscript/lexer.js +0 -187
- package/dist/tscript/lexer.js.map +0 -1
- package/dist/tscript/parser.js +0 -318
- package/dist/tscript/parser.js.map +0 -1
- package/dist/tscript/runtime.js +0 -475
- package/dist/tscript/runtime.js.map +0 -1
- package/dist/tscript/series.js +0 -79
- package/dist/tscript/series.js.map +0 -1
- package/dist/types/IChart.js +0 -2
- package/dist/types/IChart.js.map +0 -1
- package/dist/types/IRenderer.js +0 -2
- package/dist/types/IRenderer.js.map +0 -1
- package/dist/types/ISeries.js +0 -2
- package/dist/types/ISeries.js.map +0 -1
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
import { CandleEngine } from '../engine/CandleEngine';
|
|
2
|
-
/** Strip engine-only fields so the value is a plain OHLCV the chart series accepts. */
|
|
3
|
-
function toOHLCV(bar) {
|
|
4
|
-
return { time: bar.time, open: bar.open, high: bar.high, low: bar.low, close: bar.close, volume: bar.volume };
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Initial history window per timeframe — controls how much data the chart
|
|
8
|
-
* requests on first connect.
|
|
9
|
-
*
|
|
10
|
-
* TWO-LAYER ARCHITECTURE — these two concerns are independent:
|
|
11
|
-
* • CLIENT (this table): how many bars are fetched and held in memory for the
|
|
12
|
-
* initial view + scroll buffer. Larger = more data in the browser.
|
|
13
|
-
* • SERVER DB fill (bars.ts MAX_DB_SECONDS): the server always fills a full
|
|
14
|
-
* 12 months of 1m data (and all available daily history) into the DB cache
|
|
15
|
-
* in the background, regardless of the client window size. This means
|
|
16
|
-
* scroll-left lazy-loading hits the DB, not the upstream TV API.
|
|
17
|
-
*
|
|
18
|
-
* For sub-hour timeframes a large display window is impractical:
|
|
19
|
-
* 12 months of 1m = 525 600 bars — impossible to render, slow to transfer.
|
|
20
|
-
* For 4h and above, 12 months is a manageable bar count so we load it all
|
|
21
|
-
* upfront; the server will have it cached or will fill it synchronously.
|
|
22
|
-
*/
|
|
23
|
-
const HISTORY_WINDOWS = {
|
|
24
|
-
// Sub-hour: small display window; DB fills 12 months in background for scrollback.
|
|
25
|
-
'1m': 3 * 24 * 3600, // 3 days → ~4 320 bars
|
|
26
|
-
'3m': 7 * 24 * 3600, // 7 days → ~3 360 bars
|
|
27
|
-
'5m': 10 * 24 * 3600, // 10 days → ~2 880 bars
|
|
28
|
-
'15m': 30 * 24 * 3600, // 30 days → ~2 880 bars
|
|
29
|
-
'30m': 60 * 24 * 3600, // 60 days → ~2 880 bars
|
|
30
|
-
'1h': 90 * 24 * 3600, // 90 days → ~2 160 bars
|
|
31
|
-
// 4h and above: 12 months — bar counts are small enough to load in full.
|
|
32
|
-
'2h': 365 * 86400, // 12 months → ~4 380 bars
|
|
33
|
-
'4h': 365 * 86400, // 12 months → ~2 190 bars
|
|
34
|
-
'6h': 365 * 86400, // 12 months → ~1 460 bars
|
|
35
|
-
'12h': 365 * 86400, // 12 months → ~730 bars
|
|
36
|
-
'1d': 365 * 86400, // 12 months → ~365 bars
|
|
37
|
-
'3d': 365 * 86400, // 12 months → ~122 bars
|
|
38
|
-
'1w': 365 * 86400, // 12 months → ~52 bars
|
|
39
|
-
'1M': 365 * 86400, // 12 months → ~12 bars
|
|
40
|
-
};
|
|
41
|
-
const DEFAULT_HISTORY_WINDOW = 90 * 24 * 3600; // 90-day fallback (~200 bars at 1h)
|
|
42
|
-
/**
|
|
43
|
-
* How many seconds of history to fetch per lazy-load page (~500 candles).
|
|
44
|
-
* Used by `loadMoreHistory()` when the user pans past the oldest loaded bar.
|
|
45
|
-
*/
|
|
46
|
-
const PAGE_SIZES = {
|
|
47
|
-
'1m': 500 * 60,
|
|
48
|
-
'5m': 500 * 300,
|
|
49
|
-
'15m': 500 * 900,
|
|
50
|
-
'30m': 500 * 1800,
|
|
51
|
-
'1h': 500 * 3600,
|
|
52
|
-
'4h': 500 * 4 * 3600,
|
|
53
|
-
'1d': 500 * 86400,
|
|
54
|
-
'1w': 500 * 7 * 86400,
|
|
55
|
-
'1M': 500 * 30 * 86400,
|
|
56
|
-
};
|
|
57
|
-
const DEFAULT_PAGE_SIZE = 500 * 3600;
|
|
58
|
-
/** Monotonic counter so every subscriber UID is unique within the process. */
|
|
59
|
-
let _uidCounter = 0;
|
|
60
|
-
// ─── DatafeedConnector ────────────────────────────────────────────────────────
|
|
61
|
-
/**
|
|
62
|
-
* DatafeedConnector wires an {@link IDatafeed} to an {@link ISeries}.
|
|
63
|
-
*
|
|
64
|
-
* Responsibilities:
|
|
65
|
-
* - Generates stable, unique subscriber UIDs (`forgecharts-<symbol>-<tf>-<n>`).
|
|
66
|
-
* - Calls `getHistoricalBars` and pushes the result into `series.setData()`.
|
|
67
|
-
* - Calls `subscribeBars` so real-time ticks flow into `series.update()`.
|
|
68
|
-
* - Guards against stale async responses after a reconnect or destroy.
|
|
69
|
-
* - Tears down the active subscription cleanly on `disconnect()` / `destroy()`.
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* ```ts
|
|
73
|
-
* const connector = new DatafeedConnector(datafeed, series, {
|
|
74
|
-
* onLoading: (s, tf) => console.log('loading', s, tf),
|
|
75
|
-
* onLoaded: (s, tf, n) => console.log('loaded', n, 'bars'),
|
|
76
|
-
* onError: (s, tf, e) => console.error(e),
|
|
77
|
-
* });
|
|
78
|
-
*
|
|
79
|
-
* connector.connect('BTCUSDT', '1h');
|
|
80
|
-
* // ...later, on symbol change:
|
|
81
|
-
* connector.reconnect('ETHUSDT', '1h');
|
|
82
|
-
* // ...on unmount:
|
|
83
|
-
* connector.destroy();
|
|
84
|
-
* ```
|
|
85
|
-
*/
|
|
86
|
-
export class DatafeedConnector {
|
|
87
|
-
_datafeed;
|
|
88
|
-
_series;
|
|
89
|
-
_cb;
|
|
90
|
-
/**
|
|
91
|
-
* UID of the outstanding subscription.
|
|
92
|
-
* `null` means nothing is subscribed / all responses for older UIDs are stale.
|
|
93
|
-
*/
|
|
94
|
-
_activeUID = null;
|
|
95
|
-
_activeEngine = null;
|
|
96
|
-
_destroyed = false;
|
|
97
|
-
/** True while an async `loadMoreHistory()` request is in-flight. */
|
|
98
|
-
_isLoadingMore = false;
|
|
99
|
-
/** True once the datafeed returned 0 bars for a backwards page — no point fetching older. */
|
|
100
|
-
_noMoreHistory = false;
|
|
101
|
-
constructor(datafeed, series, callbacks) {
|
|
102
|
-
this._datafeed = datafeed;
|
|
103
|
-
this._series = series;
|
|
104
|
-
this._cb = callbacks;
|
|
105
|
-
}
|
|
106
|
-
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
107
|
-
/**
|
|
108
|
-
* Loads historical bars then subscribes for real-time updates.
|
|
109
|
-
* Safe to call while a previous subscription is still active — it will be
|
|
110
|
-
* cancelled first (same as calling `disconnect()` then `connect()`).
|
|
111
|
-
*/
|
|
112
|
-
connect(symbol, timeframe) {
|
|
113
|
-
if (this._destroyed)
|
|
114
|
-
return;
|
|
115
|
-
// Cancel any in-flight request / active subscription before starting fresh
|
|
116
|
-
this._cancelActive();
|
|
117
|
-
const uid = `forgecharts-${symbol}-${timeframe}-${++_uidCounter}`;
|
|
118
|
-
this._activeUID = uid;
|
|
119
|
-
this._cb.onLoading(symbol, timeframe);
|
|
120
|
-
const to = Math.floor(Date.now() / 1000);
|
|
121
|
-
const from = to - (HISTORY_WINDOWS[timeframe] ?? DEFAULT_HISTORY_WINDOW);
|
|
122
|
-
// Build the engine for this subscription. Close over `uid` for stale-guard.
|
|
123
|
-
const engine = new CandleEngine({
|
|
124
|
-
// Every live tick mutates the engine's bar array and the chart series.
|
|
125
|
-
onBarUpdated: (bar) => {
|
|
126
|
-
if (this._activeUID !== uid)
|
|
127
|
-
return;
|
|
128
|
-
const ohlcv = toOHLCV(bar);
|
|
129
|
-
this._series.update(ohlcv);
|
|
130
|
-
this._cb.onUpdate?.(ohlcv);
|
|
131
|
-
},
|
|
132
|
-
// When a gap is detected, fetch the missing range from REST and backfill.
|
|
133
|
-
// backfillGap() will call onResync which re-syncs the full chart series.
|
|
134
|
-
onGapDetected: (gapInfo) => {
|
|
135
|
-
if (this._activeUID !== uid)
|
|
136
|
-
return;
|
|
137
|
-
const gapFrom = Math.floor(gapInfo.fromTimeMs / 1000);
|
|
138
|
-
const gapTo = Math.ceil(gapInfo.toTimeMs / 1000);
|
|
139
|
-
this._datafeed.getHistoricalBars(symbol, timeframe, gapFrom, gapTo)
|
|
140
|
-
.then(({ bars }) => {
|
|
141
|
-
if (this._activeUID !== uid)
|
|
142
|
-
return;
|
|
143
|
-
engine.backfillGap(bars);
|
|
144
|
-
})
|
|
145
|
-
.catch(() => {
|
|
146
|
-
// Gap backfill errors are non-fatal — chart continues with live data
|
|
147
|
-
});
|
|
148
|
-
},
|
|
149
|
-
// Full re-paint after gap fill or reconnect reconciliation.
|
|
150
|
-
onResync: (bars) => {
|
|
151
|
-
if (this._activeUID !== uid)
|
|
152
|
-
return;
|
|
153
|
-
this._series.setData(bars.map(toOHLCV));
|
|
154
|
-
},
|
|
155
|
-
});
|
|
156
|
-
this._activeEngine = engine;
|
|
157
|
-
this._datafeed
|
|
158
|
-
.getHistoricalBars(symbol, timeframe, from, to)
|
|
159
|
-
.then((result) => {
|
|
160
|
-
// Guard: another connect() / destroy() may have superseded this request
|
|
161
|
-
if (this._activeUID !== uid)
|
|
162
|
-
return;
|
|
163
|
-
engine.initialize(result.bars, timeframe);
|
|
164
|
-
this._series.setData(engine.getBars().map(toOHLCV));
|
|
165
|
-
this._cb.onLoaded(symbol, timeframe, engine.getBars().length);
|
|
166
|
-
// Double-check still active before subscribing (setData callback could
|
|
167
|
-
// have triggered a synchronous reconnect in pathological code)
|
|
168
|
-
if (this._activeUID !== uid)
|
|
169
|
-
return;
|
|
170
|
-
this._datafeed.subscribeBars(symbol, timeframe, (bar) => {
|
|
171
|
-
if (this._activeUID === uid) {
|
|
172
|
-
engine.applyLiveUpdate(bar);
|
|
173
|
-
}
|
|
174
|
-
}, uid);
|
|
175
|
-
})
|
|
176
|
-
.catch((err) => {
|
|
177
|
-
if (this._activeUID !== uid)
|
|
178
|
-
return;
|
|
179
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
180
|
-
this._cb.onError(symbol, timeframe, message);
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Cancels the active subscription without starting a new one.
|
|
185
|
-
* Historical-bar `Promise` results arriving after this call are silently
|
|
186
|
-
* discarded via the UID guard.
|
|
187
|
-
*/
|
|
188
|
-
disconnect() {
|
|
189
|
-
this._cancelActive();
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Convenience shorthand: `disconnect()` then `connect()`.
|
|
193
|
-
* Use this for symbol or timeframe changes.
|
|
194
|
-
*/
|
|
195
|
-
reconnect(symbol, timeframe) {
|
|
196
|
-
this.disconnect();
|
|
197
|
-
this.connect(symbol, timeframe);
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Permanently deactivates the connector.
|
|
201
|
-
* Any subscription or in-flight request is cancelled; subsequent calls to
|
|
202
|
-
* `connect()` / `reconnect()` are no-ops.
|
|
203
|
-
*/
|
|
204
|
-
destroy() {
|
|
205
|
-
this._cancelActive();
|
|
206
|
-
this._destroyed = true;
|
|
207
|
-
}
|
|
208
|
-
// ─── Private ─────────────────────────────────────────────────────────────────
|
|
209
|
-
/**
|
|
210
|
-
* Fetches a page of older history (up to ~500 candles) and prepends it to
|
|
211
|
-
* the loaded dataset. Safe to call on every pan event — it is internally
|
|
212
|
-
* debounced by the `_isLoadingMore` flag and stops permanently once the
|
|
213
|
-
* datafeed signals no more history (`_noMoreHistory = true`).
|
|
214
|
-
*
|
|
215
|
-
* The series is updated automatically via the engine's `onResync` callback.
|
|
216
|
-
*/
|
|
217
|
-
loadMoreHistory(symbol, timeframe) {
|
|
218
|
-
if (this._destroyed || this._isLoadingMore || this._noMoreHistory)
|
|
219
|
-
return;
|
|
220
|
-
if (!this._activeEngine || !this._activeUID)
|
|
221
|
-
return;
|
|
222
|
-
const engine = this._activeEngine;
|
|
223
|
-
const oldest = engine.getBars()[0];
|
|
224
|
-
if (!oldest)
|
|
225
|
-
return;
|
|
226
|
-
const uid = this._activeUID;
|
|
227
|
-
const to = Math.floor(oldest.timeMs / 1000);
|
|
228
|
-
const page = PAGE_SIZES[timeframe] ?? DEFAULT_PAGE_SIZE;
|
|
229
|
-
const from = to - page;
|
|
230
|
-
this._isLoadingMore = true;
|
|
231
|
-
this._datafeed.getHistoricalBars(symbol, timeframe, from, to - 1)
|
|
232
|
-
.then(({ bars }) => {
|
|
233
|
-
if (this._activeUID !== uid)
|
|
234
|
-
return;
|
|
235
|
-
if (bars.length === 0) {
|
|
236
|
-
this._noMoreHistory = true;
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
-
engine.prependHistory(bars);
|
|
240
|
-
}
|
|
241
|
-
})
|
|
242
|
-
.catch(() => {
|
|
243
|
-
// Non-fatal — user can scroll again to retry
|
|
244
|
-
})
|
|
245
|
-
.finally(() => {
|
|
246
|
-
if (this._activeUID === uid)
|
|
247
|
-
this._isLoadingMore = false;
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
_cancelActive() {
|
|
251
|
-
if (this._activeUID !== null) {
|
|
252
|
-
try {
|
|
253
|
-
this._datafeed.unsubscribeBars(this._activeUID);
|
|
254
|
-
}
|
|
255
|
-
catch {
|
|
256
|
-
// Best-effort teardown — datafeed may already be closed
|
|
257
|
-
}
|
|
258
|
-
this._activeUID = null;
|
|
259
|
-
}
|
|
260
|
-
if (this._activeEngine !== null) {
|
|
261
|
-
this._activeEngine.reset();
|
|
262
|
-
this._activeEngine = null;
|
|
263
|
-
}
|
|
264
|
-
this._isLoadingMore = false;
|
|
265
|
-
this._noMoreHistory = false;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
//# sourceMappingURL=DatafeedConnector.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DatafeedConnector.js","sourceRoot":"","sources":["../../src/datafeed/DatafeedConnector.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,318 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CandleEngine — the single source of truth for live candle data.
|
|
3
|
-
*
|
|
4
|
-
* Architecture:
|
|
5
|
-
*
|
|
6
|
-
* WebSocket feed
|
|
7
|
-
* ↓
|
|
8
|
-
* Market Data Normalizer (normalizeOhlcv — handles strings, ms vs s)
|
|
9
|
-
* ↓
|
|
10
|
-
* CandleEngine (applyCandleUpdate — O(1) update path)
|
|
11
|
-
* ↓
|
|
12
|
-
* Chart Update Adapter (onBarUpdated / onResync callbacks)
|
|
13
|
-
* ↓
|
|
14
|
-
* Chart Library (series.update / series.setData)
|
|
15
|
-
*
|
|
16
|
-
* Guarantees:
|
|
17
|
-
* - open is never overwritten on an existing live candle
|
|
18
|
-
* - high can only increase within a candle
|
|
19
|
-
* - low can only decrease within a candle
|
|
20
|
-
* - close updates on every tick
|
|
21
|
-
* - out-of-order ticks are silently discarded
|
|
22
|
-
* - gaps are detected and reported for async REST backfill
|
|
23
|
-
* - the engine is framework-agnostic (no DOM, React, or chart-lib imports)
|
|
24
|
-
*/
|
|
25
|
-
import { timeframeToMs, getCalendarBucketStart } from './timeframeUtils';
|
|
26
|
-
import { getMissingBarCount, mergeBars } from './mergeUtils';
|
|
27
|
-
import { assertOhlcRelationships, assertBucketAlignment, assertNotFinalized, assertOpenImmutability, assertNoDuplicateAppend, assertFinalizedUnchanged, } from './candleInvariants';
|
|
28
|
-
// ─── Internal normalizer ─────────────────────────────────────────────────────
|
|
29
|
-
/**
|
|
30
|
-
* Normalise a raw OHLCV object into a CandleBar.
|
|
31
|
-
*
|
|
32
|
-
* - Coerces all fields to `number` (handles numeric strings from the feed).
|
|
33
|
-
* - Auto-detects ms vs s timestamps: values > 1e12 are treated as milliseconds.
|
|
34
|
-
* - Aligns `timeMs` to the correct candle-open boundary, with calendar
|
|
35
|
-
* alignment for `1w` (Monday) and `1M` (first of month).
|
|
36
|
-
*
|
|
37
|
-
* Never use `Date.now()` for candle time — always use the feed-provided value.
|
|
38
|
-
*/
|
|
39
|
-
function normalizeOhlcv(raw, intervalMs, tf) {
|
|
40
|
-
const rawTime = Number(raw.time);
|
|
41
|
-
// Auto-detect: Unix ms timestamps are > 1e12 (year 2001+).
|
|
42
|
-
// If the value is smaller, treat it as seconds and convert.
|
|
43
|
-
const timeMs = rawTime > 1e12 ? rawTime : rawTime * 1000;
|
|
44
|
-
// Calendar-aware bucket alignment (handles 1w/1M specially)
|
|
45
|
-
const bucketMs = getCalendarBucketStart(timeMs, tf);
|
|
46
|
-
const timeSec = Math.floor(bucketMs / 1000);
|
|
47
|
-
return {
|
|
48
|
-
timeMs: bucketMs,
|
|
49
|
-
time: timeSec,
|
|
50
|
-
open: Number(raw.open),
|
|
51
|
-
high: Number(raw.high),
|
|
52
|
-
low: Number(raw.low),
|
|
53
|
-
close: Number(raw.close),
|
|
54
|
-
volume: Number(raw.volume ?? 0),
|
|
55
|
-
isClosed: raw.isClosed ?? false,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
// ─── CandleEngine ─────────────────────────────────────────────────────────────
|
|
59
|
-
export class CandleEngine {
|
|
60
|
-
/** Authoritative ordered bar array. Only the rightmost bar may be open. */
|
|
61
|
-
_bars = [];
|
|
62
|
-
_timeframe = '1m';
|
|
63
|
-
_intervalMs = 60_000;
|
|
64
|
-
_opts;
|
|
65
|
-
constructor(options = {}) {
|
|
66
|
-
this._opts = options;
|
|
67
|
-
}
|
|
68
|
-
// ── Initialisation ──────────────────────────────────────────────────────────
|
|
69
|
-
/**
|
|
70
|
-
* Seed the engine with historical bars and set the active timeframe.
|
|
71
|
-
* All existing bars are replaced.
|
|
72
|
-
*
|
|
73
|
-
* Historical bars loaded from REST are assumed closed, except possibly
|
|
74
|
-
* the last one (which may represent the still-forming candle).
|
|
75
|
-
*
|
|
76
|
-
* @param history Raw bars from a REST endpoint (oldest → newest).
|
|
77
|
-
* @param timeframe The active timeframe label (e.g. '1m', '1h').
|
|
78
|
-
*/
|
|
79
|
-
initialize(history, timeframe) {
|
|
80
|
-
this._timeframe = timeframe;
|
|
81
|
-
this._intervalMs = timeframeToMs(timeframe);
|
|
82
|
-
// Normalise, deduplicate by timeMs (latest entry wins), sort newest-last
|
|
83
|
-
const seen = new Map();
|
|
84
|
-
for (const raw of history) {
|
|
85
|
-
const bar = normalizeOhlcv(raw, this._intervalMs, this._timeframe);
|
|
86
|
-
seen.set(bar.timeMs, bar);
|
|
87
|
-
}
|
|
88
|
-
this._bars = Array.from(seen.values()).sort((a, b) => a.timeMs - b.timeMs);
|
|
89
|
-
// Mark all bars except the last as closed
|
|
90
|
-
for (let i = 0; i < this._bars.length - 1; i++) {
|
|
91
|
-
this._bars[i].isClosed = true;
|
|
92
|
-
}
|
|
93
|
-
this._debugLog('initialize', {
|
|
94
|
-
timeframe,
|
|
95
|
-
intervalMs: this._intervalMs,
|
|
96
|
-
barCount: this._bars.length,
|
|
97
|
-
firstTime: this._bars[0]?.timeMs,
|
|
98
|
-
lastTime: this._bars[this._bars.length - 1]?.timeMs,
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
// ── Live updates ────────────────────────────────────────────────────────────
|
|
102
|
-
/**
|
|
103
|
-
* Process a single live OHLCV tick from the WebSocket feed.
|
|
104
|
-
*
|
|
105
|
-
* Algorithm (mirrors TradingView / Binance candle engine behaviour):
|
|
106
|
-
*
|
|
107
|
-
* 1. Normalise: coerce strings to numbers, detect ms vs s, align to bucket.
|
|
108
|
-
* 2. Empty array → append as first bar.
|
|
109
|
-
* 3. incoming.timeMs === last.timeMs → SAME CANDLE (update in-place):
|
|
110
|
-
* open = unchanged (first tick's open is authoritative)
|
|
111
|
-
* high = max(last.high, inc.high, inc.close)
|
|
112
|
-
* low = min(last.low, inc.low, inc.close)
|
|
113
|
-
* close = inc.close
|
|
114
|
-
* volume = inc.volume
|
|
115
|
-
* 4. incoming.timeMs > last.timeMs → NEW CANDLE:
|
|
116
|
-
* - Detect and report any gap (missing buckets between last and incoming).
|
|
117
|
-
* - Mark last bar as closed, fire onBarClosed.
|
|
118
|
-
* - Append incoming as a fresh live candle.
|
|
119
|
-
* 5. incoming.timeMs < last.timeMs → OUT-OF-ORDER: discard silently.
|
|
120
|
-
*
|
|
121
|
-
* @returns UpdateResult indicating what was done — the chart adapter uses
|
|
122
|
-
* this to choose between series.update() and series.setData().
|
|
123
|
-
*/
|
|
124
|
-
applyLiveUpdate(raw) {
|
|
125
|
-
const incoming = normalizeOhlcv(raw, this._intervalMs, this._timeframe);
|
|
126
|
-
// Invariant: incoming bar must be bucket-aligned and OHLC-valid
|
|
127
|
-
assertBucketAlignment(incoming, this._timeframe, 'applyLiveUpdate:normalize');
|
|
128
|
-
assertOhlcRelationships(incoming, 'applyLiveUpdate:normalize');
|
|
129
|
-
// ── First bar ever ─────────────────────────────────────────────────────
|
|
130
|
-
if (this._bars.length === 0) {
|
|
131
|
-
this._bars.push(incoming);
|
|
132
|
-
const result = { type: 'append', bar: incoming, previousBarClosed: false };
|
|
133
|
-
this._debugLog('append:first', incoming);
|
|
134
|
-
this._opts.onBarUpdated?.(incoming, result);
|
|
135
|
-
return result;
|
|
136
|
-
}
|
|
137
|
-
const last = this._bars[this._bars.length - 1];
|
|
138
|
-
// ── Same candle: update in-place (O(1)) ────────────────────────────────
|
|
139
|
-
if (incoming.timeMs === last.timeMs) {
|
|
140
|
-
// Invariant: must not mutate a finalized candle
|
|
141
|
-
assertNotFinalized(last, 'applyLiveUpdate:same-bucket');
|
|
142
|
-
const prevOpen = last.open;
|
|
143
|
-
const prevCount = this._bars.length;
|
|
144
|
-
// open is intentionally not updated — the first tick's open is correct
|
|
145
|
-
last.high = Math.max(last.high, incoming.high, incoming.close);
|
|
146
|
-
last.low = Math.min(last.low, incoming.low, incoming.close);
|
|
147
|
-
last.close = incoming.close;
|
|
148
|
-
last.volume = incoming.volume;
|
|
149
|
-
// Invariant: open must be unchanged, no spurious append must have occurred
|
|
150
|
-
assertOpenImmutability(prevOpen, last, 'applyLiveUpdate:same-bucket');
|
|
151
|
-
assertOhlcRelationships(last, 'applyLiveUpdate:same-bucket');
|
|
152
|
-
assertNoDuplicateAppend(prevCount, this._bars.length, 'applyLiveUpdate:same-bucket');
|
|
153
|
-
const result = { type: 'update', bar: last };
|
|
154
|
-
this._debugLog('update', last);
|
|
155
|
-
this._opts.onBarUpdated?.(last, result);
|
|
156
|
-
return result;
|
|
157
|
-
}
|
|
158
|
-
// ── Out-of-order: older than the current live candle ──────────────────
|
|
159
|
-
if (incoming.timeMs < last.timeMs) {
|
|
160
|
-
this._debugLog('ignore', {
|
|
161
|
-
incomingTimeMs: incoming.timeMs,
|
|
162
|
-
lastTimeMs: last.timeMs,
|
|
163
|
-
delta: last.timeMs - incoming.timeMs,
|
|
164
|
-
});
|
|
165
|
-
return { type: 'ignore' };
|
|
166
|
-
}
|
|
167
|
-
// ── New candle (incoming.timeMs > last.timeMs) ─────────────────────────
|
|
168
|
-
// Gap detection: how many candle buckets are missing between last and new?
|
|
169
|
-
const missingCount = getMissingBarCount(last.timeMs, incoming.timeMs, this._intervalMs);
|
|
170
|
-
if (missingCount > 0) {
|
|
171
|
-
const gapInfo = { fromTimeMs: last.timeMs, toTimeMs: incoming.timeMs, missingCount };
|
|
172
|
-
this._debugLog('gap', gapInfo);
|
|
173
|
-
this._opts.onGapDetected?.(gapInfo);
|
|
174
|
-
}
|
|
175
|
-
// Finalise the previous live candle
|
|
176
|
-
const prevLastBar = { ...last }; // snapshot for post-append invariant check
|
|
177
|
-
last.isClosed = true;
|
|
178
|
-
this._opts.onBarClosed?.(last);
|
|
179
|
-
// Append the new candle
|
|
180
|
-
this._bars.push(incoming);
|
|
181
|
-
// Invariant: previous bar must be closed and not mutated after the append
|
|
182
|
-
assertFinalizedUnchanged(prevLastBar, this._bars, 'applyLiveUpdate:new-bucket');
|
|
183
|
-
assertOhlcRelationships(incoming, 'applyLiveUpdate:new-bucket');
|
|
184
|
-
const result = missingCount > 0
|
|
185
|
-
? { type: 'gap', bar: incoming, missingCount }
|
|
186
|
-
: { type: 'append', bar: incoming, previousBarClosed: true };
|
|
187
|
-
this._debugLog(result.type, incoming);
|
|
188
|
-
this._opts.onBarUpdated?.(incoming, result);
|
|
189
|
-
return result;
|
|
190
|
-
}
|
|
191
|
-
// ── Gap backfill ────────────────────────────────────────────────────────────
|
|
192
|
-
/**
|
|
193
|
-
* Merge REST-fetched bars into the engine to fill a detected gap.
|
|
194
|
-
*
|
|
195
|
-
* Incoming bars are treated as authoritative closed candles.
|
|
196
|
-
* The current live candle is preserved if present.
|
|
197
|
-
*
|
|
198
|
-
* After calling this, `onResync` fires — the chart adapter must call
|
|
199
|
-
* `series.setData(engine.getBars())` to fully resync the chart.
|
|
200
|
-
*
|
|
201
|
-
* @param rawBars Historical bars from the REST endpoint covering the gap.
|
|
202
|
-
*/
|
|
203
|
-
backfillGap(rawBars) {
|
|
204
|
-
const incoming = rawBars.map(r => {
|
|
205
|
-
const bar = normalizeOhlcv(r, this._intervalMs, this._timeframe);
|
|
206
|
-
bar.isClosed = true; // REST bars are closed and authoritative
|
|
207
|
-
return bar;
|
|
208
|
-
});
|
|
209
|
-
// Preserve the live candle's open state after merge
|
|
210
|
-
const liveBefore = this._bars[this._bars.length - 1];
|
|
211
|
-
this._bars = mergeBars(this._bars, incoming);
|
|
212
|
-
// Re-apply live candle's isClosed=false if it survived the merge
|
|
213
|
-
if (liveBefore && !liveBefore.isClosed) {
|
|
214
|
-
const last = this._bars[this._bars.length - 1];
|
|
215
|
-
if (last && last.timeMs === liveBefore.timeMs) {
|
|
216
|
-
last.isClosed = false;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
this._debugLog('backfillGap', { filled: rawBars.length, totalBars: this._bars.length });
|
|
220
|
-
this._opts.onResync?.(this._bars);
|
|
221
|
-
}
|
|
222
|
-
// ── Reconnect reconciliation ────────────────────────────────────────────────
|
|
223
|
-
/**
|
|
224
|
-
* Reconcile after a WebSocket reconnect.
|
|
225
|
-
*
|
|
226
|
-
* Fetch a rolling overlap window from REST (e.g. last 50 bars or 2 hours)
|
|
227
|
-
* and pass them here. The engine merges them with the existing bar array,
|
|
228
|
-
* correcting any candles that were missed or corrupted during the outage.
|
|
229
|
-
*
|
|
230
|
-
* After reconciliation, `onResync` fires so the chart adapter can resync
|
|
231
|
-
* the chart series with `series.setData(engine.getBars())`.
|
|
232
|
-
*
|
|
233
|
-
* @param recentBars REST bars covering the reconnect overlap window.
|
|
234
|
-
*/
|
|
235
|
-
handleReconnect(recentBars) {
|
|
236
|
-
const incoming = recentBars.map(r => normalizeOhlcv(r, this._intervalMs, this._timeframe));
|
|
237
|
-
this._bars = mergeBars(this._bars, incoming);
|
|
238
|
-
this._debugLog('handleReconnect', { count: recentBars.length, totalBars: this._bars.length });
|
|
239
|
-
this._opts.onResync?.(this._bars);
|
|
240
|
-
}
|
|
241
|
-
// ── Trade-tick stub ─────────────────────────────────────────────────────────
|
|
242
|
-
/**
|
|
243
|
-
* Stub for future tick/trade-based candle construction.
|
|
244
|
-
*
|
|
245
|
-
* When implemented, this will:
|
|
246
|
-
* - Assign the trade to its bucket using `getBucketStart(timeMs, intervalMs)`.
|
|
247
|
-
* - Create a new candle if the bucket is absent.
|
|
248
|
-
* - Update OHLCV: open/close from trade price, high/low expanded, volume summed.
|
|
249
|
-
*
|
|
250
|
-
* Not yet implemented — structure is in place for future extension.
|
|
251
|
-
*/
|
|
252
|
-
applyTradeTick(_trade) {
|
|
253
|
-
// TODO: implement tick-based candle construction
|
|
254
|
-
}
|
|
255
|
-
// ── Read access ─────────────────────────────────────────────────────────────
|
|
256
|
-
/**
|
|
257
|
-
* Returns a readonly view of the authoritative bar array.
|
|
258
|
-
* Do not mutate — call `applyLiveUpdate`, `backfillGap`, etc. instead.
|
|
259
|
-
*/
|
|
260
|
-
getBars() {
|
|
261
|
-
return this._bars;
|
|
262
|
-
}
|
|
263
|
-
/** Returns the most recent (possibly still live) bar, or null if empty. */
|
|
264
|
-
getLastBar() {
|
|
265
|
-
return this._bars[this._bars.length - 1] ?? null;
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Prepend older historical bars fetched during a lazy scroll-left page load.
|
|
269
|
-
* Only bars older than the current oldest bar are accepted; duplicates and
|
|
270
|
-
* out-of-range bars are silently dropped.
|
|
271
|
-
* Fires `onResync` so the chart adapter updates the series with `setData`.
|
|
272
|
-
*
|
|
273
|
-
* @param rawBars Historical bars from the REST endpoint, oldest → newest.
|
|
274
|
-
*/
|
|
275
|
-
prependHistory(rawBars) {
|
|
276
|
-
const oldest = this._bars[0];
|
|
277
|
-
const cutoff = oldest ? oldest.timeMs : Infinity;
|
|
278
|
-
const incoming = rawBars
|
|
279
|
-
.map(r => {
|
|
280
|
-
const bar = normalizeOhlcv(r, this._intervalMs, this._timeframe);
|
|
281
|
-
bar.isClosed = true;
|
|
282
|
-
return bar;
|
|
283
|
-
})
|
|
284
|
-
.filter(bar => bar.timeMs < cutoff);
|
|
285
|
-
if (incoming.length === 0)
|
|
286
|
-
return;
|
|
287
|
-
incoming.sort((a, b) => a.timeMs - b.timeMs);
|
|
288
|
-
this._bars = [...incoming, ...this._bars];
|
|
289
|
-
this._debugLog('prependHistory', {
|
|
290
|
-
prepended: incoming.length,
|
|
291
|
-
firstTime: this._bars[0]?.timeMs,
|
|
292
|
-
});
|
|
293
|
-
this._opts.onResync?.(this._bars);
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Clear all state.
|
|
297
|
-
* Call before switching symbol or timeframe, or on a hard disconnect.
|
|
298
|
-
*/
|
|
299
|
-
reset() {
|
|
300
|
-
this._bars = [];
|
|
301
|
-
this._debugLog('reset', { timeframe: this._timeframe });
|
|
302
|
-
}
|
|
303
|
-
// ── Private helpers ──────────────────────────────────────────────────────────
|
|
304
|
-
_debugLog(action, data) {
|
|
305
|
-
if (!this._opts.debug)
|
|
306
|
-
return;
|
|
307
|
-
console.debug('[CANDLE_ENGINE]', {
|
|
308
|
-
action,
|
|
309
|
-
timeframe: this._timeframe,
|
|
310
|
-
barCount: this._bars.length,
|
|
311
|
-
liveBar: this._bars[this._bars.length - 1]
|
|
312
|
-
? { open: this._bars[this._bars.length - 1].open, high: this._bars[this._bars.length - 1].high, low: this._bars[this._bars.length - 1].low, close: this._bars[this._bars.length - 1].close }
|
|
313
|
-
: null,
|
|
314
|
-
...data,
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
//# sourceMappingURL=CandleEngine.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"CandleEngine.js","sourceRoot":"","sources":["../../src/engine/CandleEngine.ts"],"names":[],"mappings":"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"}
|