@tradejs/cli 3.1.29-beta.286 → 3.1.29-beta.287
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/cli.js +647 -486
- package/dist/lib/mcpReplayProcess.js +4 -2
- package/dist/lib/strategyResearch.js +24 -11
- package/dist/scripts/mcpWorker.js +4 -2
- package/dist/scripts/replay.js +644 -498
- package/dist/scripts/replayRunner.js +646 -498
- package/dist/scripts/strategyResearch.js +24 -11
- package/package.json +7 -7
package/dist/scripts/replay.js
CHANGED
|
@@ -35,10 +35,481 @@ __export(replay_exports, {
|
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(replay_exports);
|
|
37
37
|
|
|
38
|
+
// src/lib/replay/historicalSignalsReplayPreparation.ts
|
|
39
|
+
var import_data = require("@tradejs/core/data");
|
|
40
|
+
|
|
41
|
+
// src/lib/replay/support.ts
|
|
42
|
+
var import_chalk = __toESM(require("chalk"));
|
|
43
|
+
|
|
44
|
+
// src/lib/runtimeModeConfig.ts
|
|
45
|
+
var buildRuntimeModeStrategyConfig = ({
|
|
46
|
+
strategyConfig,
|
|
47
|
+
env,
|
|
48
|
+
interval,
|
|
49
|
+
makeOrders,
|
|
50
|
+
recordRuntimeTrades,
|
|
51
|
+
aiReplayAnalyses
|
|
52
|
+
}) => ({
|
|
53
|
+
...strategyConfig,
|
|
54
|
+
ENV: env,
|
|
55
|
+
MAKE_ORDERS: makeOrders,
|
|
56
|
+
INTERVAL: strategyConfig.INTERVAL ?? interval,
|
|
57
|
+
...typeof recordRuntimeTrades === "boolean" ? { RECORD_RUNTIME_TRADES: recordRuntimeTrades } : {},
|
|
58
|
+
...aiReplayAnalyses?.length ? { AI_REPLAY_ANALYSES: aiReplayAnalyses } : {}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// src/lib/replay/support.ts
|
|
62
|
+
var REPLAY_RESULTS_BY_STRATEGY_HEADERS = [
|
|
63
|
+
import_chalk.default.blue("STRATEGY"),
|
|
64
|
+
import_chalk.default.yellow("TICKERS"),
|
|
65
|
+
import_chalk.default.yellow("TRADE TICKERS"),
|
|
66
|
+
import_chalk.default.cyan("ORDERS"),
|
|
67
|
+
import_chalk.default.cyan("WIN/LOSS (%)"),
|
|
68
|
+
import_chalk.default.cyan("PROFIT"),
|
|
69
|
+
import_chalk.default.cyan("AVG TRADE")
|
|
70
|
+
];
|
|
71
|
+
var REPLAY_RUNTIME_COMPARISON_HEADERS = [
|
|
72
|
+
import_chalk.default.blue("STRATEGY"),
|
|
73
|
+
import_chalk.default.cyan("BT ENTRIES"),
|
|
74
|
+
import_chalk.default.cyan("BT PNL"),
|
|
75
|
+
import_chalk.default.yellow("RT TRADES"),
|
|
76
|
+
import_chalk.default.yellow("RT PNL"),
|
|
77
|
+
import_chalk.default.green("MATCHED"),
|
|
78
|
+
import_chalk.default.red("ORDER FAILED"),
|
|
79
|
+
import_chalk.default.yellow("RT ONLY"),
|
|
80
|
+
import_chalk.default.magenta("BT ONLY"),
|
|
81
|
+
import_chalk.default.red("UNMATCHED")
|
|
82
|
+
];
|
|
83
|
+
var REPLAY_RESULTS_CONFIG = "replay";
|
|
84
|
+
var REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS = 1;
|
|
85
|
+
var REPLAY_RUNTIME_COMPARE_TOLERANCE_MS = REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS * 15 * 60 * 1e3;
|
|
86
|
+
var formatReplayRuntimeCompareTolerance = () => `${REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS} ${REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS === 1 ? "bar" : "bars"}`;
|
|
87
|
+
var getReplayRuntimeUnmatchedCount = (row) => row.orderFailed + row.runtimeOnly + row.backtestOnly;
|
|
88
|
+
var buildReplayStrategyConfig = ({
|
|
89
|
+
strategyConfig,
|
|
90
|
+
interval
|
|
91
|
+
}) => ({
|
|
92
|
+
...buildRuntimeModeStrategyConfig({
|
|
93
|
+
strategyConfig,
|
|
94
|
+
env: "PARITY",
|
|
95
|
+
interval,
|
|
96
|
+
makeOrders: false,
|
|
97
|
+
recordRuntimeTrades: false
|
|
98
|
+
}),
|
|
99
|
+
SIMULATE_ORDERS: true
|
|
100
|
+
});
|
|
101
|
+
var toRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
102
|
+
var compactReplayAdditionalIndicators = (additionalIndicators) => {
|
|
103
|
+
const additional = toRecord(additionalIndicators);
|
|
104
|
+
if (!additional) {
|
|
105
|
+
return void 0;
|
|
106
|
+
}
|
|
107
|
+
const compact = {};
|
|
108
|
+
if (additional.executionSlippage != null) {
|
|
109
|
+
compact.executionSlippage = additional.executionSlippage;
|
|
110
|
+
}
|
|
111
|
+
if (additional.marketContext != null) {
|
|
112
|
+
compact.marketContext = additional.marketContext;
|
|
113
|
+
}
|
|
114
|
+
const baseContext = toRecord(additional.baseContext);
|
|
115
|
+
const relative = toRecord(baseContext?.relative);
|
|
116
|
+
const execution = toRecord(relative?.execution);
|
|
117
|
+
if (execution) {
|
|
118
|
+
compact.baseContext = {
|
|
119
|
+
relative: {
|
|
120
|
+
execution
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return Object.keys(compact).length ? compact : void 0;
|
|
125
|
+
};
|
|
126
|
+
var compactReplaySignal = (signal) => {
|
|
127
|
+
if (!signal) {
|
|
128
|
+
return void 0;
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
signalId: signal.signalId,
|
|
132
|
+
strategy: signal.strategy,
|
|
133
|
+
symbol: signal.symbol,
|
|
134
|
+
interval: signal.interval,
|
|
135
|
+
direction: signal.direction,
|
|
136
|
+
timestamp: signal.timestamp,
|
|
137
|
+
prices: signal.prices,
|
|
138
|
+
indicators: {},
|
|
139
|
+
figures: {},
|
|
140
|
+
additionalIndicators: compactReplayAdditionalIndicators(
|
|
141
|
+
signal.additionalIndicators
|
|
142
|
+
),
|
|
143
|
+
isConfigFromBacktest: signal.isConfigFromBacktest,
|
|
144
|
+
...signal.orderStatus ? { orderStatus: signal.orderStatus } : {},
|
|
145
|
+
...signal.orderSkipReason ? { orderSkipReason: signal.orderSkipReason } : {},
|
|
146
|
+
...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {},
|
|
147
|
+
...signal.ml ? { ml: signal.ml } : {}
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// src/lib/marketData/windows.ts
|
|
152
|
+
var import_indicators = require("@tradejs/core/indicators");
|
|
153
|
+
var splitCandlesForReplayWindow = (candles, start, preloadStart) => {
|
|
154
|
+
const prevData = [];
|
|
155
|
+
const replayData = [];
|
|
156
|
+
for (const candle of candles) {
|
|
157
|
+
if (candle.timestamp < preloadStart) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (candle.timestamp < start) {
|
|
161
|
+
prevData.push(candle);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
replayData.push(candle);
|
|
165
|
+
}
|
|
166
|
+
return { prevData, replayData };
|
|
167
|
+
};
|
|
168
|
+
var alignSymbolWithBtcReference = (symbolCandles, btcCandles) => (0, import_indicators.alignSortedCandlesByTimestamp)(symbolCandles, btcCandles);
|
|
169
|
+
|
|
170
|
+
// src/lib/replay/historicalSignalsReplayPreparation.ts
|
|
171
|
+
var buildPreparedData = ({
|
|
172
|
+
data,
|
|
173
|
+
btcData,
|
|
174
|
+
ethData,
|
|
175
|
+
btcBinanceData,
|
|
176
|
+
btcCoinbaseData,
|
|
177
|
+
start,
|
|
178
|
+
preloadStart
|
|
179
|
+
}) => {
|
|
180
|
+
const { prevData: prevDataRaw, replayData: replayDataRaw } = splitCandlesForReplayWindow(data, start, preloadStart);
|
|
181
|
+
const { prevData: btcPrevDataRaw, replayData: btcReplayDataRaw } = splitCandlesForReplayWindow(btcData, start, preloadStart);
|
|
182
|
+
const { prevData: ethPrevDataRaw, replayData: ethReplayDataRaw } = splitCandlesForReplayWindow(ethData, start, preloadStart);
|
|
183
|
+
const { prevData: btcBinancePrevDataRaw } = splitCandlesForReplayWindow(
|
|
184
|
+
btcBinanceData,
|
|
185
|
+
start,
|
|
186
|
+
preloadStart
|
|
187
|
+
);
|
|
188
|
+
const { prevData: btcCoinbasePrevDataRaw } = splitCandlesForReplayWindow(
|
|
189
|
+
btcCoinbaseData,
|
|
190
|
+
start,
|
|
191
|
+
preloadStart
|
|
192
|
+
);
|
|
193
|
+
const { alignedCoinCandles: prevData, alignedBtcCandles: btcPrevData } = alignSymbolWithBtcReference(prevDataRaw, btcPrevDataRaw);
|
|
194
|
+
const { alignedCoinCandles: replayData, alignedBtcCandles: btcReplayData } = alignSymbolWithBtcReference(replayDataRaw, btcReplayDataRaw);
|
|
195
|
+
const { alignedBtcCandles: ethPrevData } = alignSymbolWithBtcReference(
|
|
196
|
+
prevData,
|
|
197
|
+
ethPrevDataRaw
|
|
198
|
+
);
|
|
199
|
+
const { alignedBtcCandles: ethReplayData } = alignSymbolWithBtcReference(
|
|
200
|
+
replayData,
|
|
201
|
+
ethReplayDataRaw
|
|
202
|
+
);
|
|
203
|
+
const { alignedBtcCandles: btcBinancePrevData } = alignSymbolWithBtcReference(
|
|
204
|
+
prevDataRaw,
|
|
205
|
+
btcBinancePrevDataRaw
|
|
206
|
+
);
|
|
207
|
+
const { alignedBtcCandles: btcCoinbasePrevData } = alignSymbolWithBtcReference(prevDataRaw, btcCoinbasePrevDataRaw);
|
|
208
|
+
return {
|
|
209
|
+
prevData,
|
|
210
|
+
btcPrevData,
|
|
211
|
+
ethPrevData,
|
|
212
|
+
replayData,
|
|
213
|
+
btcReplayData,
|
|
214
|
+
ethReplayData,
|
|
215
|
+
btcBinancePrevData,
|
|
216
|
+
btcCoinbasePrevData
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
var strategyIncludesSymbol = (strategy, symbol) => {
|
|
220
|
+
const tickers = strategy.selection?.tickers;
|
|
221
|
+
if (!tickers) return true;
|
|
222
|
+
const normalizedSymbol = symbol.trim().toUpperCase();
|
|
223
|
+
return tickers.some(
|
|
224
|
+
(ticker) => ticker.trim().toUpperCase() === normalizedSymbol
|
|
225
|
+
);
|
|
226
|
+
};
|
|
227
|
+
var buildSymbolSharedReplayKey = ({
|
|
228
|
+
userName,
|
|
229
|
+
connectorName,
|
|
230
|
+
interval,
|
|
231
|
+
symbol,
|
|
232
|
+
start,
|
|
233
|
+
end
|
|
234
|
+
}) => ["replay", userName, connectorName, symbol, interval, start, end].join(":");
|
|
235
|
+
var resolveHistoricalReplayIntervals = (baseInterval, strategyIntervals) => {
|
|
236
|
+
const intervals = [
|
|
237
|
+
...new Set(
|
|
238
|
+
strategyIntervals.map((value) => {
|
|
239
|
+
if (!value || !Number.isFinite((0, import_data.intervalToMs)(value)) || (0, import_data.intervalToMs)(value) <= 0)
|
|
240
|
+
throw new Error(`Invalid replay strategy interval: ${value}`);
|
|
241
|
+
return value;
|
|
242
|
+
})
|
|
243
|
+
)
|
|
244
|
+
].sort((a, b) => (0, import_data.intervalToMs)(a) - (0, import_data.intervalToMs)(b));
|
|
245
|
+
if (intervals.length > 1 && intervals.includes("M")) {
|
|
246
|
+
throw new Error("Mixed replay does not support calendar intervals");
|
|
247
|
+
}
|
|
248
|
+
const baseMs = (0, import_data.intervalToMs)(baseInterval);
|
|
249
|
+
if (intervals.some(
|
|
250
|
+
(value) => (0, import_data.intervalToMs)(value) < baseMs || (0, import_data.intervalToMs)(value) % baseMs !== 0
|
|
251
|
+
)) {
|
|
252
|
+
throw new Error(
|
|
253
|
+
"Replay strategy intervals must be multiples of the base timeframe"
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
if (!intervals.length || !intervals.includes(baseInterval)) {
|
|
257
|
+
throw new Error(
|
|
258
|
+
"Replay base timeframe must match an active strategy interval"
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
return intervals;
|
|
262
|
+
};
|
|
263
|
+
var prepareMultiIntervalHistoricalReplay = async (context, adapters, loadReferences) => {
|
|
264
|
+
const intervals = resolveHistoricalReplayIntervals(
|
|
265
|
+
context.interval,
|
|
266
|
+
context.strategies.map((s) => s.strategyConfig.INTERVAL)
|
|
267
|
+
);
|
|
268
|
+
const baseMs = (0, import_data.intervalToMs)(context.interval);
|
|
269
|
+
const plan = {
|
|
270
|
+
symbolRuntimes: [],
|
|
271
|
+
orderedTimestamps: [],
|
|
272
|
+
sharedReplayKeyPrefixes: [],
|
|
273
|
+
runtimeLineages: []
|
|
274
|
+
};
|
|
275
|
+
const timestamps = /* @__PURE__ */ new Set();
|
|
276
|
+
const baseRuntimes = /* @__PURE__ */ new Map();
|
|
277
|
+
const validatedSymbols = /* @__PURE__ */ new Set();
|
|
278
|
+
try {
|
|
279
|
+
for (const interval of intervals) {
|
|
280
|
+
const offset = (0, import_data.intervalToMs)(interval) - baseMs;
|
|
281
|
+
const nativeRun = {
|
|
282
|
+
...context.preparedRun,
|
|
283
|
+
preloadStart: context.preparedRun.preloadStart - offset,
|
|
284
|
+
window: {
|
|
285
|
+
...context.preparedRun.window,
|
|
286
|
+
start: context.preparedRun.window.start - offset,
|
|
287
|
+
end: context.preparedRun.window.end - offset
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
const part = await prepareHistoricalReplay(
|
|
291
|
+
{
|
|
292
|
+
...context,
|
|
293
|
+
interval,
|
|
294
|
+
preparedRun: nativeRun,
|
|
295
|
+
strategies: context.strategies.filter(
|
|
296
|
+
(strategy) => strategy.strategyConfig.INTERVAL === interval
|
|
297
|
+
),
|
|
298
|
+
references: interval === context.interval ? context.references : await loadReferences(interval, nativeRun)
|
|
299
|
+
},
|
|
300
|
+
adapters
|
|
301
|
+
);
|
|
302
|
+
plan.sharedReplayKeyPrefixes.push(...part.sharedReplayKeyPrefixes);
|
|
303
|
+
plan.runtimeLineages.push(...part.runtimeLineages);
|
|
304
|
+
for (const runtime of part.symbolRuntimes) {
|
|
305
|
+
if (interval === context.interval) {
|
|
306
|
+
baseRuntimes.set(runtime.symbol, runtime);
|
|
307
|
+
} else if (runtime.strategies.length && !validatedSymbols.has(runtime.symbol)) {
|
|
308
|
+
assertBaseReplayCoverage(
|
|
309
|
+
baseRuntimes.get(runtime.symbol),
|
|
310
|
+
runtime.symbol,
|
|
311
|
+
context.preparedRun.window,
|
|
312
|
+
baseMs
|
|
313
|
+
);
|
|
314
|
+
validatedSymbols.add(runtime.symbol);
|
|
315
|
+
}
|
|
316
|
+
runtime.timestampOffsetMs = offset;
|
|
317
|
+
runtime.advanceMarket = interval === context.interval;
|
|
318
|
+
plan.symbolRuntimes.push(runtime);
|
|
319
|
+
}
|
|
320
|
+
for (const timestamp of part.orderedTimestamps) {
|
|
321
|
+
const scheduled = timestamp + offset;
|
|
322
|
+
if (scheduled >= context.preparedRun.window.start && scheduled <= context.preparedRun.window.end)
|
|
323
|
+
timestamps.add(scheduled);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
plan.orderedTimestamps = [...timestamps].sort((a, b) => a - b);
|
|
327
|
+
return plan;
|
|
328
|
+
} catch (error) {
|
|
329
|
+
releaseHistoricalReplayCaches(plan, adapters);
|
|
330
|
+
throw error;
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
var assertBaseReplayCoverage = (runtime, symbol, window, baseMs) => {
|
|
334
|
+
let index = 0;
|
|
335
|
+
for (let timestamp = Math.ceil(window.start / baseMs) * baseMs; timestamp <= window.end; timestamp += baseMs) {
|
|
336
|
+
if (runtime?.replayData[index]?.timestamp !== timestamp) {
|
|
337
|
+
throw new Error(`Missing base replay candle: ${symbol} at ${timestamp}`);
|
|
338
|
+
}
|
|
339
|
+
index += 1;
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
var prepareHistoricalReplay = async (context, adapters) => {
|
|
343
|
+
const {
|
|
344
|
+
userName,
|
|
345
|
+
projectRoot,
|
|
346
|
+
preparedRun,
|
|
347
|
+
interval,
|
|
348
|
+
connectorName,
|
|
349
|
+
replayConnector,
|
|
350
|
+
strategies,
|
|
351
|
+
references
|
|
352
|
+
} = context;
|
|
353
|
+
const symbolRuntimes = [];
|
|
354
|
+
const orderedTimestamps = /* @__PURE__ */ new Set();
|
|
355
|
+
const sharedReplayKeyPrefixes = [];
|
|
356
|
+
const runtimeLineages = [];
|
|
357
|
+
let skippedSymbols = 0;
|
|
358
|
+
try {
|
|
359
|
+
for (const symbol of preparedRun.tickers) {
|
|
360
|
+
const data = await preparedRun.marketConnector.kline({
|
|
361
|
+
symbol,
|
|
362
|
+
start: preparedRun.preloadStart,
|
|
363
|
+
end: preparedRun.window.end,
|
|
364
|
+
cacheOnly: true,
|
|
365
|
+
interval
|
|
366
|
+
});
|
|
367
|
+
if (symbolRuntimes.length % 50 === 0)
|
|
368
|
+
adapters.sampleMemory?.("history:loaded", {
|
|
369
|
+
symbols: symbolRuntimes.length + 1,
|
|
370
|
+
candles: data.length
|
|
371
|
+
});
|
|
372
|
+
const preparedData = buildPreparedData({
|
|
373
|
+
data,
|
|
374
|
+
btcData: references.btcMarketData,
|
|
375
|
+
ethData: references.ethMarketData,
|
|
376
|
+
btcBinanceData: references.btcBinanceData,
|
|
377
|
+
btcCoinbaseData: references.btcCoinbaseData,
|
|
378
|
+
start: preparedRun.window.start,
|
|
379
|
+
preloadStart: preparedRun.preloadStart
|
|
380
|
+
});
|
|
381
|
+
if (!preparedData.replayData.length || !preparedData.btcReplayData.length) {
|
|
382
|
+
skippedSymbols += 1;
|
|
383
|
+
adapters.progress.tick({
|
|
384
|
+
skipped: adapters.display.skipped(skippedSymbols),
|
|
385
|
+
symbol: adapters.display.symbol(symbol)
|
|
386
|
+
});
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const sharedIndicatorsReplayKey = buildSymbolSharedReplayKey({
|
|
390
|
+
userName,
|
|
391
|
+
connectorName,
|
|
392
|
+
interval,
|
|
393
|
+
symbol,
|
|
394
|
+
start: preparedRun.window.start,
|
|
395
|
+
end: preparedRun.window.end
|
|
396
|
+
});
|
|
397
|
+
sharedReplayKeyPrefixes.push(sharedIndicatorsReplayKey);
|
|
398
|
+
const initializedStrategies = await Promise.allSettled(
|
|
399
|
+
strategies.filter((strategy) => strategyIncludesSymbol(strategy, symbol)).map(async (strategy) => {
|
|
400
|
+
const replayStrategyConfig = buildReplayStrategyConfig({
|
|
401
|
+
strategyConfig: strategy.strategyConfig,
|
|
402
|
+
interval
|
|
403
|
+
});
|
|
404
|
+
const runtimeLineage = await adapters.buildLineage({
|
|
405
|
+
projectRoot,
|
|
406
|
+
strategyName: strategy.strategyName,
|
|
407
|
+
strategyRevision: strategy.strategyRevision,
|
|
408
|
+
deploymentCompositionId: strategy.deploymentCompositionId,
|
|
409
|
+
strategyPackageVersion: strategy.strategyPackageVersion,
|
|
410
|
+
strategyDependencyVersions: strategy.strategyDependencyVersions,
|
|
411
|
+
runtimePackageVersion: strategy.runtimePackageVersion,
|
|
412
|
+
config: {
|
|
413
|
+
configId: strategy.strategyRevision,
|
|
414
|
+
strategyConfig: strategy.strategyConfig,
|
|
415
|
+
symbolResultConfig: strategy.strategyResults?.[symbol]?.config ?? null
|
|
416
|
+
},
|
|
417
|
+
runContext: {
|
|
418
|
+
connectorName: connectorName.toLowerCase(),
|
|
419
|
+
interval: String(interval),
|
|
420
|
+
universe: preparedRun.universe ?? null
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
runtimeLineages.push({
|
|
424
|
+
strategy: strategy.strategyName,
|
|
425
|
+
symbol,
|
|
426
|
+
...preparedRun.deploymentId ? { deploymentId: preparedRun.deploymentId } : {},
|
|
427
|
+
...preparedRun.accountId ? { accountId: preparedRun.accountId } : {},
|
|
428
|
+
lineage: runtimeLineage
|
|
429
|
+
});
|
|
430
|
+
return {
|
|
431
|
+
strategyName: strategy.strategyName,
|
|
432
|
+
strategyConfig: strategy.strategyConfig,
|
|
433
|
+
runtimeLineage,
|
|
434
|
+
run: await strategy.strategyCreator({
|
|
435
|
+
userName,
|
|
436
|
+
connectorName,
|
|
437
|
+
runtimeConfigId: strategy.strategyRevision,
|
|
438
|
+
strategyRevision: strategy.strategyRevision,
|
|
439
|
+
runtimeLineage,
|
|
440
|
+
universe: preparedRun.universe,
|
|
441
|
+
accountId: preparedRun.accountId,
|
|
442
|
+
deploymentId: preparedRun.deploymentId,
|
|
443
|
+
runtimeConfigSnapshot: {
|
|
444
|
+
userConfig: replayStrategyConfig
|
|
445
|
+
},
|
|
446
|
+
config: replayStrategyConfig,
|
|
447
|
+
symbol,
|
|
448
|
+
data: preparedData.prevData,
|
|
449
|
+
btcData: preparedData.btcPrevData,
|
|
450
|
+
ethData: preparedData.ethPrevData,
|
|
451
|
+
btcBinanceData: preparedData.btcBinancePrevData,
|
|
452
|
+
btcCoinbaseData: preparedData.btcCoinbasePrevData,
|
|
453
|
+
connector: replayConnector,
|
|
454
|
+
sharedIndicatorsReplayKey
|
|
455
|
+
})
|
|
456
|
+
};
|
|
457
|
+
})
|
|
458
|
+
);
|
|
459
|
+
const strategiesForSymbol = initializedStrategies.map((result) => {
|
|
460
|
+
if (result.status === "rejected") throw result.reason;
|
|
461
|
+
return result.value;
|
|
462
|
+
});
|
|
463
|
+
const symbolRuntime = {
|
|
464
|
+
symbol,
|
|
465
|
+
replayData: preparedData.replayData,
|
|
466
|
+
btcReplayData: preparedData.btcReplayData,
|
|
467
|
+
ethReplayData: preparedData.ethReplayData,
|
|
468
|
+
currentIndex: 0,
|
|
469
|
+
strategies: strategiesForSymbol
|
|
470
|
+
};
|
|
471
|
+
symbolRuntimes.push(symbolRuntime);
|
|
472
|
+
if (symbolRuntimes.length % 50 === 0 || symbol === preparedRun.tickers.at(-1))
|
|
473
|
+
adapters.sampleMemory?.("strategies:initializing", {
|
|
474
|
+
symbols: symbolRuntimes.length,
|
|
475
|
+
strategiesPerSymbol: strategiesForSymbol.length
|
|
476
|
+
});
|
|
477
|
+
adapters.progress.tick({
|
|
478
|
+
skipped: adapters.display.skipped(skippedSymbols),
|
|
479
|
+
symbol: adapters.display.symbol(symbol)
|
|
480
|
+
});
|
|
481
|
+
for (const candle of preparedData.replayData) {
|
|
482
|
+
orderedTimestamps.add(candle.timestamp);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
symbolRuntimes,
|
|
487
|
+
orderedTimestamps: [...orderedTimestamps].sort(
|
|
488
|
+
(left, right) => left - right
|
|
489
|
+
),
|
|
490
|
+
sharedReplayKeyPrefixes,
|
|
491
|
+
runtimeLineages
|
|
492
|
+
};
|
|
493
|
+
} catch (error) {
|
|
494
|
+
releaseHistoricalReplayCaches({ sharedReplayKeyPrefixes }, adapters);
|
|
495
|
+
throw error;
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
var releaseHistoricalReplayPlan = (plan) => {
|
|
499
|
+
plan.symbolRuntimes.length = 0;
|
|
500
|
+
plan.sharedReplayKeyPrefixes.length = 0;
|
|
501
|
+
};
|
|
502
|
+
var releaseHistoricalReplayCaches = (plan, adapters) => {
|
|
503
|
+
for (const keyPrefix of plan.sharedReplayKeyPrefixes) {
|
|
504
|
+
adapters.releaseIndicatorsCache(keyPrefix);
|
|
505
|
+
adapters.releaseReplayCache(keyPrefix);
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
|
|
38
509
|
// src/scripts/replayRunner.ts
|
|
39
510
|
var import_chalk13 = __toESM(require("chalk"));
|
|
40
511
|
var import_constants7 = require("@tradejs/core/constants");
|
|
41
|
-
var
|
|
512
|
+
var import_data5 = require("@tradejs/core/data");
|
|
42
513
|
var import_time7 = require("@tradejs/core/time");
|
|
43
514
|
var import_redis6 = require("@tradejs/infra/redis");
|
|
44
515
|
var import_candles = require("@tradejs/infra/timescale/candles");
|
|
@@ -71,7 +542,7 @@ var timeOperation = async (label, operation, log = console.log) => {
|
|
|
71
542
|
};
|
|
72
543
|
|
|
73
544
|
// src/lib/runEnvironment.ts
|
|
74
|
-
var
|
|
545
|
+
var import_chalk4 = __toESM(require("chalk"));
|
|
75
546
|
var import_connectors3 = require("@tradejs/node/connectors");
|
|
76
547
|
var import_cli3 = require("@tradejs/node/cli");
|
|
77
548
|
var import_constants = require("@tradejs/core/constants");
|
|
@@ -79,7 +550,7 @@ var import_time2 = require("@tradejs/core/time");
|
|
|
79
550
|
var import_runtimeStrategies = require("@tradejs/node/runtimeStrategies");
|
|
80
551
|
|
|
81
552
|
// src/lib/runtimeStrategyBacktest.ts
|
|
82
|
-
var
|
|
553
|
+
var import_chalk2 = __toESM(require("chalk"));
|
|
83
554
|
|
|
84
555
|
// src/lib/runtimeRedis.ts
|
|
85
556
|
var import_redis = require("@tradejs/infra/redis");
|
|
@@ -229,7 +700,7 @@ var resolveTimeWindow = ({
|
|
|
229
700
|
};
|
|
230
701
|
|
|
231
702
|
// src/lib/marketData/historyPrepare.ts
|
|
232
|
-
var
|
|
703
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
233
704
|
var import_connectors = require("@tradejs/connectors");
|
|
234
705
|
var import_cli = require("@tradejs/node/cli");
|
|
235
706
|
var import_connectors2 = require("@tradejs/node/connectors");
|
|
@@ -320,7 +791,7 @@ var updatePrimaryMarketHistory = async ({
|
|
|
320
791
|
const timeOperation3 = (label, operation) => timeOperation(
|
|
321
792
|
label,
|
|
322
793
|
operation,
|
|
323
|
-
log ?? ((message) => console.log(
|
|
794
|
+
log ?? ((message) => console.log(import_chalk3.default.gray(message)))
|
|
324
795
|
);
|
|
325
796
|
const updateOptions = preloadStart != null || preloadEnd != null ? {
|
|
326
797
|
connectorLabel: connectorName,
|
|
@@ -352,7 +823,7 @@ var updateBtcReferenceHistory = async ({
|
|
|
352
823
|
const timeOperation3 = (label, operation) => timeOperation(
|
|
353
824
|
label,
|
|
354
825
|
operation,
|
|
355
|
-
log ?? ((message) => console.log(
|
|
826
|
+
log ?? ((message) => console.log(import_chalk3.default.gray(message)))
|
|
356
827
|
);
|
|
357
828
|
const updateReference = async ({
|
|
358
829
|
connector,
|
|
@@ -474,7 +945,7 @@ var loadRunTickers = async ({
|
|
|
474
945
|
var timeOperation2 = async (label, operation) => timeOperation(
|
|
475
946
|
label,
|
|
476
947
|
operation,
|
|
477
|
-
(message) => console.log(
|
|
948
|
+
(message) => console.log(import_chalk4.default.gray(message))
|
|
478
949
|
);
|
|
479
950
|
var resolveBacktestExecutionPreloadInterval = (interval) => {
|
|
480
951
|
const normalized = String(interval);
|
|
@@ -495,7 +966,7 @@ var resolveRunConnectorName = async ({
|
|
|
495
966
|
return connectorName;
|
|
496
967
|
}
|
|
497
968
|
console.log(
|
|
498
|
-
|
|
969
|
+
import_chalk4.default.yellow(
|
|
499
970
|
`Unknown connector "${String(value || "").trim() || String(value)}". Fallback to ${import_connectors3.DEFAULT_CONNECTOR_NAME}.`
|
|
500
971
|
)
|
|
501
972
|
);
|
|
@@ -550,7 +1021,7 @@ var loadDeploymentReplayStrategies = async ({
|
|
|
550
1021
|
) : [];
|
|
551
1022
|
if (!strategies.length) {
|
|
552
1023
|
console.log(
|
|
553
|
-
|
|
1024
|
+
import_chalk4.default.yellow(
|
|
554
1025
|
`No enabled strategies found in runtime deployment ${deploymentId}`
|
|
555
1026
|
)
|
|
556
1027
|
);
|
|
@@ -616,7 +1087,7 @@ var prepareRunEnvironment = async ({
|
|
|
616
1087
|
);
|
|
617
1088
|
}
|
|
618
1089
|
if (showTickersList) {
|
|
619
|
-
console.log(
|
|
1090
|
+
console.log(import_chalk4.default.gray(JSON.stringify(loadedTickers.sort(), null, 2)));
|
|
620
1091
|
return null;
|
|
621
1092
|
}
|
|
622
1093
|
const instruments = !cacheOnly && typeof marketConnector.listInstruments === "function" ? await timeOperation2(
|
|
@@ -653,7 +1124,7 @@ var prepareRunEnvironment = async ({
|
|
|
653
1124
|
projectRoot,
|
|
654
1125
|
shouldUseDedicatedReferences: universe === "crypto",
|
|
655
1126
|
requireDedicatedReferences: universe === "crypto",
|
|
656
|
-
warn: (message) => console.log(
|
|
1127
|
+
warn: (message) => console.log(import_chalk4.default.yellow(message))
|
|
657
1128
|
});
|
|
658
1129
|
await updateMarketHistoryWithBtcReferences({
|
|
659
1130
|
marketConnector,
|
|
@@ -664,7 +1135,7 @@ var prepareRunEnvironment = async ({
|
|
|
664
1135
|
preloadStart,
|
|
665
1136
|
preloadEnd: window.end,
|
|
666
1137
|
universe,
|
|
667
|
-
log: (message) => console.log(
|
|
1138
|
+
log: (message) => console.log(import_chalk4.default.gray(message))
|
|
668
1139
|
});
|
|
669
1140
|
const backtestExecutionInterval = resolveBacktestExecutionPreloadInterval(interval);
|
|
670
1141
|
if (import_constants.BACKTEST_LOWER_TIMEFRAME_EXECUTION_ENABLED && backtestExecutionInterval && String(backtestExecutionInterval) !== String(interval)) {
|
|
@@ -701,13 +1172,13 @@ var prepareRunEnvironment = async ({
|
|
|
701
1172
|
};
|
|
702
1173
|
|
|
703
1174
|
// src/lib/marketContextPrepare.ts
|
|
704
|
-
var
|
|
1175
|
+
var import_chalk8 = __toESM(require("chalk"));
|
|
705
1176
|
var import_marketContext3 = require("@tradejs/infra/timescale/marketContext");
|
|
706
1177
|
var import_registry = require("@tradejs/node/registry");
|
|
707
1178
|
|
|
708
1179
|
// src/lib/binanceMarketContextBackfill.ts
|
|
709
1180
|
var import_progress = __toESM(require("progress"));
|
|
710
|
-
var
|
|
1181
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
711
1182
|
var import_connectors4 = require("@tradejs/connectors");
|
|
712
1183
|
var import_connectors5 = require("@tradejs/node/connectors");
|
|
713
1184
|
var import_strategies = require("@tradejs/node/strategies");
|
|
@@ -1261,7 +1732,7 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
1261
1732
|
let tradeFlowRows = 0;
|
|
1262
1733
|
let breadthRows = 0;
|
|
1263
1734
|
console.log(
|
|
1264
|
-
|
|
1735
|
+
import_chalk5.default.cyan(
|
|
1265
1736
|
`binance market context backfill: requestedSymbols=${symbols.length}, referenceSymbols=${referenceSymbols.length}, interval=${interval}, tradeFlowWindow=${new Date(tradeFlowStartMs).toISOString()}..${new Date(endMs).toISOString()}, breadthWindow=${new Date(breadthStartMs).toISOString()}..${new Date(endMs).toISOString()}`
|
|
1266
1737
|
)
|
|
1267
1738
|
);
|
|
@@ -1397,7 +1868,7 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
1397
1868
|
}) : [];
|
|
1398
1869
|
if (!missingChunks2.length) {
|
|
1399
1870
|
console.log(
|
|
1400
|
-
|
|
1871
|
+
import_chalk5.default.gray(`breadth cached: universe=${definition.universe}`)
|
|
1401
1872
|
);
|
|
1402
1873
|
}
|
|
1403
1874
|
return {
|
|
@@ -1485,7 +1956,7 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
1485
1956
|
}
|
|
1486
1957
|
}
|
|
1487
1958
|
console.log(
|
|
1488
|
-
|
|
1959
|
+
import_chalk5.default.green(
|
|
1489
1960
|
`binance market context backfill done: tradeFlowRows=${tradeFlowRows}, depthRows=0, breadthRows=${breadthRows}, skippedSymbols=${skippedSymbols}`
|
|
1490
1961
|
)
|
|
1491
1962
|
);
|
|
@@ -1502,11 +1973,11 @@ var backfillBinanceMarketContextForSignals = (params) => backfillBinanceMarketCo
|
|
|
1502
1973
|
var backfillBinanceMarketContextForReplay = (params) => backfillBinanceMarketContext(params, true);
|
|
1503
1974
|
|
|
1504
1975
|
// src/lib/derivativesContextBackfill.ts
|
|
1505
|
-
var
|
|
1976
|
+
var import_chalk6 = __toESM(require("chalk"));
|
|
1506
1977
|
var import_progress2 = __toESM(require("progress"));
|
|
1507
1978
|
var import_async = require("@tradejs/core/async");
|
|
1508
1979
|
var import_constants2 = require("@tradejs/core/constants");
|
|
1509
|
-
var
|
|
1980
|
+
var import_indicators2 = require("@tradejs/core/indicators");
|
|
1510
1981
|
var import_derivatives = require("@tradejs/infra/timescale/derivatives");
|
|
1511
1982
|
var import_client2 = require("@tradejs/infra/timescale/client");
|
|
1512
1983
|
var import_userSettings = require("@tradejs/infra/userSettings");
|
|
@@ -1609,7 +2080,7 @@ var resolveDerivativesContextBackfillWindow = (params) => {
|
|
|
1609
2080
|
};
|
|
1610
2081
|
var resolveDerivativesContextIntervalBackfillWindow = (params) => {
|
|
1611
2082
|
const intervalMs = derivativesIntervalMs(params.interval);
|
|
1612
|
-
const alignedToMs = params.closedOnly ? (0,
|
|
2083
|
+
const alignedToMs = params.closedOnly ? (0, import_indicators2.getLastClosedDerivativesBarStartMs)(params.toMs, params.interval) : Math.floor(params.toMs / intervalMs) * intervalMs;
|
|
1613
2084
|
return {
|
|
1614
2085
|
fromMs: Math.floor(params.fromMs / intervalMs) * intervalMs,
|
|
1615
2086
|
toMs: alignedToMs,
|
|
@@ -1646,7 +2117,7 @@ var resolveDerivativesContextRequiredFetchFromMs = (params) => {
|
|
|
1646
2117
|
refreshClosedTail: params.mode === "signals"
|
|
1647
2118
|
});
|
|
1648
2119
|
const dataFetchFromMs = dataCoverageFromMs != null && edgesFromMs != null ? Math.max(dataCoverageFromMs, edgesFromMs) : null;
|
|
1649
|
-
const confirmedLiquidationWindow = (0,
|
|
2120
|
+
const confirmedLiquidationWindow = (0, import_indicators2.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
1650
2121
|
interval: params.interval,
|
|
1651
2122
|
fromMs: params.fromMs,
|
|
1652
2123
|
toMs: params.toMs,
|
|
@@ -2037,7 +2508,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2037
2508
|
const coverageNowMs = Date.now();
|
|
2038
2509
|
await Promise.all(
|
|
2039
2510
|
intervalWindows.map(async (window) => {
|
|
2040
|
-
const confirmedLiquidationWindow = (0,
|
|
2511
|
+
const confirmedLiquidationWindow = (0, import_indicators2.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
2041
2512
|
interval: window.interval,
|
|
2042
2513
|
fromMs: window.fromMs,
|
|
2043
2514
|
toMs: window.toMs,
|
|
@@ -2133,7 +2604,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2133
2604
|
toMs: backfillWindow.toMs
|
|
2134
2605
|
})
|
|
2135
2606
|
);
|
|
2136
|
-
const confirmedLiquidationWindow = (0,
|
|
2607
|
+
const confirmedLiquidationWindow = (0, import_indicators2.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
2137
2608
|
interval: window.interval,
|
|
2138
2609
|
fromMs: backfillWindow.fromMs,
|
|
2139
2610
|
toMs: backfillWindow.toMs,
|
|
@@ -2151,7 +2622,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2151
2622
|
});
|
|
2152
2623
|
if (allBackfillWindowsCached) {
|
|
2153
2624
|
console.log(
|
|
2154
|
-
|
|
2625
|
+
import_chalk6.default.gray(
|
|
2155
2626
|
`derivatives context backfill: cached symbols=${symbols.length}, requestedSymbols=${requestedSymbols.length}, intervals=${intervals.join(",")}, window=${new Date(fromMs).toISOString()}..${new Date(safeEndMs).toISOString()}`
|
|
2156
2627
|
)
|
|
2157
2628
|
);
|
|
@@ -2216,7 +2687,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2216
2687
|
let failedWindows = 0;
|
|
2217
2688
|
let skippedWindows = 0;
|
|
2218
2689
|
console.log(
|
|
2219
|
-
|
|
2690
|
+
import_chalk6.default.cyan(
|
|
2220
2691
|
`derivatives context backfill: symbols=${matches.length}, requestedSymbols=${requestedSymbols.length}, unmatched=${unmatched.length}, intervals=${intervals.join(",")}, window=${new Date(fromMs).toISOString()}..${new Date(safeEndMs).toISOString()}, testStart=${new Date(safeStartMs).toISOString()}`
|
|
2221
2692
|
)
|
|
2222
2693
|
);
|
|
@@ -2321,13 +2792,13 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2321
2792
|
});
|
|
2322
2793
|
rows = missingBatch.flatMap((item) => {
|
|
2323
2794
|
const marketSymbol = item.marketSymbol.toUpperCase();
|
|
2324
|
-
const points = (0,
|
|
2795
|
+
const points = (0, import_indicators2.mergeCoinalyzeMetrics)({
|
|
2325
2796
|
symbol: item.symbol,
|
|
2326
2797
|
oiRaw: oiMap.get(marketSymbol) ?? [],
|
|
2327
2798
|
fundingRaw: fundingMap.get(marketSymbol) ?? [],
|
|
2328
2799
|
liqRaw: liquidationRowsByMarket.get(marketSymbol) ?? []
|
|
2329
2800
|
});
|
|
2330
|
-
return (0,
|
|
2801
|
+
return (0, import_indicators2.coinalyzePointsToRows)(points, interval, "coinalyze");
|
|
2331
2802
|
});
|
|
2332
2803
|
missingClosedSymbols = mode === "signals" ? getMissingClosedDerivativesSymbols({
|
|
2333
2804
|
symbols: missingBatch.map((item) => item.symbol),
|
|
@@ -2348,7 +2819,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2348
2819
|
await (0, import_derivatives.upsertDerivatives)(rows);
|
|
2349
2820
|
totalRows += rows.length;
|
|
2350
2821
|
}
|
|
2351
|
-
const confirmedLiquidationWindow = (0,
|
|
2822
|
+
const confirmedLiquidationWindow = (0, import_indicators2.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
2352
2823
|
interval,
|
|
2353
2824
|
fromMs: group.fromMs,
|
|
2354
2825
|
toMs,
|
|
@@ -2358,7 +2829,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2358
2829
|
const metricCoverageRows = missingBatch.map((item) => {
|
|
2359
2830
|
const marketSymbol = item.marketSymbol.toUpperCase();
|
|
2360
2831
|
const eventRowsCount = (liquidationRowsByMarket.get(marketSymbol) ?? []).filter((point) => {
|
|
2361
|
-
const timestamp = (0,
|
|
2832
|
+
const timestamp = (0, import_indicators2.toCoinalyzeTimestampMs)(
|
|
2362
2833
|
point.t ?? point.ts ?? point.time ?? point.timestamp
|
|
2363
2834
|
);
|
|
2364
2835
|
return timestamp != null && timestamp >= confirmedLiquidationWindow.fromMs && timestamp <= confirmedLiquidationWindow.toMs;
|
|
@@ -2430,13 +2901,13 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2430
2901
|
} catch (error) {
|
|
2431
2902
|
failedWindows += 1;
|
|
2432
2903
|
console.error(
|
|
2433
|
-
|
|
2904
|
+
import_chalk6.default.red(
|
|
2434
2905
|
`derivatives context backfill window failed batch=${batchIdx + 1}/${symbolBatches.length} interval=${interval} ${new Date(cursor).toISOString()}..${new Date(toMs).toISOString()}: ${error}`
|
|
2435
2906
|
)
|
|
2436
2907
|
);
|
|
2437
2908
|
} finally {
|
|
2438
2909
|
bar.tick(1, {
|
|
2439
|
-
batch:
|
|
2910
|
+
batch: import_chalk6.default.gray(
|
|
2440
2911
|
`${batchIdx + 1}/${symbolBatches.length} ${interval}`
|
|
2441
2912
|
),
|
|
2442
2913
|
rows: totalRows,
|
|
@@ -2450,7 +2921,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2450
2921
|
}
|
|
2451
2922
|
console.log("");
|
|
2452
2923
|
console.log(
|
|
2453
|
-
|
|
2924
|
+
import_chalk6.default.green(
|
|
2454
2925
|
`derivatives context backfill done: rows=${totalRows}, failed_windows=${failedWindows}, skipped_windows=${skippedWindows}`
|
|
2455
2926
|
)
|
|
2456
2927
|
);
|
|
@@ -2480,7 +2951,7 @@ var backfillDerivativesContextForSignals = async (params) => backfillDerivatives
|
|
|
2480
2951
|
);
|
|
2481
2952
|
|
|
2482
2953
|
// src/lib/coinMarketCapContextBackfill.ts
|
|
2483
|
-
var
|
|
2954
|
+
var import_chalk7 = __toESM(require("chalk"));
|
|
2484
2955
|
var import_async2 = require("@tradejs/core/async");
|
|
2485
2956
|
var import_marketContext2 = require("@tradejs/infra/timescale/marketContext");
|
|
2486
2957
|
var import_client3 = require("@tradejs/infra/timescale/client");
|
|
@@ -2742,7 +3213,7 @@ var formatRowTimeRange = (rows) => {
|
|
|
2742
3213
|
};
|
|
2743
3214
|
var logBackfillSourceDone = (params) => {
|
|
2744
3215
|
console.log(
|
|
2745
|
-
|
|
3216
|
+
import_chalk7.default.gray(
|
|
2746
3217
|
`coinmarketcap context source done: ${params.name}=${params.status} rows=${params.rows}${params.details ? ` ${params.details}` : ""}`
|
|
2747
3218
|
)
|
|
2748
3219
|
);
|
|
@@ -2776,7 +3247,7 @@ var coinMarketCapFetch = async (params) => {
|
|
|
2776
3247
|
recordCoinMarketCapFetchStats(params.stats, params.path, creditCount);
|
|
2777
3248
|
if (creditCount != null && isVerboseRequestLoggingEnabled()) {
|
|
2778
3249
|
console.log(
|
|
2779
|
-
|
|
3250
|
+
import_chalk7.default.gray(`coinmarketcap ${params.path}: credits=${creditCount}`)
|
|
2780
3251
|
);
|
|
2781
3252
|
}
|
|
2782
3253
|
return payload;
|
|
@@ -2821,7 +3292,7 @@ var fetchCoinMarketCapIndexRows = async (params) => {
|
|
|
2821
3292
|
);
|
|
2822
3293
|
if (chunk === 1 || chunk === totalChunks || chunk % 25 === 0) {
|
|
2823
3294
|
console.log(
|
|
2824
|
-
|
|
3295
|
+
import_chalk7.default.gray(
|
|
2825
3296
|
`coinmarketcap index backfill progress: ${params.index.slug} chunk=${chunk}/${totalChunks} rows=${rows.length} chunkRange=${new Date(cursorMs).toISOString()}..${new Date(chunkEndMs).toISOString()}`
|
|
2826
3297
|
)
|
|
2827
3298
|
);
|
|
@@ -3260,7 +3731,7 @@ var backfillCoinMarketCapContext = async (params) => {
|
|
|
3260
3731
|
covered: indexesBackfillCovered
|
|
3261
3732
|
});
|
|
3262
3733
|
console.log(
|
|
3263
|
-
|
|
3734
|
+
import_chalk7.default.gray(
|
|
3264
3735
|
[
|
|
3265
3736
|
"coinmarketcap context status:",
|
|
3266
3737
|
formatSourceStatus({
|
|
@@ -3295,7 +3766,7 @@ var backfillCoinMarketCapContext = async (params) => {
|
|
|
3295
3766
|
);
|
|
3296
3767
|
if (globalDailyReady && referencesDailyReady && exchangeLiquidityReady && fearGreedReady && indexesReady) {
|
|
3297
3768
|
console.log(
|
|
3298
|
-
|
|
3769
|
+
import_chalk7.default.gray(
|
|
3299
3770
|
`coinmarketcap context cached: ${new Date(fromMs).toISOString()}..${new Date(toMs).toISOString()}`
|
|
3300
3771
|
)
|
|
3301
3772
|
);
|
|
@@ -3322,7 +3793,7 @@ var backfillCoinMarketCapContext = async (params) => {
|
|
|
3322
3793
|
!indexesReady ? "indexes" : null
|
|
3323
3794
|
].filter((source) => source != null);
|
|
3324
3795
|
console.log(
|
|
3325
|
-
|
|
3796
|
+
import_chalk7.default.cyan(
|
|
3326
3797
|
`coinmarketcap context backfill: window=${new Date(fromMs).toISOString()}..${new Date(toMs).toISOString()} days=${Math.floor((toMs - fromMs) / DAY_MS3) + 1} fetch=${pendingSources.join(",")}`
|
|
3327
3798
|
)
|
|
3328
3799
|
);
|
|
@@ -3514,7 +3985,7 @@ var backfillCoinMarketCapContext = async (params) => {
|
|
|
3514
3985
|
});
|
|
3515
3986
|
}
|
|
3516
3987
|
console.log(
|
|
3517
|
-
|
|
3988
|
+
import_chalk7.default.green(
|
|
3518
3989
|
[
|
|
3519
3990
|
"coinmarketcap context backfill done:",
|
|
3520
3991
|
`global=${globalStatus} rows=${globalRows}`,
|
|
@@ -4126,7 +4597,7 @@ var buildDerivativesBackfillParams = (params) => ({
|
|
|
4126
4597
|
});
|
|
4127
4598
|
var prepareMarketContextForRun = async (params) => {
|
|
4128
4599
|
const log = params.log ?? ((message) => {
|
|
4129
|
-
console.log(
|
|
4600
|
+
console.log(import_chalk8.default.gray(message));
|
|
4130
4601
|
});
|
|
4131
4602
|
const timeOperation3 = (label, operation) => timeOperation(label, operation, log);
|
|
4132
4603
|
const requirements = await resolveMarketContextRunRequirements(params);
|
|
@@ -4577,174 +5048,64 @@ var buildRuntimeEvidenceLineageScopes = ({
|
|
|
4577
5048
|
(left, right) => left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.firstTimestamp - right.firstTimestamp
|
|
4578
5049
|
);
|
|
4579
5050
|
};
|
|
4580
|
-
var loadReplayRuntimeEvidenceSource = async ({
|
|
4581
|
-
filePath,
|
|
4582
|
-
projectRoot,
|
|
4583
|
-
expectedUserName,
|
|
4584
|
-
expectedWindow
|
|
4585
|
-
}) => {
|
|
4586
|
-
const { resolvedPath, runtime, userName, startTime, endTime, deployment } = await readRuntimeEvidenceArtifact({ filePath, projectRoot });
|
|
4587
|
-
if (!userName || userName !== expectedUserName) {
|
|
4588
|
-
throw new Error(
|
|
4589
|
-
`Runtime evidence user mismatch: expected=${expectedUserName}, actual=${userName || "missing"}`
|
|
4590
|
-
);
|
|
4591
|
-
}
|
|
4592
|
-
const endTimeMatches = endTime === expectedWindow.end || endTime === expectedWindow.end + 1;
|
|
4593
|
-
if (startTime !== expectedWindow.start || !endTimeMatches) {
|
|
4594
|
-
throw new Error(
|
|
4595
|
-
`Runtime evidence window mismatch: expected=${expectedWindow.start}..${expectedWindow.end}, actual=${startTime ?? "missing"}..${endTime ?? "missing"}`
|
|
4596
|
-
);
|
|
4597
|
-
}
|
|
4598
|
-
const trades = unwrapRows(runtime.trades, "trade");
|
|
4599
|
-
const signals = unwrapRows(runtime.signals, "signal");
|
|
4600
|
-
const evaluations = unwrapRows(
|
|
4601
|
-
runtime.evaluations,
|
|
4602
|
-
"evaluation"
|
|
4603
|
-
);
|
|
4604
|
-
const storedLineageScopes = unwrapRows(
|
|
4605
|
-
runtime.lineageScopes,
|
|
4606
|
-
"lineageScope"
|
|
4607
|
-
);
|
|
4608
|
-
assertRuntimeRowsMatchDeployment({
|
|
4609
|
-
label: "trade",
|
|
4610
|
-
rows: trades,
|
|
4611
|
-
deployment
|
|
4612
|
-
});
|
|
4613
|
-
assertRuntimeRowsMatchDeployment({
|
|
4614
|
-
label: "signal",
|
|
4615
|
-
rows: signals,
|
|
4616
|
-
deployment
|
|
4617
|
-
});
|
|
4618
|
-
assertRuntimeRowsMatchDeployment({
|
|
4619
|
-
label: "evaluation",
|
|
4620
|
-
rows: evaluations,
|
|
4621
|
-
deployment
|
|
4622
|
-
});
|
|
4623
|
-
assertRuntimeRowsMatchDeployment({
|
|
4624
|
-
label: "lineage scope",
|
|
4625
|
-
rows: storedLineageScopes,
|
|
4626
|
-
deployment
|
|
4627
|
-
});
|
|
4628
|
-
return {
|
|
4629
|
-
path: resolvedPath,
|
|
4630
|
-
userName,
|
|
4631
|
-
startTime,
|
|
4632
|
-
endTime,
|
|
4633
|
-
trades,
|
|
4634
|
-
signals,
|
|
4635
|
-
evaluations,
|
|
4636
|
-
lineageScopes: storedLineageScopes.length ? storedLineageScopes : buildRuntimeEvidenceLineageScopes({ signals, evaluations }),
|
|
4637
|
-
deployment
|
|
4638
|
-
};
|
|
4639
|
-
};
|
|
4640
|
-
|
|
4641
|
-
// src/lib/replay/support.ts
|
|
4642
|
-
var import_chalk8 = __toESM(require("chalk"));
|
|
4643
|
-
|
|
4644
|
-
// src/lib/runtimeModeConfig.ts
|
|
4645
|
-
var buildRuntimeModeStrategyConfig = ({
|
|
4646
|
-
strategyConfig,
|
|
4647
|
-
env,
|
|
4648
|
-
interval,
|
|
4649
|
-
makeOrders,
|
|
4650
|
-
recordRuntimeTrades,
|
|
4651
|
-
aiReplayAnalyses
|
|
4652
|
-
}) => ({
|
|
4653
|
-
...strategyConfig,
|
|
4654
|
-
ENV: env,
|
|
4655
|
-
MAKE_ORDERS: makeOrders,
|
|
4656
|
-
INTERVAL: strategyConfig.INTERVAL ?? interval,
|
|
4657
|
-
...typeof recordRuntimeTrades === "boolean" ? { RECORD_RUNTIME_TRADES: recordRuntimeTrades } : {},
|
|
4658
|
-
...aiReplayAnalyses?.length ? { AI_REPLAY_ANALYSES: aiReplayAnalyses } : {}
|
|
4659
|
-
});
|
|
4660
|
-
|
|
4661
|
-
// src/lib/replay/support.ts
|
|
4662
|
-
var REPLAY_RESULTS_BY_STRATEGY_HEADERS = [
|
|
4663
|
-
import_chalk8.default.blue("STRATEGY"),
|
|
4664
|
-
import_chalk8.default.yellow("TICKERS"),
|
|
4665
|
-
import_chalk8.default.yellow("TRADE TICKERS"),
|
|
4666
|
-
import_chalk8.default.cyan("ORDERS"),
|
|
4667
|
-
import_chalk8.default.cyan("WIN/LOSS (%)"),
|
|
4668
|
-
import_chalk8.default.cyan("PROFIT"),
|
|
4669
|
-
import_chalk8.default.cyan("AVG TRADE")
|
|
4670
|
-
];
|
|
4671
|
-
var REPLAY_RUNTIME_COMPARISON_HEADERS = [
|
|
4672
|
-
import_chalk8.default.blue("STRATEGY"),
|
|
4673
|
-
import_chalk8.default.cyan("BT ENTRIES"),
|
|
4674
|
-
import_chalk8.default.cyan("BT PNL"),
|
|
4675
|
-
import_chalk8.default.yellow("RT TRADES"),
|
|
4676
|
-
import_chalk8.default.yellow("RT PNL"),
|
|
4677
|
-
import_chalk8.default.green("MATCHED"),
|
|
4678
|
-
import_chalk8.default.red("ORDER FAILED"),
|
|
4679
|
-
import_chalk8.default.yellow("RT ONLY"),
|
|
4680
|
-
import_chalk8.default.magenta("BT ONLY"),
|
|
4681
|
-
import_chalk8.default.red("UNMATCHED")
|
|
4682
|
-
];
|
|
4683
|
-
var REPLAY_RESULTS_CONFIG = "replay";
|
|
4684
|
-
var REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS = 1;
|
|
4685
|
-
var REPLAY_RUNTIME_COMPARE_TOLERANCE_MS = REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS * 15 * 60 * 1e3;
|
|
4686
|
-
var formatReplayRuntimeCompareTolerance = () => `${REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS} ${REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS === 1 ? "bar" : "bars"}`;
|
|
4687
|
-
var getReplayRuntimeUnmatchedCount = (row) => row.orderFailed + row.runtimeOnly + row.backtestOnly;
|
|
4688
|
-
var buildReplayStrategyConfig = ({
|
|
4689
|
-
strategyConfig,
|
|
4690
|
-
interval
|
|
4691
|
-
}) => ({
|
|
4692
|
-
...buildRuntimeModeStrategyConfig({
|
|
4693
|
-
strategyConfig,
|
|
4694
|
-
env: "PARITY",
|
|
4695
|
-
interval,
|
|
4696
|
-
makeOrders: false,
|
|
4697
|
-
recordRuntimeTrades: false
|
|
4698
|
-
}),
|
|
4699
|
-
SIMULATE_ORDERS: true
|
|
4700
|
-
});
|
|
4701
|
-
var toRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
4702
|
-
var compactReplayAdditionalIndicators = (additionalIndicators) => {
|
|
4703
|
-
const additional = toRecord(additionalIndicators);
|
|
4704
|
-
if (!additional) {
|
|
4705
|
-
return void 0;
|
|
4706
|
-
}
|
|
4707
|
-
const compact = {};
|
|
4708
|
-
if (additional.executionSlippage != null) {
|
|
4709
|
-
compact.executionSlippage = additional.executionSlippage;
|
|
4710
|
-
}
|
|
4711
|
-
if (additional.marketContext != null) {
|
|
4712
|
-
compact.marketContext = additional.marketContext;
|
|
4713
|
-
}
|
|
4714
|
-
const baseContext = toRecord(additional.baseContext);
|
|
4715
|
-
const relative = toRecord(baseContext?.relative);
|
|
4716
|
-
const execution = toRecord(relative?.execution);
|
|
4717
|
-
if (execution) {
|
|
4718
|
-
compact.baseContext = {
|
|
4719
|
-
relative: {
|
|
4720
|
-
execution
|
|
4721
|
-
}
|
|
4722
|
-
};
|
|
4723
|
-
}
|
|
4724
|
-
return Object.keys(compact).length ? compact : void 0;
|
|
4725
|
-
};
|
|
4726
|
-
var compactReplaySignal = (signal) => {
|
|
4727
|
-
if (!signal) {
|
|
4728
|
-
return void 0;
|
|
5051
|
+
var loadReplayRuntimeEvidenceSource = async ({
|
|
5052
|
+
filePath,
|
|
5053
|
+
projectRoot,
|
|
5054
|
+
expectedUserName,
|
|
5055
|
+
expectedWindow
|
|
5056
|
+
}) => {
|
|
5057
|
+
const { resolvedPath, runtime, userName, startTime, endTime, deployment } = await readRuntimeEvidenceArtifact({ filePath, projectRoot });
|
|
5058
|
+
if (!userName || userName !== expectedUserName) {
|
|
5059
|
+
throw new Error(
|
|
5060
|
+
`Runtime evidence user mismatch: expected=${expectedUserName}, actual=${userName || "missing"}`
|
|
5061
|
+
);
|
|
5062
|
+
}
|
|
5063
|
+
const endTimeMatches = endTime === expectedWindow.end || endTime === expectedWindow.end + 1;
|
|
5064
|
+
if (startTime !== expectedWindow.start || !endTimeMatches) {
|
|
5065
|
+
throw new Error(
|
|
5066
|
+
`Runtime evidence window mismatch: expected=${expectedWindow.start}..${expectedWindow.end}, actual=${startTime ?? "missing"}..${endTime ?? "missing"}`
|
|
5067
|
+
);
|
|
4729
5068
|
}
|
|
5069
|
+
const trades = unwrapRows(runtime.trades, "trade");
|
|
5070
|
+
const signals = unwrapRows(runtime.signals, "signal");
|
|
5071
|
+
const evaluations = unwrapRows(
|
|
5072
|
+
runtime.evaluations,
|
|
5073
|
+
"evaluation"
|
|
5074
|
+
);
|
|
5075
|
+
const storedLineageScopes = unwrapRows(
|
|
5076
|
+
runtime.lineageScopes,
|
|
5077
|
+
"lineageScope"
|
|
5078
|
+
);
|
|
5079
|
+
assertRuntimeRowsMatchDeployment({
|
|
5080
|
+
label: "trade",
|
|
5081
|
+
rows: trades,
|
|
5082
|
+
deployment
|
|
5083
|
+
});
|
|
5084
|
+
assertRuntimeRowsMatchDeployment({
|
|
5085
|
+
label: "signal",
|
|
5086
|
+
rows: signals,
|
|
5087
|
+
deployment
|
|
5088
|
+
});
|
|
5089
|
+
assertRuntimeRowsMatchDeployment({
|
|
5090
|
+
label: "evaluation",
|
|
5091
|
+
rows: evaluations,
|
|
5092
|
+
deployment
|
|
5093
|
+
});
|
|
5094
|
+
assertRuntimeRowsMatchDeployment({
|
|
5095
|
+
label: "lineage scope",
|
|
5096
|
+
rows: storedLineageScopes,
|
|
5097
|
+
deployment
|
|
5098
|
+
});
|
|
4730
5099
|
return {
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
additionalIndicators: compactReplayAdditionalIndicators(
|
|
4741
|
-
signal.additionalIndicators
|
|
4742
|
-
),
|
|
4743
|
-
isConfigFromBacktest: signal.isConfigFromBacktest,
|
|
4744
|
-
...signal.orderStatus ? { orderStatus: signal.orderStatus } : {},
|
|
4745
|
-
...signal.orderSkipReason ? { orderSkipReason: signal.orderSkipReason } : {},
|
|
4746
|
-
...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {},
|
|
4747
|
-
...signal.ml ? { ml: signal.ml } : {}
|
|
5100
|
+
path: resolvedPath,
|
|
5101
|
+
userName,
|
|
5102
|
+
startTime,
|
|
5103
|
+
endTime,
|
|
5104
|
+
trades,
|
|
5105
|
+
signals,
|
|
5106
|
+
evaluations,
|
|
5107
|
+
lineageScopes: storedLineageScopes.length ? storedLineageScopes : buildRuntimeEvidenceLineageScopes({ signals, evaluations }),
|
|
5108
|
+
deployment
|
|
4748
5109
|
};
|
|
4749
5110
|
};
|
|
4750
5111
|
|
|
@@ -4813,6 +5174,7 @@ var getPositionPnlSnapshot = ({
|
|
|
4813
5174
|
var createPortfolioReplayConnector = (connector) => {
|
|
4814
5175
|
let state = {};
|
|
4815
5176
|
let amount = import_constants3.INITIAL_BACKTEST_AMOUNT;
|
|
5177
|
+
let executionTimestamp = null;
|
|
4816
5178
|
const positionsBySymbol = /* @__PURE__ */ new Map();
|
|
4817
5179
|
const currentPriceBySymbol = /* @__PURE__ */ new Map();
|
|
4818
5180
|
const orderLog = [];
|
|
@@ -5074,6 +5436,7 @@ var createPortfolioReplayConnector = (connector) => {
|
|
|
5074
5436
|
});
|
|
5075
5437
|
const nextPosition = {
|
|
5076
5438
|
...order,
|
|
5439
|
+
timestamp: executionTimestamp ?? order.timestamp,
|
|
5077
5440
|
price: entryPrice,
|
|
5078
5441
|
signal: compactReplaySignal(order.signal),
|
|
5079
5442
|
amount,
|
|
@@ -5098,6 +5461,7 @@ var createPortfolioReplayConnector = (connector) => {
|
|
|
5098
5461
|
positionState,
|
|
5099
5462
|
data: {
|
|
5100
5463
|
...order,
|
|
5464
|
+
timestamp: executionTimestamp ?? order.timestamp,
|
|
5101
5465
|
price: entryPrice,
|
|
5102
5466
|
profit,
|
|
5103
5467
|
fee,
|
|
@@ -5161,6 +5525,7 @@ var createPortfolioReplayConnector = (connector) => {
|
|
|
5161
5525
|
positionState,
|
|
5162
5526
|
data: {
|
|
5163
5527
|
...order,
|
|
5528
|
+
timestamp: executionTimestamp ?? order.timestamp,
|
|
5164
5529
|
price: executionPrice,
|
|
5165
5530
|
qty: position.qty,
|
|
5166
5531
|
profit,
|
|
@@ -5169,11 +5534,12 @@ var createPortfolioReplayConnector = (connector) => {
|
|
|
5169
5534
|
...getExecutionSlippageLogData(slippageBreakdown, "exit")
|
|
5170
5535
|
}
|
|
5171
5536
|
});
|
|
5172
|
-
clearPosition(order.symbol, order.timestamp);
|
|
5537
|
+
clearPosition(order.symbol, executionTimestamp ?? order.timestamp);
|
|
5173
5538
|
return true;
|
|
5174
5539
|
},
|
|
5175
5540
|
getTickers: connector.getTickers,
|
|
5176
5541
|
advanceMarket: async ({ symbol, candle }) => {
|
|
5542
|
+
executionTimestamp = candle.timestamp;
|
|
5177
5543
|
currentPriceBySymbol.set(symbol, candle.close);
|
|
5178
5544
|
await checkSl({ symbol, candle });
|
|
5179
5545
|
await checkTp({ symbol, candle });
|
|
@@ -5214,244 +5580,8 @@ var invokeAfterSignalsHooks = async (hooks, params) => {
|
|
|
5214
5580
|
}
|
|
5215
5581
|
};
|
|
5216
5582
|
|
|
5217
|
-
// src/lib/marketData/windows.ts
|
|
5218
|
-
var import_indicators2 = require("@tradejs/core/indicators");
|
|
5219
|
-
var splitCandlesForReplayWindow = (candles, start, preloadStart) => {
|
|
5220
|
-
const prevData = [];
|
|
5221
|
-
const replayData = [];
|
|
5222
|
-
for (const candle of candles) {
|
|
5223
|
-
if (candle.timestamp < preloadStart) {
|
|
5224
|
-
continue;
|
|
5225
|
-
}
|
|
5226
|
-
if (candle.timestamp < start) {
|
|
5227
|
-
prevData.push(candle);
|
|
5228
|
-
continue;
|
|
5229
|
-
}
|
|
5230
|
-
replayData.push(candle);
|
|
5231
|
-
}
|
|
5232
|
-
return { prevData, replayData };
|
|
5233
|
-
};
|
|
5234
|
-
var alignSymbolWithBtcReference = (symbolCandles, btcCandles) => (0, import_indicators2.alignSortedCandlesByTimestamp)(symbolCandles, btcCandles);
|
|
5235
|
-
|
|
5236
|
-
// src/lib/replay/historicalSignalsReplayPreparation.ts
|
|
5237
|
-
var buildPreparedData = ({
|
|
5238
|
-
data,
|
|
5239
|
-
btcData,
|
|
5240
|
-
ethData,
|
|
5241
|
-
btcBinanceData,
|
|
5242
|
-
btcCoinbaseData,
|
|
5243
|
-
start,
|
|
5244
|
-
preloadStart
|
|
5245
|
-
}) => {
|
|
5246
|
-
const { prevData: prevDataRaw, replayData: replayDataRaw } = splitCandlesForReplayWindow(data, start, preloadStart);
|
|
5247
|
-
const { prevData: btcPrevDataRaw, replayData: btcReplayDataRaw } = splitCandlesForReplayWindow(btcData, start, preloadStart);
|
|
5248
|
-
const { prevData: ethPrevDataRaw, replayData: ethReplayDataRaw } = splitCandlesForReplayWindow(ethData, start, preloadStart);
|
|
5249
|
-
const { prevData: btcBinancePrevDataRaw } = splitCandlesForReplayWindow(
|
|
5250
|
-
btcBinanceData,
|
|
5251
|
-
start,
|
|
5252
|
-
preloadStart
|
|
5253
|
-
);
|
|
5254
|
-
const { prevData: btcCoinbasePrevDataRaw } = splitCandlesForReplayWindow(
|
|
5255
|
-
btcCoinbaseData,
|
|
5256
|
-
start,
|
|
5257
|
-
preloadStart
|
|
5258
|
-
);
|
|
5259
|
-
const { alignedCoinCandles: prevData, alignedBtcCandles: btcPrevData } = alignSymbolWithBtcReference(prevDataRaw, btcPrevDataRaw);
|
|
5260
|
-
const { alignedCoinCandles: replayData, alignedBtcCandles: btcReplayData } = alignSymbolWithBtcReference(replayDataRaw, btcReplayDataRaw);
|
|
5261
|
-
const { alignedBtcCandles: ethPrevData } = alignSymbolWithBtcReference(
|
|
5262
|
-
prevData,
|
|
5263
|
-
ethPrevDataRaw
|
|
5264
|
-
);
|
|
5265
|
-
const { alignedBtcCandles: ethReplayData } = alignSymbolWithBtcReference(
|
|
5266
|
-
replayData,
|
|
5267
|
-
ethReplayDataRaw
|
|
5268
|
-
);
|
|
5269
|
-
const { alignedBtcCandles: btcBinancePrevData } = alignSymbolWithBtcReference(
|
|
5270
|
-
prevDataRaw,
|
|
5271
|
-
btcBinancePrevDataRaw
|
|
5272
|
-
);
|
|
5273
|
-
const { alignedBtcCandles: btcCoinbasePrevData } = alignSymbolWithBtcReference(prevDataRaw, btcCoinbasePrevDataRaw);
|
|
5274
|
-
return {
|
|
5275
|
-
prevData,
|
|
5276
|
-
btcPrevData,
|
|
5277
|
-
ethPrevData,
|
|
5278
|
-
replayData,
|
|
5279
|
-
btcReplayData,
|
|
5280
|
-
ethReplayData,
|
|
5281
|
-
btcBinancePrevData,
|
|
5282
|
-
btcCoinbasePrevData
|
|
5283
|
-
};
|
|
5284
|
-
};
|
|
5285
|
-
var strategyIncludesSymbol = (strategy, symbol) => {
|
|
5286
|
-
const tickers = strategy.selection?.tickers;
|
|
5287
|
-
if (!tickers) return true;
|
|
5288
|
-
const normalizedSymbol = symbol.trim().toUpperCase();
|
|
5289
|
-
return tickers.some(
|
|
5290
|
-
(ticker) => ticker.trim().toUpperCase() === normalizedSymbol
|
|
5291
|
-
);
|
|
5292
|
-
};
|
|
5293
|
-
var buildSymbolSharedReplayKey = ({
|
|
5294
|
-
userName,
|
|
5295
|
-
connectorName,
|
|
5296
|
-
interval,
|
|
5297
|
-
symbol,
|
|
5298
|
-
start,
|
|
5299
|
-
end
|
|
5300
|
-
}) => ["replay", userName, connectorName, symbol, interval, start, end].join(":");
|
|
5301
|
-
var prepareHistoricalReplay = async (context, adapters) => {
|
|
5302
|
-
const {
|
|
5303
|
-
userName,
|
|
5304
|
-
projectRoot,
|
|
5305
|
-
preparedRun,
|
|
5306
|
-
interval,
|
|
5307
|
-
connectorName,
|
|
5308
|
-
replayConnector,
|
|
5309
|
-
strategies,
|
|
5310
|
-
references
|
|
5311
|
-
} = context;
|
|
5312
|
-
const symbolRuntimes = [];
|
|
5313
|
-
const orderedTimestamps = /* @__PURE__ */ new Set();
|
|
5314
|
-
const sharedReplayKeyPrefixes = [];
|
|
5315
|
-
const runtimeLineages = [];
|
|
5316
|
-
let skippedSymbols = 0;
|
|
5317
|
-
for (const symbol of preparedRun.tickers) {
|
|
5318
|
-
const data = await preparedRun.marketConnector.kline({
|
|
5319
|
-
symbol,
|
|
5320
|
-
start: preparedRun.preloadStart,
|
|
5321
|
-
end: preparedRun.window.end,
|
|
5322
|
-
cacheOnly: true,
|
|
5323
|
-
interval
|
|
5324
|
-
});
|
|
5325
|
-
if (symbolRuntimes.length % 50 === 0)
|
|
5326
|
-
adapters.sampleMemory?.("history:loaded", {
|
|
5327
|
-
symbols: symbolRuntimes.length + 1,
|
|
5328
|
-
candles: data.length
|
|
5329
|
-
});
|
|
5330
|
-
const preparedData = buildPreparedData({
|
|
5331
|
-
data,
|
|
5332
|
-
btcData: references.btcMarketData,
|
|
5333
|
-
ethData: references.ethMarketData,
|
|
5334
|
-
btcBinanceData: references.btcBinanceData,
|
|
5335
|
-
btcCoinbaseData: references.btcCoinbaseData,
|
|
5336
|
-
start: preparedRun.window.start,
|
|
5337
|
-
preloadStart: preparedRun.preloadStart
|
|
5338
|
-
});
|
|
5339
|
-
if (!preparedData.replayData.length || !preparedData.btcReplayData.length) {
|
|
5340
|
-
skippedSymbols += 1;
|
|
5341
|
-
adapters.progress.tick({
|
|
5342
|
-
skipped: adapters.display.skipped(skippedSymbols),
|
|
5343
|
-
symbol: adapters.display.symbol(symbol)
|
|
5344
|
-
});
|
|
5345
|
-
continue;
|
|
5346
|
-
}
|
|
5347
|
-
const sharedIndicatorsReplayKey = buildSymbolSharedReplayKey({
|
|
5348
|
-
userName,
|
|
5349
|
-
connectorName,
|
|
5350
|
-
interval,
|
|
5351
|
-
symbol,
|
|
5352
|
-
start: preparedRun.window.start,
|
|
5353
|
-
end: preparedRun.window.end
|
|
5354
|
-
});
|
|
5355
|
-
sharedReplayKeyPrefixes.push(sharedIndicatorsReplayKey);
|
|
5356
|
-
const strategiesForSymbol = await Promise.all(
|
|
5357
|
-
strategies.filter((strategy) => strategyIncludesSymbol(strategy, symbol)).map(async (strategy) => {
|
|
5358
|
-
const replayStrategyConfig = buildReplayStrategyConfig({
|
|
5359
|
-
strategyConfig: strategy.strategyConfig,
|
|
5360
|
-
interval
|
|
5361
|
-
});
|
|
5362
|
-
const runtimeLineage = await adapters.buildLineage({
|
|
5363
|
-
projectRoot,
|
|
5364
|
-
strategyName: strategy.strategyName,
|
|
5365
|
-
strategyRevision: strategy.strategyRevision,
|
|
5366
|
-
deploymentCompositionId: strategy.deploymentCompositionId,
|
|
5367
|
-
strategyPackageVersion: strategy.strategyPackageVersion,
|
|
5368
|
-
strategyDependencyVersions: strategy.strategyDependencyVersions,
|
|
5369
|
-
runtimePackageVersion: strategy.runtimePackageVersion,
|
|
5370
|
-
config: {
|
|
5371
|
-
configId: strategy.strategyRevision,
|
|
5372
|
-
strategyConfig: strategy.strategyConfig,
|
|
5373
|
-
symbolResultConfig: strategy.strategyResults?.[symbol]?.config ?? null
|
|
5374
|
-
},
|
|
5375
|
-
runContext: {
|
|
5376
|
-
connectorName: connectorName.toLowerCase(),
|
|
5377
|
-
interval: String(interval),
|
|
5378
|
-
universe: preparedRun.universe ?? null
|
|
5379
|
-
}
|
|
5380
|
-
});
|
|
5381
|
-
runtimeLineages.push({
|
|
5382
|
-
strategy: strategy.strategyName,
|
|
5383
|
-
symbol,
|
|
5384
|
-
...preparedRun.deploymentId ? { deploymentId: preparedRun.deploymentId } : {},
|
|
5385
|
-
...preparedRun.accountId ? { accountId: preparedRun.accountId } : {},
|
|
5386
|
-
lineage: runtimeLineage
|
|
5387
|
-
});
|
|
5388
|
-
return {
|
|
5389
|
-
strategyName: strategy.strategyName,
|
|
5390
|
-
strategyConfig: strategy.strategyConfig,
|
|
5391
|
-
runtimeLineage,
|
|
5392
|
-
run: await strategy.strategyCreator({
|
|
5393
|
-
userName,
|
|
5394
|
-
connectorName,
|
|
5395
|
-
runtimeConfigId: strategy.strategyRevision,
|
|
5396
|
-
strategyRevision: strategy.strategyRevision,
|
|
5397
|
-
runtimeLineage,
|
|
5398
|
-
universe: preparedRun.universe,
|
|
5399
|
-
accountId: preparedRun.accountId,
|
|
5400
|
-
deploymentId: preparedRun.deploymentId,
|
|
5401
|
-
runtimeConfigSnapshot: {
|
|
5402
|
-
userConfig: replayStrategyConfig
|
|
5403
|
-
},
|
|
5404
|
-
config: replayStrategyConfig,
|
|
5405
|
-
symbol,
|
|
5406
|
-
data: preparedData.prevData,
|
|
5407
|
-
btcData: preparedData.btcPrevData,
|
|
5408
|
-
ethData: preparedData.ethPrevData,
|
|
5409
|
-
btcBinanceData: preparedData.btcBinancePrevData,
|
|
5410
|
-
btcCoinbaseData: preparedData.btcCoinbasePrevData,
|
|
5411
|
-
connector: replayConnector,
|
|
5412
|
-
sharedIndicatorsReplayKey
|
|
5413
|
-
})
|
|
5414
|
-
};
|
|
5415
|
-
})
|
|
5416
|
-
);
|
|
5417
|
-
const symbolRuntime = {
|
|
5418
|
-
symbol,
|
|
5419
|
-
replayData: preparedData.replayData,
|
|
5420
|
-
btcReplayData: preparedData.btcReplayData,
|
|
5421
|
-
ethReplayData: preparedData.ethReplayData,
|
|
5422
|
-
currentIndex: 0,
|
|
5423
|
-
strategies: strategiesForSymbol
|
|
5424
|
-
};
|
|
5425
|
-
symbolRuntimes.push(symbolRuntime);
|
|
5426
|
-
if (symbolRuntimes.length % 50 === 0 || symbol === preparedRun.tickers.at(-1))
|
|
5427
|
-
adapters.sampleMemory?.("strategies:initializing", {
|
|
5428
|
-
symbols: symbolRuntimes.length,
|
|
5429
|
-
strategiesPerSymbol: strategiesForSymbol.length
|
|
5430
|
-
});
|
|
5431
|
-
adapters.progress.tick({
|
|
5432
|
-
skipped: adapters.display.skipped(skippedSymbols),
|
|
5433
|
-
symbol: adapters.display.symbol(symbol)
|
|
5434
|
-
});
|
|
5435
|
-
for (const candle of preparedData.replayData) {
|
|
5436
|
-
orderedTimestamps.add(candle.timestamp);
|
|
5437
|
-
}
|
|
5438
|
-
}
|
|
5439
|
-
return {
|
|
5440
|
-
symbolRuntimes,
|
|
5441
|
-
orderedTimestamps: [...orderedTimestamps].sort(
|
|
5442
|
-
(left, right) => left - right
|
|
5443
|
-
),
|
|
5444
|
-
sharedReplayKeyPrefixes,
|
|
5445
|
-
runtimeLineages
|
|
5446
|
-
};
|
|
5447
|
-
};
|
|
5448
|
-
var releaseHistoricalReplayPlan = (plan) => {
|
|
5449
|
-
plan.symbolRuntimes.length = 0;
|
|
5450
|
-
plan.sharedReplayKeyPrefixes.length = 0;
|
|
5451
|
-
};
|
|
5452
|
-
|
|
5453
5583
|
// src/lib/replay/historicalSignalsReplaySkipEvidence.ts
|
|
5454
|
-
var
|
|
5584
|
+
var import_data2 = require("@tradejs/core/data");
|
|
5455
5585
|
var replayScopeKey = (strategy, symbol) => `${strategy}::${symbol}`;
|
|
5456
5586
|
var appendReplaySignalEvaluationRun = ({
|
|
5457
5587
|
runs = [],
|
|
@@ -5459,7 +5589,7 @@ var appendReplaySignalEvaluationRun = ({
|
|
|
5459
5589
|
timestamp,
|
|
5460
5590
|
interval
|
|
5461
5591
|
}) => {
|
|
5462
|
-
const stepMs = (0,
|
|
5592
|
+
const stepMs = (0, import_data2.intervalToMs)(interval);
|
|
5463
5593
|
const last = runs.at(-1);
|
|
5464
5594
|
if (last?.reason === reason && last.stepMs === stepMs && timestamp === last.lastTimestamp + stepMs) {
|
|
5465
5595
|
return [...runs.slice(0, -1), { ...last, lastTimestamp: timestamp }];
|
|
@@ -5531,11 +5661,12 @@ var createHistoricalReplaySkipEvidence = (runtimeLineages, enabled = true) => {
|
|
|
5531
5661
|
// src/lib/replay/historicalSignalsReplayExecution.ts
|
|
5532
5662
|
var advanceCycleMarket = async (plan, connector, timestamp) => {
|
|
5533
5663
|
const cycleSymbols = plan.symbolRuntimes.filter(
|
|
5534
|
-
(symbolRuntime) => symbolRuntime.replayData[symbolRuntime.currentIndex]?.timestamp === timestamp
|
|
5664
|
+
(symbolRuntime) => (symbolRuntime.replayData[symbolRuntime.currentIndex]?.timestamp ?? NaN) + (symbolRuntime.timestampOffsetMs ?? 0) === timestamp
|
|
5535
5665
|
);
|
|
5536
5666
|
for (const symbolRuntime of cycleSymbols) {
|
|
5537
5667
|
const candle = symbolRuntime.replayData[symbolRuntime.currentIndex];
|
|
5538
|
-
if (candle
|
|
5668
|
+
if (candle && candle.timestamp + (symbolRuntime.timestampOffsetMs ?? 0) === timestamp) {
|
|
5669
|
+
if (symbolRuntime.advanceMarket === false) continue;
|
|
5539
5670
|
await connector.advanceMarket({
|
|
5540
5671
|
symbol: symbolRuntime.symbol,
|
|
5541
5672
|
candle
|
|
@@ -5547,7 +5678,7 @@ var advanceCycleMarket = async (plan, connector, timestamp) => {
|
|
|
5547
5678
|
var advanceAbortedCycle = (cycleSymbols, timestamp) => {
|
|
5548
5679
|
for (const symbolRuntime of cycleSymbols ?? []) {
|
|
5549
5680
|
const candle = symbolRuntime.replayData[symbolRuntime.currentIndex];
|
|
5550
|
-
if (candle
|
|
5681
|
+
if (candle && candle.timestamp + (symbolRuntime.timestampOffsetMs ?? 0) === timestamp) {
|
|
5551
5682
|
symbolRuntime.currentIndex += 1;
|
|
5552
5683
|
}
|
|
5553
5684
|
}
|
|
@@ -5596,21 +5727,21 @@ var executeHistoricalReplay = async (context, adapters) => {
|
|
|
5596
5727
|
const candle = symbolRuntime.replayData[symbolRuntime.currentIndex];
|
|
5597
5728
|
const btcCandle = symbolRuntime.btcReplayData[symbolRuntime.currentIndex];
|
|
5598
5729
|
const ethCandle = symbolRuntime.ethReplayData[symbolRuntime.currentIndex];
|
|
5599
|
-
if (!candle || !btcCandle || candle.timestamp !== timestamp || btcCandle.timestamp !== timestamp) {
|
|
5730
|
+
if (!candle || !btcCandle || candle.timestamp + (symbolRuntime.timestampOffsetMs ?? 0) !== timestamp || btcCandle.timestamp !== candle.timestamp) {
|
|
5600
5731
|
continue;
|
|
5601
5732
|
}
|
|
5602
5733
|
for (const strategyRuntime of symbolRuntime.strategies) {
|
|
5603
5734
|
const result = await strategyRuntime.run(
|
|
5604
5735
|
candle,
|
|
5605
5736
|
btcCandle,
|
|
5606
|
-
ethCandle?.timestamp === timestamp ? ethCandle : void 0
|
|
5737
|
+
ethCandle?.timestamp === candle.timestamp ? ethCandle : void 0
|
|
5607
5738
|
);
|
|
5608
5739
|
skipEvidence.record({
|
|
5609
5740
|
strategyName: strategyRuntime.strategyName,
|
|
5610
5741
|
symbol: symbolRuntime.symbol,
|
|
5611
5742
|
strategyConfig: strategyRuntime.strategyConfig,
|
|
5612
5743
|
runtimeLineage: strategyRuntime.runtimeLineage,
|
|
5613
|
-
timestamp,
|
|
5744
|
+
timestamp: candle.timestamp,
|
|
5614
5745
|
result
|
|
5615
5746
|
});
|
|
5616
5747
|
if (result && typeof result !== "string") {
|
|
@@ -5631,10 +5762,7 @@ var executeHistoricalReplay = async (context, adapters) => {
|
|
|
5631
5762
|
reportProgress(timestamp);
|
|
5632
5763
|
}
|
|
5633
5764
|
} finally {
|
|
5634
|
-
|
|
5635
|
-
adapters.releaseIndicatorsCache(keyPrefix);
|
|
5636
|
-
adapters.releaseReplayCache(keyPrefix);
|
|
5637
|
-
}
|
|
5765
|
+
releaseHistoricalReplayCaches(context.plan, adapters);
|
|
5638
5766
|
}
|
|
5639
5767
|
return {
|
|
5640
5768
|
signals,
|
|
@@ -5908,7 +6036,19 @@ var runHistoricalSignalsReplay = async ({
|
|
|
5908
6036
|
}
|
|
5909
6037
|
);
|
|
5910
6038
|
memory?.("references:loaded");
|
|
5911
|
-
const
|
|
6039
|
+
const cacheAdapters = {
|
|
6040
|
+
releaseIndicatorsCache: (keyPrefix) => releaseReplayCacheCopies(
|
|
6041
|
+
keyPrefix,
|
|
6042
|
+
import_strategies3.releaseStrategyIndicatorsReplayCache,
|
|
6043
|
+
getProjectReplayCacheReleasers()?.releaseStrategyIndicatorsReplayCache
|
|
6044
|
+
),
|
|
6045
|
+
releaseReplayCache: (keyPrefix) => releaseReplayCacheCopies(
|
|
6046
|
+
keyPrefix,
|
|
6047
|
+
import_strategies3.releaseStrategyReplayCache,
|
|
6048
|
+
getProjectReplayCacheReleasers()?.releaseStrategyReplayCache
|
|
6049
|
+
)
|
|
6050
|
+
};
|
|
6051
|
+
const plan = await prepareMultiIntervalHistoricalReplay(
|
|
5912
6052
|
{
|
|
5913
6053
|
userName: replayUserName,
|
|
5914
6054
|
projectRoot: replayProjectRoot,
|
|
@@ -5925,6 +6065,7 @@ var runHistoricalSignalsReplay = async ({
|
|
|
5925
6065
|
}
|
|
5926
6066
|
},
|
|
5927
6067
|
{
|
|
6068
|
+
...cacheAdapters,
|
|
5928
6069
|
progress: {
|
|
5929
6070
|
tick: (tokens) => prepareBar.tick(1, tokens)
|
|
5930
6071
|
},
|
|
@@ -5934,7 +6075,11 @@ var runHistoricalSignalsReplay = async ({
|
|
|
5934
6075
|
},
|
|
5935
6076
|
buildLineage: buildRuntimeLineage,
|
|
5936
6077
|
sampleMemory: memory
|
|
5937
|
-
}
|
|
6078
|
+
},
|
|
6079
|
+
(nativeInterval, nativeRun) => loadHistoricalReplayReferences({
|
|
6080
|
+
preparedRun: nativeRun,
|
|
6081
|
+
interval: nativeInterval
|
|
6082
|
+
})
|
|
5938
6083
|
);
|
|
5939
6084
|
memory?.("strategies:prepared", { symbols: plan.symbolRuntimes.length });
|
|
5940
6085
|
const hookContext = buildAfterSignalsContext({
|
|
@@ -5961,6 +6106,7 @@ var runHistoricalSignalsReplay = async ({
|
|
|
5961
6106
|
hookContext
|
|
5962
6107
|
},
|
|
5963
6108
|
{
|
|
6109
|
+
...cacheAdapters,
|
|
5964
6110
|
clock: { now: Date.now },
|
|
5965
6111
|
progress: {
|
|
5966
6112
|
enabled: showProgress,
|
|
@@ -5973,17 +6119,7 @@ var runHistoricalSignalsReplay = async ({
|
|
|
5973
6119
|
},
|
|
5974
6120
|
invokeBeforeSignals: invokeBeforeSignalsHooks,
|
|
5975
6121
|
invokeAfterSignals: invokeAfterSignalsHooks,
|
|
5976
|
-
enrichSignal: (signal) => (0, import_strategies4.enrichSignalWithBinanceMarketContext)({ signal, env: "PARITY" })
|
|
5977
|
-
releaseIndicatorsCache: (keyPrefix) => releaseReplayCacheCopies(
|
|
5978
|
-
keyPrefix,
|
|
5979
|
-
import_strategies3.releaseStrategyIndicatorsReplayCache,
|
|
5980
|
-
getProjectReplayCacheReleasers()?.releaseStrategyIndicatorsReplayCache
|
|
5981
|
-
),
|
|
5982
|
-
releaseReplayCache: (keyPrefix) => releaseReplayCacheCopies(
|
|
5983
|
-
keyPrefix,
|
|
5984
|
-
import_strategies3.releaseStrategyReplayCache,
|
|
5985
|
-
getProjectReplayCacheReleasers()?.releaseStrategyReplayCache
|
|
5986
|
-
)
|
|
6122
|
+
enrichSignal: (signal) => (0, import_strategies4.enrichSignalWithBinanceMarketContext)({ signal, env: "PARITY" })
|
|
5987
6123
|
}
|
|
5988
6124
|
);
|
|
5989
6125
|
memory?.("execution:complete", { signals: execution.signals.length });
|
|
@@ -6443,6 +6579,7 @@ var saveAndPrintReplayResultsByStrategy = async ({
|
|
|
6443
6579
|
|
|
6444
6580
|
// src/lib/replay/runtimeComparison.ts
|
|
6445
6581
|
var import_chalk12 = __toESM(require("chalk"));
|
|
6582
|
+
var import_data4 = require("@tradejs/core/data");
|
|
6446
6583
|
|
|
6447
6584
|
// src/lib/paritySummary.ts
|
|
6448
6585
|
var summarizeRuntimeTradesByStrategy = (trades) => {
|
|
@@ -6521,7 +6658,7 @@ var import_redis4 = require("@tradejs/infra/redis");
|
|
|
6521
6658
|
|
|
6522
6659
|
// src/lib/runtimeSignalsStorage.ts
|
|
6523
6660
|
var import_constants5 = require("@tradejs/core/constants");
|
|
6524
|
-
var
|
|
6661
|
+
var import_data3 = require("@tradejs/core/data");
|
|
6525
6662
|
var import_time4 = require("@tradejs/core/time");
|
|
6526
6663
|
var isRuntimeSignalBucketRef = (value) => {
|
|
6527
6664
|
if (!value || typeof value !== "object") {
|
|
@@ -8046,7 +8183,10 @@ var findExchangeRuntimeOutcome = ({
|
|
|
8046
8183
|
return candidates.map((candidate) => ({
|
|
8047
8184
|
...candidate,
|
|
8048
8185
|
timestampDiffMs: Math.abs(
|
|
8049
|
-
candidate.timestamp +
|
|
8186
|
+
candidate.timestamp + resolveTradeParityTimestampOffset(
|
|
8187
|
+
strategyName,
|
|
8188
|
+
signalTimestampOffsetMs
|
|
8189
|
+
) - exchangeEntry.entryTimestamp
|
|
8050
8190
|
)
|
|
8051
8191
|
})).filter((candidate) => candidate.timestampDiffMs <= toleranceMs).sort(
|
|
8052
8192
|
(left, right) => left.timestampDiffMs - right.timestampDiffMs || Number(right.orderStatus === "failed") - Number(left.orderStatus === "failed")
|
|
@@ -8286,10 +8426,13 @@ var writeReplayComparisonReport = (lines) => {
|
|
|
8286
8426
|
};
|
|
8287
8427
|
|
|
8288
8428
|
// src/lib/replay/runtimeComparison.ts
|
|
8289
|
-
var
|
|
8290
|
-
const
|
|
8291
|
-
|
|
8292
|
-
};
|
|
8429
|
+
var replayStrategyIntervals = (summaries) => summaries.map(({ strategyName, strategyConfig }) => {
|
|
8430
|
+
const durationMs = (0, import_data4.intervalToMs)(strategyConfig.INTERVAL);
|
|
8431
|
+
if (!Number.isFinite(durationMs) || durationMs <= 0) {
|
|
8432
|
+
throw new Error(`Invalid replay interval for ${strategyName}`);
|
|
8433
|
+
}
|
|
8434
|
+
return { strategyName, interval: String(durationMs / 6e4) };
|
|
8435
|
+
});
|
|
8293
8436
|
var buildReplaySignalEvaluations = (signals) => signals.map((signal) => ({
|
|
8294
8437
|
evaluationId: `${signal.strategy}:${signal.symbol}:${signal.timestamp}`,
|
|
8295
8438
|
userName: replayUserName,
|
|
@@ -8506,7 +8649,8 @@ var saveAndPrintReplayExchangeComparison = async ({
|
|
|
8506
8649
|
runtimeSignals,
|
|
8507
8650
|
runtimeSignalEvaluations,
|
|
8508
8651
|
runtimeLineageScopes,
|
|
8509
|
-
lineage
|
|
8652
|
+
lineage,
|
|
8653
|
+
timestampOffsetMs
|
|
8510
8654
|
}) => {
|
|
8511
8655
|
const { connector, connectorName, window } = getRuntimeCompareContext();
|
|
8512
8656
|
const rawExchangeEntries = await loadExchangeEntriesForComparison({
|
|
@@ -8531,7 +8675,7 @@ var saveAndPrintReplayExchangeComparison = async ({
|
|
|
8531
8675
|
runtimeSignals,
|
|
8532
8676
|
runtimeSignalEvaluations,
|
|
8533
8677
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8534
|
-
signalTimestampOffsetMs:
|
|
8678
|
+
signalTimestampOffsetMs: timestampOffsetMs
|
|
8535
8679
|
});
|
|
8536
8680
|
});
|
|
8537
8681
|
const comparisonLineage = {
|
|
@@ -8553,7 +8697,7 @@ var saveAndPrintReplayExchangeComparison = async ({
|
|
|
8553
8697
|
backtestEntries,
|
|
8554
8698
|
strategyNameByOrderLinkKey,
|
|
8555
8699
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8556
|
-
backtestTimestampOffsetMs:
|
|
8700
|
+
backtestTimestampOffsetMs: timestampOffsetMs,
|
|
8557
8701
|
runtimeSignals,
|
|
8558
8702
|
runtimeSignalEvaluations,
|
|
8559
8703
|
replaySignals,
|
|
@@ -8592,7 +8736,7 @@ var saveAndPrintReplayExchangeComparison = async ({
|
|
|
8592
8736
|
exchangeEntries,
|
|
8593
8737
|
backtestEntries,
|
|
8594
8738
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8595
|
-
backtestTimestampOffsetMs:
|
|
8739
|
+
backtestTimestampOffsetMs: timestampOffsetMs
|
|
8596
8740
|
});
|
|
8597
8741
|
const { completed: matched, orderFailed } = splitExchangeMatchesByRuntimeOrderStatus({
|
|
8598
8742
|
matched: comparison.matched,
|
|
@@ -8600,7 +8744,7 @@ var saveAndPrintReplayExchangeComparison = async ({
|
|
|
8600
8744
|
runtimeSignals,
|
|
8601
8745
|
runtimeSignalEvaluations,
|
|
8602
8746
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8603
|
-
signalTimestampOffsetMs:
|
|
8747
|
+
signalTimestampOffsetMs: timestampOffsetMs
|
|
8604
8748
|
});
|
|
8605
8749
|
const details = buildReplayExchangeComparisonDetails({
|
|
8606
8750
|
matched,
|
|
@@ -8611,7 +8755,7 @@ var saveAndPrintReplayExchangeComparison = async ({
|
|
|
8611
8755
|
backtestEntries,
|
|
8612
8756
|
strategyNameByOrderLinkKey,
|
|
8613
8757
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8614
|
-
backtestTimestampOffsetMs:
|
|
8758
|
+
backtestTimestampOffsetMs: timestampOffsetMs,
|
|
8615
8759
|
runtimeSignals,
|
|
8616
8760
|
runtimeSignalEvaluations,
|
|
8617
8761
|
replaySignals,
|
|
@@ -8672,9 +8816,16 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8672
8816
|
expectedUserName: replayUserName,
|
|
8673
8817
|
expectedWindow: window
|
|
8674
8818
|
}) : null;
|
|
8675
|
-
const
|
|
8676
|
-
|
|
8677
|
-
|
|
8819
|
+
const declaredIntervals = runtimeEvidence ? [] : replayStrategyIntervals(liveStrategySummaries);
|
|
8820
|
+
const replayTimestampOffset = buildTradeParityTimestampOffset(
|
|
8821
|
+
runtimeEvidence ? (0, runtimeEvidenceDeployment_exports.activeRuntimeEvidenceStrategies)(runtimeEvidence.deployment) : declaredIntervals
|
|
8822
|
+
);
|
|
8823
|
+
const metadataStart = runtimeEvidence ? window.start : window.start - Math.max(
|
|
8824
|
+
0,
|
|
8825
|
+
...declaredIntervals.map(
|
|
8826
|
+
({ interval }) => Number(interval) * 6e4 - (0, import_data4.intervalToMs)(replayInterval)
|
|
8827
|
+
)
|
|
8828
|
+
);
|
|
8678
8829
|
const [
|
|
8679
8830
|
rawRuntimeTrades,
|
|
8680
8831
|
runtimeSignals,
|
|
@@ -8691,15 +8842,15 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8691
8842
|
endTime: window.end
|
|
8692
8843
|
}),
|
|
8693
8844
|
loadRuntimeSignals(replayUserName, {
|
|
8694
|
-
startTime:
|
|
8845
|
+
startTime: metadataStart,
|
|
8695
8846
|
endTime: window.end
|
|
8696
8847
|
}),
|
|
8697
8848
|
loadRuntimeSignalEvaluations(replayUserName, {
|
|
8698
|
-
startTime:
|
|
8849
|
+
startTime: metadataStart,
|
|
8699
8850
|
endTime: window.end
|
|
8700
8851
|
}),
|
|
8701
8852
|
loadRuntimeLineageScopes(replayUserName, {
|
|
8702
|
-
startTime:
|
|
8853
|
+
startTime: metadataStart,
|
|
8703
8854
|
endTime: window.end
|
|
8704
8855
|
})
|
|
8705
8856
|
]);
|
|
@@ -8767,6 +8918,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8767
8918
|
);
|
|
8768
8919
|
return saveAndPrintReplayExchangeComparison({
|
|
8769
8920
|
liveStrategySummaries,
|
|
8921
|
+
timestampOffsetMs: replayTimestampOffset,
|
|
8770
8922
|
backtestEntries: comparableBacktestEntries,
|
|
8771
8923
|
replaySignals,
|
|
8772
8924
|
replaySignalEvaluations,
|
|
@@ -9916,16 +10068,10 @@ var replayBacktest = async () => {
|
|
|
9916
10068
|
if (!replayStrategies.length) {
|
|
9917
10069
|
throw new Error(`No enabled strategies in deployment ${deployment.id}`);
|
|
9918
10070
|
}
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
)
|
|
10071
|
+
resolveHistoricalReplayIntervals(
|
|
10072
|
+
replayInterval,
|
|
10073
|
+
replayStrategies.map(({ strategyConfig }) => strategyConfig.INTERVAL)
|
|
9923
10074
|
);
|
|
9924
|
-
if (strategyIntervals.size !== 1 || !strategyIntervals.has(replayInterval)) {
|
|
9925
|
-
throw new Error(
|
|
9926
|
-
`Replay interval does not match deployment ${deployment.id}: replay=${replayInterval}, strategies=${[...strategyIntervals].join(",")}`
|
|
9927
|
-
);
|
|
9928
|
-
}
|
|
9929
10075
|
const strategyUniverses = new Set(
|
|
9930
10076
|
replayStrategies.map(
|
|
9931
10077
|
({ strategyConfig }) => String(strategyConfig.UNIVERSE)
|
|
@@ -9971,13 +10117,13 @@ var replayBacktest = async () => {
|
|
|
9971
10117
|
deploymentId: deployment.id,
|
|
9972
10118
|
assetClasses: deployment.assetClasses,
|
|
9973
10119
|
deployment,
|
|
9974
|
-
closedIntervalMs: (0,
|
|
10120
|
+
closedIntervalMs: (0, import_data5.intervalToMs)(replayInterval)
|
|
9975
10121
|
});
|
|
9976
10122
|
if (!preparedRun || isReplayUpdateOnlyRun) {
|
|
9977
10123
|
return;
|
|
9978
10124
|
}
|
|
9979
10125
|
if (useAllData) {
|
|
9980
|
-
const intervalMs = (0,
|
|
10126
|
+
const intervalMs = (0, import_data5.intervalToMs)(replayInterval);
|
|
9981
10127
|
const edges = await (0, import_candles.getDataEdgesForSymbols)(
|
|
9982
10128
|
deployment.provider,
|
|
9983
10129
|
preparedRun.tickers,
|