@tradejs/cli 3.1.29-beta.286 → 3.1.29-beta.288
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
|
@@ -39,9 +39,482 @@ __export(replayRunner_exports, {
|
|
|
39
39
|
resolveReplayStrategyNameFromExchangeEntry: () => resolveReplayStrategyNameFromExchangeEntry
|
|
40
40
|
});
|
|
41
41
|
module.exports = __toCommonJS(replayRunner_exports);
|
|
42
|
+
|
|
43
|
+
// src/lib/replay/historicalSignalsReplayPreparation.ts
|
|
44
|
+
var import_data = require("@tradejs/core/data");
|
|
45
|
+
|
|
46
|
+
// src/lib/replay/support.ts
|
|
47
|
+
var import_chalk = __toESM(require("chalk"));
|
|
48
|
+
|
|
49
|
+
// src/lib/runtimeModeConfig.ts
|
|
50
|
+
var buildRuntimeModeStrategyConfig = ({
|
|
51
|
+
strategyConfig,
|
|
52
|
+
env,
|
|
53
|
+
interval,
|
|
54
|
+
makeOrders,
|
|
55
|
+
recordRuntimeTrades,
|
|
56
|
+
aiReplayAnalyses
|
|
57
|
+
}) => ({
|
|
58
|
+
...strategyConfig,
|
|
59
|
+
ENV: env,
|
|
60
|
+
MAKE_ORDERS: makeOrders,
|
|
61
|
+
INTERVAL: strategyConfig.INTERVAL ?? interval,
|
|
62
|
+
...typeof recordRuntimeTrades === "boolean" ? { RECORD_RUNTIME_TRADES: recordRuntimeTrades } : {},
|
|
63
|
+
...aiReplayAnalyses?.length ? { AI_REPLAY_ANALYSES: aiReplayAnalyses } : {}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// src/lib/replay/support.ts
|
|
67
|
+
var REPLAY_RESULTS_BY_STRATEGY_HEADERS = [
|
|
68
|
+
import_chalk.default.blue("STRATEGY"),
|
|
69
|
+
import_chalk.default.yellow("TICKERS"),
|
|
70
|
+
import_chalk.default.yellow("TRADE TICKERS"),
|
|
71
|
+
import_chalk.default.cyan("ORDERS"),
|
|
72
|
+
import_chalk.default.cyan("WIN/LOSS (%)"),
|
|
73
|
+
import_chalk.default.cyan("PROFIT"),
|
|
74
|
+
import_chalk.default.cyan("AVG TRADE")
|
|
75
|
+
];
|
|
76
|
+
var REPLAY_RUNTIME_COMPARISON_HEADERS = [
|
|
77
|
+
import_chalk.default.blue("STRATEGY"),
|
|
78
|
+
import_chalk.default.cyan("BT ENTRIES"),
|
|
79
|
+
import_chalk.default.cyan("BT PNL"),
|
|
80
|
+
import_chalk.default.yellow("RT TRADES"),
|
|
81
|
+
import_chalk.default.yellow("RT PNL"),
|
|
82
|
+
import_chalk.default.green("MATCHED"),
|
|
83
|
+
import_chalk.default.red("ORDER FAILED"),
|
|
84
|
+
import_chalk.default.yellow("RT ONLY"),
|
|
85
|
+
import_chalk.default.magenta("BT ONLY"),
|
|
86
|
+
import_chalk.default.red("UNMATCHED")
|
|
87
|
+
];
|
|
88
|
+
var REPLAY_RESULTS_CONFIG = "replay";
|
|
89
|
+
var REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS = 1;
|
|
90
|
+
var REPLAY_RUNTIME_COMPARE_TOLERANCE_MS = REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS * 15 * 60 * 1e3;
|
|
91
|
+
var formatReplayRuntimeCompareTolerance = () => `${REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS} ${REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS === 1 ? "bar" : "bars"}`;
|
|
92
|
+
var getReplayRuntimeUnmatchedCount = (row) => row.orderFailed + row.runtimeOnly + row.backtestOnly;
|
|
93
|
+
var buildReplayStrategyConfig = ({
|
|
94
|
+
strategyConfig,
|
|
95
|
+
interval
|
|
96
|
+
}) => ({
|
|
97
|
+
...buildRuntimeModeStrategyConfig({
|
|
98
|
+
strategyConfig,
|
|
99
|
+
env: "PARITY",
|
|
100
|
+
interval,
|
|
101
|
+
makeOrders: false,
|
|
102
|
+
recordRuntimeTrades: false
|
|
103
|
+
}),
|
|
104
|
+
SIMULATE_ORDERS: true
|
|
105
|
+
});
|
|
106
|
+
var toRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
107
|
+
var compactReplayAdditionalIndicators = (additionalIndicators) => {
|
|
108
|
+
const additional = toRecord(additionalIndicators);
|
|
109
|
+
if (!additional) {
|
|
110
|
+
return void 0;
|
|
111
|
+
}
|
|
112
|
+
const compact = {};
|
|
113
|
+
if (additional.executionSlippage != null) {
|
|
114
|
+
compact.executionSlippage = additional.executionSlippage;
|
|
115
|
+
}
|
|
116
|
+
if (additional.marketContext != null) {
|
|
117
|
+
compact.marketContext = additional.marketContext;
|
|
118
|
+
}
|
|
119
|
+
const baseContext = toRecord(additional.baseContext);
|
|
120
|
+
const relative = toRecord(baseContext?.relative);
|
|
121
|
+
const execution = toRecord(relative?.execution);
|
|
122
|
+
if (execution) {
|
|
123
|
+
compact.baseContext = {
|
|
124
|
+
relative: {
|
|
125
|
+
execution
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return Object.keys(compact).length ? compact : void 0;
|
|
130
|
+
};
|
|
131
|
+
var compactReplaySignal = (signal) => {
|
|
132
|
+
if (!signal) {
|
|
133
|
+
return void 0;
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
signalId: signal.signalId,
|
|
137
|
+
strategy: signal.strategy,
|
|
138
|
+
symbol: signal.symbol,
|
|
139
|
+
interval: signal.interval,
|
|
140
|
+
direction: signal.direction,
|
|
141
|
+
timestamp: signal.timestamp,
|
|
142
|
+
prices: signal.prices,
|
|
143
|
+
indicators: {},
|
|
144
|
+
figures: {},
|
|
145
|
+
additionalIndicators: compactReplayAdditionalIndicators(
|
|
146
|
+
signal.additionalIndicators
|
|
147
|
+
),
|
|
148
|
+
isConfigFromBacktest: signal.isConfigFromBacktest,
|
|
149
|
+
...signal.orderStatus ? { orderStatus: signal.orderStatus } : {},
|
|
150
|
+
...signal.orderSkipReason ? { orderSkipReason: signal.orderSkipReason } : {},
|
|
151
|
+
...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {},
|
|
152
|
+
...signal.ml ? { ml: signal.ml } : {}
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// src/lib/marketData/windows.ts
|
|
157
|
+
var import_indicators = require("@tradejs/core/indicators");
|
|
158
|
+
var splitCandlesForReplayWindow = (candles, start, preloadStart) => {
|
|
159
|
+
const prevData = [];
|
|
160
|
+
const replayData = [];
|
|
161
|
+
for (const candle of candles) {
|
|
162
|
+
if (candle.timestamp < preloadStart) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (candle.timestamp < start) {
|
|
166
|
+
prevData.push(candle);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
replayData.push(candle);
|
|
170
|
+
}
|
|
171
|
+
return { prevData, replayData };
|
|
172
|
+
};
|
|
173
|
+
var alignSymbolWithBtcReference = (symbolCandles, btcCandles) => (0, import_indicators.alignSortedCandlesByTimestamp)(symbolCandles, btcCandles);
|
|
174
|
+
|
|
175
|
+
// src/lib/replay/historicalSignalsReplayPreparation.ts
|
|
176
|
+
var buildPreparedData = ({
|
|
177
|
+
data,
|
|
178
|
+
btcData,
|
|
179
|
+
ethData,
|
|
180
|
+
btcBinanceData,
|
|
181
|
+
btcCoinbaseData,
|
|
182
|
+
start,
|
|
183
|
+
preloadStart
|
|
184
|
+
}) => {
|
|
185
|
+
const { prevData: prevDataRaw, replayData: replayDataRaw } = splitCandlesForReplayWindow(data, start, preloadStart);
|
|
186
|
+
const { prevData: btcPrevDataRaw, replayData: btcReplayDataRaw } = splitCandlesForReplayWindow(btcData, start, preloadStart);
|
|
187
|
+
const { prevData: ethPrevDataRaw, replayData: ethReplayDataRaw } = splitCandlesForReplayWindow(ethData, start, preloadStart);
|
|
188
|
+
const { prevData: btcBinancePrevDataRaw } = splitCandlesForReplayWindow(
|
|
189
|
+
btcBinanceData,
|
|
190
|
+
start,
|
|
191
|
+
preloadStart
|
|
192
|
+
);
|
|
193
|
+
const { prevData: btcCoinbasePrevDataRaw } = splitCandlesForReplayWindow(
|
|
194
|
+
btcCoinbaseData,
|
|
195
|
+
start,
|
|
196
|
+
preloadStart
|
|
197
|
+
);
|
|
198
|
+
const { alignedCoinCandles: prevData, alignedBtcCandles: btcPrevData } = alignSymbolWithBtcReference(prevDataRaw, btcPrevDataRaw);
|
|
199
|
+
const { alignedCoinCandles: replayData, alignedBtcCandles: btcReplayData } = alignSymbolWithBtcReference(replayDataRaw, btcReplayDataRaw);
|
|
200
|
+
const { alignedBtcCandles: ethPrevData } = alignSymbolWithBtcReference(
|
|
201
|
+
prevData,
|
|
202
|
+
ethPrevDataRaw
|
|
203
|
+
);
|
|
204
|
+
const { alignedBtcCandles: ethReplayData } = alignSymbolWithBtcReference(
|
|
205
|
+
replayData,
|
|
206
|
+
ethReplayDataRaw
|
|
207
|
+
);
|
|
208
|
+
const { alignedBtcCandles: btcBinancePrevData } = alignSymbolWithBtcReference(
|
|
209
|
+
prevDataRaw,
|
|
210
|
+
btcBinancePrevDataRaw
|
|
211
|
+
);
|
|
212
|
+
const { alignedBtcCandles: btcCoinbasePrevData } = alignSymbolWithBtcReference(prevDataRaw, btcCoinbasePrevDataRaw);
|
|
213
|
+
return {
|
|
214
|
+
prevData,
|
|
215
|
+
btcPrevData,
|
|
216
|
+
ethPrevData,
|
|
217
|
+
replayData,
|
|
218
|
+
btcReplayData,
|
|
219
|
+
ethReplayData,
|
|
220
|
+
btcBinancePrevData,
|
|
221
|
+
btcCoinbasePrevData
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
var strategyIncludesSymbol = (strategy, symbol) => {
|
|
225
|
+
const tickers = strategy.selection?.tickers;
|
|
226
|
+
if (!tickers) return true;
|
|
227
|
+
const normalizedSymbol = symbol.trim().toUpperCase();
|
|
228
|
+
return tickers.some(
|
|
229
|
+
(ticker) => ticker.trim().toUpperCase() === normalizedSymbol
|
|
230
|
+
);
|
|
231
|
+
};
|
|
232
|
+
var buildSymbolSharedReplayKey = ({
|
|
233
|
+
userName,
|
|
234
|
+
connectorName,
|
|
235
|
+
interval,
|
|
236
|
+
symbol,
|
|
237
|
+
start,
|
|
238
|
+
end
|
|
239
|
+
}) => ["replay", userName, connectorName, symbol, interval, start, end].join(":");
|
|
240
|
+
var resolveHistoricalReplayIntervals = (baseInterval, strategyIntervals) => {
|
|
241
|
+
const intervals = [
|
|
242
|
+
...new Set(
|
|
243
|
+
strategyIntervals.map((value) => {
|
|
244
|
+
if (!value || !Number.isFinite((0, import_data.intervalToMs)(value)) || (0, import_data.intervalToMs)(value) <= 0)
|
|
245
|
+
throw new Error(`Invalid replay strategy interval: ${value}`);
|
|
246
|
+
return value;
|
|
247
|
+
})
|
|
248
|
+
)
|
|
249
|
+
].sort((a, b) => (0, import_data.intervalToMs)(a) - (0, import_data.intervalToMs)(b));
|
|
250
|
+
if (intervals.length > 1 && intervals.includes("M")) {
|
|
251
|
+
throw new Error("Mixed replay does not support calendar intervals");
|
|
252
|
+
}
|
|
253
|
+
const baseMs = (0, import_data.intervalToMs)(baseInterval);
|
|
254
|
+
if (intervals.some(
|
|
255
|
+
(value) => (0, import_data.intervalToMs)(value) < baseMs || (0, import_data.intervalToMs)(value) % baseMs !== 0
|
|
256
|
+
)) {
|
|
257
|
+
throw new Error(
|
|
258
|
+
"Replay strategy intervals must be multiples of the base timeframe"
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
if (!intervals.length || !intervals.includes(baseInterval)) {
|
|
262
|
+
throw new Error(
|
|
263
|
+
"Replay base timeframe must match an active strategy interval"
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
return intervals;
|
|
267
|
+
};
|
|
268
|
+
var prepareMultiIntervalHistoricalReplay = async (context, adapters, loadReferences) => {
|
|
269
|
+
const intervals = resolveHistoricalReplayIntervals(
|
|
270
|
+
context.interval,
|
|
271
|
+
context.strategies.map((s) => s.strategyConfig.INTERVAL)
|
|
272
|
+
);
|
|
273
|
+
const baseMs = (0, import_data.intervalToMs)(context.interval);
|
|
274
|
+
const plan = {
|
|
275
|
+
symbolRuntimes: [],
|
|
276
|
+
orderedTimestamps: [],
|
|
277
|
+
sharedReplayKeyPrefixes: [],
|
|
278
|
+
runtimeLineages: []
|
|
279
|
+
};
|
|
280
|
+
const timestamps = /* @__PURE__ */ new Set();
|
|
281
|
+
const baseRuntimes = /* @__PURE__ */ new Map();
|
|
282
|
+
const validatedSymbols = /* @__PURE__ */ new Set();
|
|
283
|
+
try {
|
|
284
|
+
for (const interval of intervals) {
|
|
285
|
+
const offset = (0, import_data.intervalToMs)(interval) - baseMs;
|
|
286
|
+
const nativeRun = {
|
|
287
|
+
...context.preparedRun,
|
|
288
|
+
preloadStart: context.preparedRun.preloadStart - offset,
|
|
289
|
+
window: {
|
|
290
|
+
...context.preparedRun.window,
|
|
291
|
+
start: context.preparedRun.window.start - offset,
|
|
292
|
+
end: context.preparedRun.window.end - offset
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
const part = await prepareHistoricalReplay(
|
|
296
|
+
{
|
|
297
|
+
...context,
|
|
298
|
+
interval,
|
|
299
|
+
preparedRun: nativeRun,
|
|
300
|
+
strategies: context.strategies.filter(
|
|
301
|
+
(strategy) => strategy.strategyConfig.INTERVAL === interval
|
|
302
|
+
),
|
|
303
|
+
references: interval === context.interval ? context.references : await loadReferences(interval, nativeRun)
|
|
304
|
+
},
|
|
305
|
+
adapters
|
|
306
|
+
);
|
|
307
|
+
plan.sharedReplayKeyPrefixes.push(...part.sharedReplayKeyPrefixes);
|
|
308
|
+
plan.runtimeLineages.push(...part.runtimeLineages);
|
|
309
|
+
for (const runtime of part.symbolRuntimes) {
|
|
310
|
+
if (interval === context.interval) {
|
|
311
|
+
baseRuntimes.set(runtime.symbol, runtime);
|
|
312
|
+
} else if (runtime.strategies.length && !validatedSymbols.has(runtime.symbol)) {
|
|
313
|
+
assertBaseReplayCoverage(
|
|
314
|
+
baseRuntimes.get(runtime.symbol),
|
|
315
|
+
runtime.symbol,
|
|
316
|
+
context.preparedRun.window,
|
|
317
|
+
baseMs
|
|
318
|
+
);
|
|
319
|
+
validatedSymbols.add(runtime.symbol);
|
|
320
|
+
}
|
|
321
|
+
runtime.timestampOffsetMs = offset;
|
|
322
|
+
runtime.advanceMarket = interval === context.interval;
|
|
323
|
+
plan.symbolRuntimes.push(runtime);
|
|
324
|
+
}
|
|
325
|
+
for (const timestamp of part.orderedTimestamps) {
|
|
326
|
+
const scheduled = timestamp + offset;
|
|
327
|
+
if (scheduled >= context.preparedRun.window.start && scheduled <= context.preparedRun.window.end)
|
|
328
|
+
timestamps.add(scheduled);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
plan.orderedTimestamps = [...timestamps].sort((a, b) => a - b);
|
|
332
|
+
return plan;
|
|
333
|
+
} catch (error) {
|
|
334
|
+
releaseHistoricalReplayCaches(plan, adapters);
|
|
335
|
+
throw error;
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
var assertBaseReplayCoverage = (runtime, symbol, window, baseMs) => {
|
|
339
|
+
let index = 0;
|
|
340
|
+
for (let timestamp = Math.ceil(window.start / baseMs) * baseMs; timestamp <= window.end; timestamp += baseMs) {
|
|
341
|
+
if (runtime?.replayData[index]?.timestamp !== timestamp) {
|
|
342
|
+
throw new Error(`Missing base replay candle: ${symbol} at ${timestamp}`);
|
|
343
|
+
}
|
|
344
|
+
index += 1;
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
var prepareHistoricalReplay = async (context, adapters) => {
|
|
348
|
+
const {
|
|
349
|
+
userName,
|
|
350
|
+
projectRoot,
|
|
351
|
+
preparedRun,
|
|
352
|
+
interval,
|
|
353
|
+
connectorName,
|
|
354
|
+
replayConnector,
|
|
355
|
+
strategies,
|
|
356
|
+
references
|
|
357
|
+
} = context;
|
|
358
|
+
const symbolRuntimes = [];
|
|
359
|
+
const orderedTimestamps = /* @__PURE__ */ new Set();
|
|
360
|
+
const sharedReplayKeyPrefixes = [];
|
|
361
|
+
const runtimeLineages = [];
|
|
362
|
+
let skippedSymbols = 0;
|
|
363
|
+
try {
|
|
364
|
+
for (const symbol of preparedRun.tickers) {
|
|
365
|
+
const data = await preparedRun.marketConnector.kline({
|
|
366
|
+
symbol,
|
|
367
|
+
start: preparedRun.preloadStart,
|
|
368
|
+
end: preparedRun.window.end,
|
|
369
|
+
cacheOnly: true,
|
|
370
|
+
interval
|
|
371
|
+
});
|
|
372
|
+
if (symbolRuntimes.length % 50 === 0)
|
|
373
|
+
adapters.sampleMemory?.("history:loaded", {
|
|
374
|
+
symbols: symbolRuntimes.length + 1,
|
|
375
|
+
candles: data.length
|
|
376
|
+
});
|
|
377
|
+
const preparedData = buildPreparedData({
|
|
378
|
+
data,
|
|
379
|
+
btcData: references.btcMarketData,
|
|
380
|
+
ethData: references.ethMarketData,
|
|
381
|
+
btcBinanceData: references.btcBinanceData,
|
|
382
|
+
btcCoinbaseData: references.btcCoinbaseData,
|
|
383
|
+
start: preparedRun.window.start,
|
|
384
|
+
preloadStart: preparedRun.preloadStart
|
|
385
|
+
});
|
|
386
|
+
if (!preparedData.replayData.length || !preparedData.btcReplayData.length) {
|
|
387
|
+
skippedSymbols += 1;
|
|
388
|
+
adapters.progress.tick({
|
|
389
|
+
skipped: adapters.display.skipped(skippedSymbols),
|
|
390
|
+
symbol: adapters.display.symbol(symbol)
|
|
391
|
+
});
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
const sharedIndicatorsReplayKey = buildSymbolSharedReplayKey({
|
|
395
|
+
userName,
|
|
396
|
+
connectorName,
|
|
397
|
+
interval,
|
|
398
|
+
symbol,
|
|
399
|
+
start: preparedRun.window.start,
|
|
400
|
+
end: preparedRun.window.end
|
|
401
|
+
});
|
|
402
|
+
sharedReplayKeyPrefixes.push(sharedIndicatorsReplayKey);
|
|
403
|
+
const initializedStrategies = await Promise.allSettled(
|
|
404
|
+
strategies.filter((strategy) => strategyIncludesSymbol(strategy, symbol)).map(async (strategy) => {
|
|
405
|
+
const replayStrategyConfig = buildReplayStrategyConfig({
|
|
406
|
+
strategyConfig: strategy.strategyConfig,
|
|
407
|
+
interval
|
|
408
|
+
});
|
|
409
|
+
const runtimeLineage = await adapters.buildLineage({
|
|
410
|
+
projectRoot,
|
|
411
|
+
strategyName: strategy.strategyName,
|
|
412
|
+
strategyRevision: strategy.strategyRevision,
|
|
413
|
+
deploymentCompositionId: strategy.deploymentCompositionId,
|
|
414
|
+
strategyPackageVersion: strategy.strategyPackageVersion,
|
|
415
|
+
strategyDependencyVersions: strategy.strategyDependencyVersions,
|
|
416
|
+
runtimePackageVersion: strategy.runtimePackageVersion,
|
|
417
|
+
config: {
|
|
418
|
+
configId: strategy.strategyRevision,
|
|
419
|
+
strategyConfig: strategy.strategyConfig,
|
|
420
|
+
symbolResultConfig: strategy.strategyResults?.[symbol]?.config ?? null
|
|
421
|
+
},
|
|
422
|
+
runContext: {
|
|
423
|
+
connectorName: connectorName.toLowerCase(),
|
|
424
|
+
interval: String(interval),
|
|
425
|
+
universe: preparedRun.universe ?? null
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
runtimeLineages.push({
|
|
429
|
+
strategy: strategy.strategyName,
|
|
430
|
+
symbol,
|
|
431
|
+
...preparedRun.deploymentId ? { deploymentId: preparedRun.deploymentId } : {},
|
|
432
|
+
...preparedRun.accountId ? { accountId: preparedRun.accountId } : {},
|
|
433
|
+
lineage: runtimeLineage
|
|
434
|
+
});
|
|
435
|
+
return {
|
|
436
|
+
strategyName: strategy.strategyName,
|
|
437
|
+
strategyConfig: strategy.strategyConfig,
|
|
438
|
+
runtimeLineage,
|
|
439
|
+
run: await strategy.strategyCreator({
|
|
440
|
+
userName,
|
|
441
|
+
connectorName,
|
|
442
|
+
runtimeConfigId: strategy.strategyRevision,
|
|
443
|
+
strategyRevision: strategy.strategyRevision,
|
|
444
|
+
runtimeLineage,
|
|
445
|
+
universe: preparedRun.universe,
|
|
446
|
+
accountId: preparedRun.accountId,
|
|
447
|
+
deploymentId: preparedRun.deploymentId,
|
|
448
|
+
runtimeConfigSnapshot: {
|
|
449
|
+
userConfig: replayStrategyConfig
|
|
450
|
+
},
|
|
451
|
+
config: replayStrategyConfig,
|
|
452
|
+
symbol,
|
|
453
|
+
data: preparedData.prevData,
|
|
454
|
+
btcData: preparedData.btcPrevData,
|
|
455
|
+
ethData: preparedData.ethPrevData,
|
|
456
|
+
btcBinanceData: preparedData.btcBinancePrevData,
|
|
457
|
+
btcCoinbaseData: preparedData.btcCoinbasePrevData,
|
|
458
|
+
connector: replayConnector,
|
|
459
|
+
sharedIndicatorsReplayKey
|
|
460
|
+
})
|
|
461
|
+
};
|
|
462
|
+
})
|
|
463
|
+
);
|
|
464
|
+
const strategiesForSymbol = initializedStrategies.map((result) => {
|
|
465
|
+
if (result.status === "rejected") throw result.reason;
|
|
466
|
+
return result.value;
|
|
467
|
+
});
|
|
468
|
+
const symbolRuntime = {
|
|
469
|
+
symbol,
|
|
470
|
+
replayData: preparedData.replayData,
|
|
471
|
+
btcReplayData: preparedData.btcReplayData,
|
|
472
|
+
ethReplayData: preparedData.ethReplayData,
|
|
473
|
+
currentIndex: 0,
|
|
474
|
+
strategies: strategiesForSymbol
|
|
475
|
+
};
|
|
476
|
+
symbolRuntimes.push(symbolRuntime);
|
|
477
|
+
if (symbolRuntimes.length % 50 === 0 || symbol === preparedRun.tickers.at(-1))
|
|
478
|
+
adapters.sampleMemory?.("strategies:initializing", {
|
|
479
|
+
symbols: symbolRuntimes.length,
|
|
480
|
+
strategiesPerSymbol: strategiesForSymbol.length
|
|
481
|
+
});
|
|
482
|
+
adapters.progress.tick({
|
|
483
|
+
skipped: adapters.display.skipped(skippedSymbols),
|
|
484
|
+
symbol: adapters.display.symbol(symbol)
|
|
485
|
+
});
|
|
486
|
+
for (const candle of preparedData.replayData) {
|
|
487
|
+
orderedTimestamps.add(candle.timestamp);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return {
|
|
491
|
+
symbolRuntimes,
|
|
492
|
+
orderedTimestamps: [...orderedTimestamps].sort(
|
|
493
|
+
(left, right) => left - right
|
|
494
|
+
),
|
|
495
|
+
sharedReplayKeyPrefixes,
|
|
496
|
+
runtimeLineages
|
|
497
|
+
};
|
|
498
|
+
} catch (error) {
|
|
499
|
+
releaseHistoricalReplayCaches({ sharedReplayKeyPrefixes }, adapters);
|
|
500
|
+
throw error;
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
var releaseHistoricalReplayPlan = (plan) => {
|
|
504
|
+
plan.symbolRuntimes.length = 0;
|
|
505
|
+
plan.sharedReplayKeyPrefixes.length = 0;
|
|
506
|
+
};
|
|
507
|
+
var releaseHistoricalReplayCaches = (plan, adapters) => {
|
|
508
|
+
for (const keyPrefix of plan.sharedReplayKeyPrefixes) {
|
|
509
|
+
adapters.releaseIndicatorsCache(keyPrefix);
|
|
510
|
+
adapters.releaseReplayCache(keyPrefix);
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
// src/scripts/replayRunner.ts
|
|
42
515
|
var import_chalk13 = __toESM(require("chalk"));
|
|
43
516
|
var import_constants7 = require("@tradejs/core/constants");
|
|
44
|
-
var
|
|
517
|
+
var import_data5 = require("@tradejs/core/data");
|
|
45
518
|
var import_time7 = require("@tradejs/core/time");
|
|
46
519
|
var import_redis6 = require("@tradejs/infra/redis");
|
|
47
520
|
var import_candles = require("@tradejs/infra/timescale/candles");
|
|
@@ -74,7 +547,7 @@ var timeOperation = async (label, operation, log = console.log) => {
|
|
|
74
547
|
};
|
|
75
548
|
|
|
76
549
|
// src/lib/runEnvironment.ts
|
|
77
|
-
var
|
|
550
|
+
var import_chalk4 = __toESM(require("chalk"));
|
|
78
551
|
var import_connectors3 = require("@tradejs/node/connectors");
|
|
79
552
|
var import_cli3 = require("@tradejs/node/cli");
|
|
80
553
|
var import_constants = require("@tradejs/core/constants");
|
|
@@ -82,7 +555,7 @@ var import_time2 = require("@tradejs/core/time");
|
|
|
82
555
|
var import_runtimeStrategies = require("@tradejs/node/runtimeStrategies");
|
|
83
556
|
|
|
84
557
|
// src/lib/runtimeStrategyBacktest.ts
|
|
85
|
-
var
|
|
558
|
+
var import_chalk2 = __toESM(require("chalk"));
|
|
86
559
|
|
|
87
560
|
// src/lib/runtimeRedis.ts
|
|
88
561
|
var import_redis = require("@tradejs/infra/redis");
|
|
@@ -232,7 +705,7 @@ var resolveTimeWindow = ({
|
|
|
232
705
|
};
|
|
233
706
|
|
|
234
707
|
// src/lib/marketData/historyPrepare.ts
|
|
235
|
-
var
|
|
708
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
236
709
|
var import_connectors = require("@tradejs/connectors");
|
|
237
710
|
var import_cli = require("@tradejs/node/cli");
|
|
238
711
|
var import_connectors2 = require("@tradejs/node/connectors");
|
|
@@ -323,7 +796,7 @@ var updatePrimaryMarketHistory = async ({
|
|
|
323
796
|
const timeOperation3 = (label, operation) => timeOperation(
|
|
324
797
|
label,
|
|
325
798
|
operation,
|
|
326
|
-
log ?? ((message) => console.log(
|
|
799
|
+
log ?? ((message) => console.log(import_chalk3.default.gray(message)))
|
|
327
800
|
);
|
|
328
801
|
const updateOptions = preloadStart != null || preloadEnd != null ? {
|
|
329
802
|
connectorLabel: connectorName,
|
|
@@ -355,7 +828,7 @@ var updateBtcReferenceHistory = async ({
|
|
|
355
828
|
const timeOperation3 = (label, operation) => timeOperation(
|
|
356
829
|
label,
|
|
357
830
|
operation,
|
|
358
|
-
log ?? ((message) => console.log(
|
|
831
|
+
log ?? ((message) => console.log(import_chalk3.default.gray(message)))
|
|
359
832
|
);
|
|
360
833
|
const updateReference = async ({
|
|
361
834
|
connector,
|
|
@@ -477,7 +950,7 @@ var loadRunTickers = async ({
|
|
|
477
950
|
var timeOperation2 = async (label, operation) => timeOperation(
|
|
478
951
|
label,
|
|
479
952
|
operation,
|
|
480
|
-
(message) => console.log(
|
|
953
|
+
(message) => console.log(import_chalk4.default.gray(message))
|
|
481
954
|
);
|
|
482
955
|
var resolveBacktestExecutionPreloadInterval = (interval) => {
|
|
483
956
|
const normalized = String(interval);
|
|
@@ -498,7 +971,7 @@ var resolveRunConnectorName = async ({
|
|
|
498
971
|
return connectorName;
|
|
499
972
|
}
|
|
500
973
|
console.log(
|
|
501
|
-
|
|
974
|
+
import_chalk4.default.yellow(
|
|
502
975
|
`Unknown connector "${String(value || "").trim() || String(value)}". Fallback to ${import_connectors3.DEFAULT_CONNECTOR_NAME}.`
|
|
503
976
|
)
|
|
504
977
|
);
|
|
@@ -553,7 +1026,7 @@ var loadDeploymentReplayStrategies = async ({
|
|
|
553
1026
|
) : [];
|
|
554
1027
|
if (!strategies.length) {
|
|
555
1028
|
console.log(
|
|
556
|
-
|
|
1029
|
+
import_chalk4.default.yellow(
|
|
557
1030
|
`No enabled strategies found in runtime deployment ${deploymentId}`
|
|
558
1031
|
)
|
|
559
1032
|
);
|
|
@@ -619,7 +1092,7 @@ var prepareRunEnvironment = async ({
|
|
|
619
1092
|
);
|
|
620
1093
|
}
|
|
621
1094
|
if (showTickersList) {
|
|
622
|
-
console.log(
|
|
1095
|
+
console.log(import_chalk4.default.gray(JSON.stringify(loadedTickers.sort(), null, 2)));
|
|
623
1096
|
return null;
|
|
624
1097
|
}
|
|
625
1098
|
const instruments = !cacheOnly && typeof marketConnector.listInstruments === "function" ? await timeOperation2(
|
|
@@ -656,7 +1129,7 @@ var prepareRunEnvironment = async ({
|
|
|
656
1129
|
projectRoot,
|
|
657
1130
|
shouldUseDedicatedReferences: universe === "crypto",
|
|
658
1131
|
requireDedicatedReferences: universe === "crypto",
|
|
659
|
-
warn: (message) => console.log(
|
|
1132
|
+
warn: (message) => console.log(import_chalk4.default.yellow(message))
|
|
660
1133
|
});
|
|
661
1134
|
await updateMarketHistoryWithBtcReferences({
|
|
662
1135
|
marketConnector,
|
|
@@ -667,7 +1140,7 @@ var prepareRunEnvironment = async ({
|
|
|
667
1140
|
preloadStart,
|
|
668
1141
|
preloadEnd: window.end,
|
|
669
1142
|
universe,
|
|
670
|
-
log: (message) => console.log(
|
|
1143
|
+
log: (message) => console.log(import_chalk4.default.gray(message))
|
|
671
1144
|
});
|
|
672
1145
|
const backtestExecutionInterval = resolveBacktestExecutionPreloadInterval(interval);
|
|
673
1146
|
if (import_constants.BACKTEST_LOWER_TIMEFRAME_EXECUTION_ENABLED && backtestExecutionInterval && String(backtestExecutionInterval) !== String(interval)) {
|
|
@@ -704,13 +1177,13 @@ var prepareRunEnvironment = async ({
|
|
|
704
1177
|
};
|
|
705
1178
|
|
|
706
1179
|
// src/lib/marketContextPrepare.ts
|
|
707
|
-
var
|
|
1180
|
+
var import_chalk8 = __toESM(require("chalk"));
|
|
708
1181
|
var import_marketContext3 = require("@tradejs/infra/timescale/marketContext");
|
|
709
1182
|
var import_registry = require("@tradejs/node/registry");
|
|
710
1183
|
|
|
711
1184
|
// src/lib/binanceMarketContextBackfill.ts
|
|
712
1185
|
var import_progress = __toESM(require("progress"));
|
|
713
|
-
var
|
|
1186
|
+
var import_chalk5 = __toESM(require("chalk"));
|
|
714
1187
|
var import_connectors4 = require("@tradejs/connectors");
|
|
715
1188
|
var import_connectors5 = require("@tradejs/node/connectors");
|
|
716
1189
|
var import_strategies = require("@tradejs/node/strategies");
|
|
@@ -1264,7 +1737,7 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
1264
1737
|
let tradeFlowRows = 0;
|
|
1265
1738
|
let breadthRows = 0;
|
|
1266
1739
|
console.log(
|
|
1267
|
-
|
|
1740
|
+
import_chalk5.default.cyan(
|
|
1268
1741
|
`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()}`
|
|
1269
1742
|
)
|
|
1270
1743
|
);
|
|
@@ -1400,7 +1873,7 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
1400
1873
|
}) : [];
|
|
1401
1874
|
if (!missingChunks2.length) {
|
|
1402
1875
|
console.log(
|
|
1403
|
-
|
|
1876
|
+
import_chalk5.default.gray(`breadth cached: universe=${definition.universe}`)
|
|
1404
1877
|
);
|
|
1405
1878
|
}
|
|
1406
1879
|
return {
|
|
@@ -1488,7 +1961,7 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
1488
1961
|
}
|
|
1489
1962
|
}
|
|
1490
1963
|
console.log(
|
|
1491
|
-
|
|
1964
|
+
import_chalk5.default.green(
|
|
1492
1965
|
`binance market context backfill done: tradeFlowRows=${tradeFlowRows}, depthRows=0, breadthRows=${breadthRows}, skippedSymbols=${skippedSymbols}`
|
|
1493
1966
|
)
|
|
1494
1967
|
);
|
|
@@ -1505,11 +1978,11 @@ var backfillBinanceMarketContextForSignals = (params) => backfillBinanceMarketCo
|
|
|
1505
1978
|
var backfillBinanceMarketContextForReplay = (params) => backfillBinanceMarketContext(params, true);
|
|
1506
1979
|
|
|
1507
1980
|
// src/lib/derivativesContextBackfill.ts
|
|
1508
|
-
var
|
|
1981
|
+
var import_chalk6 = __toESM(require("chalk"));
|
|
1509
1982
|
var import_progress2 = __toESM(require("progress"));
|
|
1510
1983
|
var import_async = require("@tradejs/core/async");
|
|
1511
1984
|
var import_constants2 = require("@tradejs/core/constants");
|
|
1512
|
-
var
|
|
1985
|
+
var import_indicators2 = require("@tradejs/core/indicators");
|
|
1513
1986
|
var import_derivatives = require("@tradejs/infra/timescale/derivatives");
|
|
1514
1987
|
var import_client2 = require("@tradejs/infra/timescale/client");
|
|
1515
1988
|
var import_userSettings = require("@tradejs/infra/userSettings");
|
|
@@ -1612,7 +2085,7 @@ var resolveDerivativesContextBackfillWindow = (params) => {
|
|
|
1612
2085
|
};
|
|
1613
2086
|
var resolveDerivativesContextIntervalBackfillWindow = (params) => {
|
|
1614
2087
|
const intervalMs = derivativesIntervalMs(params.interval);
|
|
1615
|
-
const alignedToMs = params.closedOnly ? (0,
|
|
2088
|
+
const alignedToMs = params.closedOnly ? (0, import_indicators2.getLastClosedDerivativesBarStartMs)(params.toMs, params.interval) : Math.floor(params.toMs / intervalMs) * intervalMs;
|
|
1616
2089
|
return {
|
|
1617
2090
|
fromMs: Math.floor(params.fromMs / intervalMs) * intervalMs,
|
|
1618
2091
|
toMs: alignedToMs,
|
|
@@ -1649,7 +2122,7 @@ var resolveDerivativesContextRequiredFetchFromMs = (params) => {
|
|
|
1649
2122
|
refreshClosedTail: params.mode === "signals"
|
|
1650
2123
|
});
|
|
1651
2124
|
const dataFetchFromMs = dataCoverageFromMs != null && edgesFromMs != null ? Math.max(dataCoverageFromMs, edgesFromMs) : null;
|
|
1652
|
-
const confirmedLiquidationWindow = (0,
|
|
2125
|
+
const confirmedLiquidationWindow = (0, import_indicators2.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
1653
2126
|
interval: params.interval,
|
|
1654
2127
|
fromMs: params.fromMs,
|
|
1655
2128
|
toMs: params.toMs,
|
|
@@ -2040,7 +2513,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2040
2513
|
const coverageNowMs = Date.now();
|
|
2041
2514
|
await Promise.all(
|
|
2042
2515
|
intervalWindows.map(async (window) => {
|
|
2043
|
-
const confirmedLiquidationWindow = (0,
|
|
2516
|
+
const confirmedLiquidationWindow = (0, import_indicators2.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
2044
2517
|
interval: window.interval,
|
|
2045
2518
|
fromMs: window.fromMs,
|
|
2046
2519
|
toMs: window.toMs,
|
|
@@ -2136,7 +2609,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2136
2609
|
toMs: backfillWindow.toMs
|
|
2137
2610
|
})
|
|
2138
2611
|
);
|
|
2139
|
-
const confirmedLiquidationWindow = (0,
|
|
2612
|
+
const confirmedLiquidationWindow = (0, import_indicators2.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
2140
2613
|
interval: window.interval,
|
|
2141
2614
|
fromMs: backfillWindow.fromMs,
|
|
2142
2615
|
toMs: backfillWindow.toMs,
|
|
@@ -2154,7 +2627,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2154
2627
|
});
|
|
2155
2628
|
if (allBackfillWindowsCached) {
|
|
2156
2629
|
console.log(
|
|
2157
|
-
|
|
2630
|
+
import_chalk6.default.gray(
|
|
2158
2631
|
`derivatives context backfill: cached symbols=${symbols.length}, requestedSymbols=${requestedSymbols.length}, intervals=${intervals.join(",")}, window=${new Date(fromMs).toISOString()}..${new Date(safeEndMs).toISOString()}`
|
|
2159
2632
|
)
|
|
2160
2633
|
);
|
|
@@ -2219,7 +2692,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2219
2692
|
let failedWindows = 0;
|
|
2220
2693
|
let skippedWindows = 0;
|
|
2221
2694
|
console.log(
|
|
2222
|
-
|
|
2695
|
+
import_chalk6.default.cyan(
|
|
2223
2696
|
`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()}`
|
|
2224
2697
|
)
|
|
2225
2698
|
);
|
|
@@ -2324,13 +2797,13 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2324
2797
|
});
|
|
2325
2798
|
rows = missingBatch.flatMap((item) => {
|
|
2326
2799
|
const marketSymbol = item.marketSymbol.toUpperCase();
|
|
2327
|
-
const points = (0,
|
|
2800
|
+
const points = (0, import_indicators2.mergeCoinalyzeMetrics)({
|
|
2328
2801
|
symbol: item.symbol,
|
|
2329
2802
|
oiRaw: oiMap.get(marketSymbol) ?? [],
|
|
2330
2803
|
fundingRaw: fundingMap.get(marketSymbol) ?? [],
|
|
2331
2804
|
liqRaw: liquidationRowsByMarket.get(marketSymbol) ?? []
|
|
2332
2805
|
});
|
|
2333
|
-
return (0,
|
|
2806
|
+
return (0, import_indicators2.coinalyzePointsToRows)(points, interval, "coinalyze");
|
|
2334
2807
|
});
|
|
2335
2808
|
missingClosedSymbols = mode === "signals" ? getMissingClosedDerivativesSymbols({
|
|
2336
2809
|
symbols: missingBatch.map((item) => item.symbol),
|
|
@@ -2351,7 +2824,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2351
2824
|
await (0, import_derivatives.upsertDerivatives)(rows);
|
|
2352
2825
|
totalRows += rows.length;
|
|
2353
2826
|
}
|
|
2354
|
-
const confirmedLiquidationWindow = (0,
|
|
2827
|
+
const confirmedLiquidationWindow = (0, import_indicators2.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
2355
2828
|
interval,
|
|
2356
2829
|
fromMs: group.fromMs,
|
|
2357
2830
|
toMs,
|
|
@@ -2361,7 +2834,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2361
2834
|
const metricCoverageRows = missingBatch.map((item) => {
|
|
2362
2835
|
const marketSymbol = item.marketSymbol.toUpperCase();
|
|
2363
2836
|
const eventRowsCount = (liquidationRowsByMarket.get(marketSymbol) ?? []).filter((point) => {
|
|
2364
|
-
const timestamp = (0,
|
|
2837
|
+
const timestamp = (0, import_indicators2.toCoinalyzeTimestampMs)(
|
|
2365
2838
|
point.t ?? point.ts ?? point.time ?? point.timestamp
|
|
2366
2839
|
);
|
|
2367
2840
|
return timestamp != null && timestamp >= confirmedLiquidationWindow.fromMs && timestamp <= confirmedLiquidationWindow.toMs;
|
|
@@ -2433,13 +2906,13 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2433
2906
|
} catch (error) {
|
|
2434
2907
|
failedWindows += 1;
|
|
2435
2908
|
console.error(
|
|
2436
|
-
|
|
2909
|
+
import_chalk6.default.red(
|
|
2437
2910
|
`derivatives context backfill window failed batch=${batchIdx + 1}/${symbolBatches.length} interval=${interval} ${new Date(cursor).toISOString()}..${new Date(toMs).toISOString()}: ${error}`
|
|
2438
2911
|
)
|
|
2439
2912
|
);
|
|
2440
2913
|
} finally {
|
|
2441
2914
|
bar.tick(1, {
|
|
2442
|
-
batch:
|
|
2915
|
+
batch: import_chalk6.default.gray(
|
|
2443
2916
|
`${batchIdx + 1}/${symbolBatches.length} ${interval}`
|
|
2444
2917
|
),
|
|
2445
2918
|
rows: totalRows,
|
|
@@ -2453,7 +2926,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
2453
2926
|
}
|
|
2454
2927
|
console.log("");
|
|
2455
2928
|
console.log(
|
|
2456
|
-
|
|
2929
|
+
import_chalk6.default.green(
|
|
2457
2930
|
`derivatives context backfill done: rows=${totalRows}, failed_windows=${failedWindows}, skipped_windows=${skippedWindows}`
|
|
2458
2931
|
)
|
|
2459
2932
|
);
|
|
@@ -2483,7 +2956,7 @@ var backfillDerivativesContextForSignals = async (params) => backfillDerivatives
|
|
|
2483
2956
|
);
|
|
2484
2957
|
|
|
2485
2958
|
// src/lib/coinMarketCapContextBackfill.ts
|
|
2486
|
-
var
|
|
2959
|
+
var import_chalk7 = __toESM(require("chalk"));
|
|
2487
2960
|
var import_async2 = require("@tradejs/core/async");
|
|
2488
2961
|
var import_marketContext2 = require("@tradejs/infra/timescale/marketContext");
|
|
2489
2962
|
var import_client3 = require("@tradejs/infra/timescale/client");
|
|
@@ -2745,7 +3218,7 @@ var formatRowTimeRange = (rows) => {
|
|
|
2745
3218
|
};
|
|
2746
3219
|
var logBackfillSourceDone = (params) => {
|
|
2747
3220
|
console.log(
|
|
2748
|
-
|
|
3221
|
+
import_chalk7.default.gray(
|
|
2749
3222
|
`coinmarketcap context source done: ${params.name}=${params.status} rows=${params.rows}${params.details ? ` ${params.details}` : ""}`
|
|
2750
3223
|
)
|
|
2751
3224
|
);
|
|
@@ -2779,7 +3252,7 @@ var coinMarketCapFetch = async (params) => {
|
|
|
2779
3252
|
recordCoinMarketCapFetchStats(params.stats, params.path, creditCount);
|
|
2780
3253
|
if (creditCount != null && isVerboseRequestLoggingEnabled()) {
|
|
2781
3254
|
console.log(
|
|
2782
|
-
|
|
3255
|
+
import_chalk7.default.gray(`coinmarketcap ${params.path}: credits=${creditCount}`)
|
|
2783
3256
|
);
|
|
2784
3257
|
}
|
|
2785
3258
|
return payload;
|
|
@@ -2824,7 +3297,7 @@ var fetchCoinMarketCapIndexRows = async (params) => {
|
|
|
2824
3297
|
);
|
|
2825
3298
|
if (chunk === 1 || chunk === totalChunks || chunk % 25 === 0) {
|
|
2826
3299
|
console.log(
|
|
2827
|
-
|
|
3300
|
+
import_chalk7.default.gray(
|
|
2828
3301
|
`coinmarketcap index backfill progress: ${params.index.slug} chunk=${chunk}/${totalChunks} rows=${rows.length} chunkRange=${new Date(cursorMs).toISOString()}..${new Date(chunkEndMs).toISOString()}`
|
|
2829
3302
|
)
|
|
2830
3303
|
);
|
|
@@ -3263,7 +3736,7 @@ var backfillCoinMarketCapContext = async (params) => {
|
|
|
3263
3736
|
covered: indexesBackfillCovered
|
|
3264
3737
|
});
|
|
3265
3738
|
console.log(
|
|
3266
|
-
|
|
3739
|
+
import_chalk7.default.gray(
|
|
3267
3740
|
[
|
|
3268
3741
|
"coinmarketcap context status:",
|
|
3269
3742
|
formatSourceStatus({
|
|
@@ -3298,7 +3771,7 @@ var backfillCoinMarketCapContext = async (params) => {
|
|
|
3298
3771
|
);
|
|
3299
3772
|
if (globalDailyReady && referencesDailyReady && exchangeLiquidityReady && fearGreedReady && indexesReady) {
|
|
3300
3773
|
console.log(
|
|
3301
|
-
|
|
3774
|
+
import_chalk7.default.gray(
|
|
3302
3775
|
`coinmarketcap context cached: ${new Date(fromMs).toISOString()}..${new Date(toMs).toISOString()}`
|
|
3303
3776
|
)
|
|
3304
3777
|
);
|
|
@@ -3325,7 +3798,7 @@ var backfillCoinMarketCapContext = async (params) => {
|
|
|
3325
3798
|
!indexesReady ? "indexes" : null
|
|
3326
3799
|
].filter((source) => source != null);
|
|
3327
3800
|
console.log(
|
|
3328
|
-
|
|
3801
|
+
import_chalk7.default.cyan(
|
|
3329
3802
|
`coinmarketcap context backfill: window=${new Date(fromMs).toISOString()}..${new Date(toMs).toISOString()} days=${Math.floor((toMs - fromMs) / DAY_MS3) + 1} fetch=${pendingSources.join(",")}`
|
|
3330
3803
|
)
|
|
3331
3804
|
);
|
|
@@ -3517,7 +3990,7 @@ var backfillCoinMarketCapContext = async (params) => {
|
|
|
3517
3990
|
});
|
|
3518
3991
|
}
|
|
3519
3992
|
console.log(
|
|
3520
|
-
|
|
3993
|
+
import_chalk7.default.green(
|
|
3521
3994
|
[
|
|
3522
3995
|
"coinmarketcap context backfill done:",
|
|
3523
3996
|
`global=${globalStatus} rows=${globalRows}`,
|
|
@@ -4129,7 +4602,7 @@ var buildDerivativesBackfillParams = (params) => ({
|
|
|
4129
4602
|
});
|
|
4130
4603
|
var prepareMarketContextForRun = async (params) => {
|
|
4131
4604
|
const log = params.log ?? ((message) => {
|
|
4132
|
-
console.log(
|
|
4605
|
+
console.log(import_chalk8.default.gray(message));
|
|
4133
4606
|
});
|
|
4134
4607
|
const timeOperation3 = (label, operation) => timeOperation(label, operation, log);
|
|
4135
4608
|
const requirements = await resolveMarketContextRunRequirements(params);
|
|
@@ -4580,174 +5053,64 @@ var buildRuntimeEvidenceLineageScopes = ({
|
|
|
4580
5053
|
(left, right) => left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.firstTimestamp - right.firstTimestamp
|
|
4581
5054
|
);
|
|
4582
5055
|
};
|
|
4583
|
-
var loadReplayRuntimeEvidenceSource = async ({
|
|
4584
|
-
filePath,
|
|
4585
|
-
projectRoot,
|
|
4586
|
-
expectedUserName,
|
|
4587
|
-
expectedWindow
|
|
4588
|
-
}) => {
|
|
4589
|
-
const { resolvedPath, runtime, userName, startTime, endTime, deployment } = await readRuntimeEvidenceArtifact({ filePath, projectRoot });
|
|
4590
|
-
if (!userName || userName !== expectedUserName) {
|
|
4591
|
-
throw new Error(
|
|
4592
|
-
`Runtime evidence user mismatch: expected=${expectedUserName}, actual=${userName || "missing"}`
|
|
4593
|
-
);
|
|
4594
|
-
}
|
|
4595
|
-
const endTimeMatches = endTime === expectedWindow.end || endTime === expectedWindow.end + 1;
|
|
4596
|
-
if (startTime !== expectedWindow.start || !endTimeMatches) {
|
|
4597
|
-
throw new Error(
|
|
4598
|
-
`Runtime evidence window mismatch: expected=${expectedWindow.start}..${expectedWindow.end}, actual=${startTime ?? "missing"}..${endTime ?? "missing"}`
|
|
4599
|
-
);
|
|
4600
|
-
}
|
|
4601
|
-
const trades = unwrapRows(runtime.trades, "trade");
|
|
4602
|
-
const signals = unwrapRows(runtime.signals, "signal");
|
|
4603
|
-
const evaluations = unwrapRows(
|
|
4604
|
-
runtime.evaluations,
|
|
4605
|
-
"evaluation"
|
|
4606
|
-
);
|
|
4607
|
-
const storedLineageScopes = unwrapRows(
|
|
4608
|
-
runtime.lineageScopes,
|
|
4609
|
-
"lineageScope"
|
|
4610
|
-
);
|
|
4611
|
-
assertRuntimeRowsMatchDeployment({
|
|
4612
|
-
label: "trade",
|
|
4613
|
-
rows: trades,
|
|
4614
|
-
deployment
|
|
4615
|
-
});
|
|
4616
|
-
assertRuntimeRowsMatchDeployment({
|
|
4617
|
-
label: "signal",
|
|
4618
|
-
rows: signals,
|
|
4619
|
-
deployment
|
|
4620
|
-
});
|
|
4621
|
-
assertRuntimeRowsMatchDeployment({
|
|
4622
|
-
label: "evaluation",
|
|
4623
|
-
rows: evaluations,
|
|
4624
|
-
deployment
|
|
4625
|
-
});
|
|
4626
|
-
assertRuntimeRowsMatchDeployment({
|
|
4627
|
-
label: "lineage scope",
|
|
4628
|
-
rows: storedLineageScopes,
|
|
4629
|
-
deployment
|
|
4630
|
-
});
|
|
4631
|
-
return {
|
|
4632
|
-
path: resolvedPath,
|
|
4633
|
-
userName,
|
|
4634
|
-
startTime,
|
|
4635
|
-
endTime,
|
|
4636
|
-
trades,
|
|
4637
|
-
signals,
|
|
4638
|
-
evaluations,
|
|
4639
|
-
lineageScopes: storedLineageScopes.length ? storedLineageScopes : buildRuntimeEvidenceLineageScopes({ signals, evaluations }),
|
|
4640
|
-
deployment
|
|
4641
|
-
};
|
|
4642
|
-
};
|
|
4643
|
-
|
|
4644
|
-
// src/lib/replay/support.ts
|
|
4645
|
-
var import_chalk8 = __toESM(require("chalk"));
|
|
4646
|
-
|
|
4647
|
-
// src/lib/runtimeModeConfig.ts
|
|
4648
|
-
var buildRuntimeModeStrategyConfig = ({
|
|
4649
|
-
strategyConfig,
|
|
4650
|
-
env,
|
|
4651
|
-
interval,
|
|
4652
|
-
makeOrders,
|
|
4653
|
-
recordRuntimeTrades,
|
|
4654
|
-
aiReplayAnalyses
|
|
4655
|
-
}) => ({
|
|
4656
|
-
...strategyConfig,
|
|
4657
|
-
ENV: env,
|
|
4658
|
-
MAKE_ORDERS: makeOrders,
|
|
4659
|
-
INTERVAL: strategyConfig.INTERVAL ?? interval,
|
|
4660
|
-
...typeof recordRuntimeTrades === "boolean" ? { RECORD_RUNTIME_TRADES: recordRuntimeTrades } : {},
|
|
4661
|
-
...aiReplayAnalyses?.length ? { AI_REPLAY_ANALYSES: aiReplayAnalyses } : {}
|
|
4662
|
-
});
|
|
4663
|
-
|
|
4664
|
-
// src/lib/replay/support.ts
|
|
4665
|
-
var REPLAY_RESULTS_BY_STRATEGY_HEADERS = [
|
|
4666
|
-
import_chalk8.default.blue("STRATEGY"),
|
|
4667
|
-
import_chalk8.default.yellow("TICKERS"),
|
|
4668
|
-
import_chalk8.default.yellow("TRADE TICKERS"),
|
|
4669
|
-
import_chalk8.default.cyan("ORDERS"),
|
|
4670
|
-
import_chalk8.default.cyan("WIN/LOSS (%)"),
|
|
4671
|
-
import_chalk8.default.cyan("PROFIT"),
|
|
4672
|
-
import_chalk8.default.cyan("AVG TRADE")
|
|
4673
|
-
];
|
|
4674
|
-
var REPLAY_RUNTIME_COMPARISON_HEADERS = [
|
|
4675
|
-
import_chalk8.default.blue("STRATEGY"),
|
|
4676
|
-
import_chalk8.default.cyan("BT ENTRIES"),
|
|
4677
|
-
import_chalk8.default.cyan("BT PNL"),
|
|
4678
|
-
import_chalk8.default.yellow("RT TRADES"),
|
|
4679
|
-
import_chalk8.default.yellow("RT PNL"),
|
|
4680
|
-
import_chalk8.default.green("MATCHED"),
|
|
4681
|
-
import_chalk8.default.red("ORDER FAILED"),
|
|
4682
|
-
import_chalk8.default.yellow("RT ONLY"),
|
|
4683
|
-
import_chalk8.default.magenta("BT ONLY"),
|
|
4684
|
-
import_chalk8.default.red("UNMATCHED")
|
|
4685
|
-
];
|
|
4686
|
-
var REPLAY_RESULTS_CONFIG = "replay";
|
|
4687
|
-
var REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS = 1;
|
|
4688
|
-
var REPLAY_RUNTIME_COMPARE_TOLERANCE_MS = REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS * 15 * 60 * 1e3;
|
|
4689
|
-
var formatReplayRuntimeCompareTolerance = () => `${REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS} ${REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS === 1 ? "bar" : "bars"}`;
|
|
4690
|
-
var getReplayRuntimeUnmatchedCount = (row) => row.orderFailed + row.runtimeOnly + row.backtestOnly;
|
|
4691
|
-
var buildReplayStrategyConfig = ({
|
|
4692
|
-
strategyConfig,
|
|
4693
|
-
interval
|
|
4694
|
-
}) => ({
|
|
4695
|
-
...buildRuntimeModeStrategyConfig({
|
|
4696
|
-
strategyConfig,
|
|
4697
|
-
env: "PARITY",
|
|
4698
|
-
interval,
|
|
4699
|
-
makeOrders: false,
|
|
4700
|
-
recordRuntimeTrades: false
|
|
4701
|
-
}),
|
|
4702
|
-
SIMULATE_ORDERS: true
|
|
4703
|
-
});
|
|
4704
|
-
var toRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
4705
|
-
var compactReplayAdditionalIndicators = (additionalIndicators) => {
|
|
4706
|
-
const additional = toRecord(additionalIndicators);
|
|
4707
|
-
if (!additional) {
|
|
4708
|
-
return void 0;
|
|
4709
|
-
}
|
|
4710
|
-
const compact = {};
|
|
4711
|
-
if (additional.executionSlippage != null) {
|
|
4712
|
-
compact.executionSlippage = additional.executionSlippage;
|
|
4713
|
-
}
|
|
4714
|
-
if (additional.marketContext != null) {
|
|
4715
|
-
compact.marketContext = additional.marketContext;
|
|
4716
|
-
}
|
|
4717
|
-
const baseContext = toRecord(additional.baseContext);
|
|
4718
|
-
const relative = toRecord(baseContext?.relative);
|
|
4719
|
-
const execution = toRecord(relative?.execution);
|
|
4720
|
-
if (execution) {
|
|
4721
|
-
compact.baseContext = {
|
|
4722
|
-
relative: {
|
|
4723
|
-
execution
|
|
4724
|
-
}
|
|
4725
|
-
};
|
|
4726
|
-
}
|
|
4727
|
-
return Object.keys(compact).length ? compact : void 0;
|
|
4728
|
-
};
|
|
4729
|
-
var compactReplaySignal = (signal) => {
|
|
4730
|
-
if (!signal) {
|
|
4731
|
-
return void 0;
|
|
5056
|
+
var loadReplayRuntimeEvidenceSource = async ({
|
|
5057
|
+
filePath,
|
|
5058
|
+
projectRoot,
|
|
5059
|
+
expectedUserName,
|
|
5060
|
+
expectedWindow
|
|
5061
|
+
}) => {
|
|
5062
|
+
const { resolvedPath, runtime, userName, startTime, endTime, deployment } = await readRuntimeEvidenceArtifact({ filePath, projectRoot });
|
|
5063
|
+
if (!userName || userName !== expectedUserName) {
|
|
5064
|
+
throw new Error(
|
|
5065
|
+
`Runtime evidence user mismatch: expected=${expectedUserName}, actual=${userName || "missing"}`
|
|
5066
|
+
);
|
|
5067
|
+
}
|
|
5068
|
+
const endTimeMatches = endTime === expectedWindow.end || endTime === expectedWindow.end + 1;
|
|
5069
|
+
if (startTime !== expectedWindow.start || !endTimeMatches) {
|
|
5070
|
+
throw new Error(
|
|
5071
|
+
`Runtime evidence window mismatch: expected=${expectedWindow.start}..${expectedWindow.end}, actual=${startTime ?? "missing"}..${endTime ?? "missing"}`
|
|
5072
|
+
);
|
|
4732
5073
|
}
|
|
5074
|
+
const trades = unwrapRows(runtime.trades, "trade");
|
|
5075
|
+
const signals = unwrapRows(runtime.signals, "signal");
|
|
5076
|
+
const evaluations = unwrapRows(
|
|
5077
|
+
runtime.evaluations,
|
|
5078
|
+
"evaluation"
|
|
5079
|
+
);
|
|
5080
|
+
const storedLineageScopes = unwrapRows(
|
|
5081
|
+
runtime.lineageScopes,
|
|
5082
|
+
"lineageScope"
|
|
5083
|
+
);
|
|
5084
|
+
assertRuntimeRowsMatchDeployment({
|
|
5085
|
+
label: "trade",
|
|
5086
|
+
rows: trades,
|
|
5087
|
+
deployment
|
|
5088
|
+
});
|
|
5089
|
+
assertRuntimeRowsMatchDeployment({
|
|
5090
|
+
label: "signal",
|
|
5091
|
+
rows: signals,
|
|
5092
|
+
deployment
|
|
5093
|
+
});
|
|
5094
|
+
assertRuntimeRowsMatchDeployment({
|
|
5095
|
+
label: "evaluation",
|
|
5096
|
+
rows: evaluations,
|
|
5097
|
+
deployment
|
|
5098
|
+
});
|
|
5099
|
+
assertRuntimeRowsMatchDeployment({
|
|
5100
|
+
label: "lineage scope",
|
|
5101
|
+
rows: storedLineageScopes,
|
|
5102
|
+
deployment
|
|
5103
|
+
});
|
|
4733
5104
|
return {
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
additionalIndicators: compactReplayAdditionalIndicators(
|
|
4744
|
-
signal.additionalIndicators
|
|
4745
|
-
),
|
|
4746
|
-
isConfigFromBacktest: signal.isConfigFromBacktest,
|
|
4747
|
-
...signal.orderStatus ? { orderStatus: signal.orderStatus } : {},
|
|
4748
|
-
...signal.orderSkipReason ? { orderSkipReason: signal.orderSkipReason } : {},
|
|
4749
|
-
...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {},
|
|
4750
|
-
...signal.ml ? { ml: signal.ml } : {}
|
|
5105
|
+
path: resolvedPath,
|
|
5106
|
+
userName,
|
|
5107
|
+
startTime,
|
|
5108
|
+
endTime,
|
|
5109
|
+
trades,
|
|
5110
|
+
signals,
|
|
5111
|
+
evaluations,
|
|
5112
|
+
lineageScopes: storedLineageScopes.length ? storedLineageScopes : buildRuntimeEvidenceLineageScopes({ signals, evaluations }),
|
|
5113
|
+
deployment
|
|
4751
5114
|
};
|
|
4752
5115
|
};
|
|
4753
5116
|
|
|
@@ -4816,6 +5179,7 @@ var getPositionPnlSnapshot = ({
|
|
|
4816
5179
|
var createPortfolioReplayConnector = (connector) => {
|
|
4817
5180
|
let state = {};
|
|
4818
5181
|
let amount = import_constants3.INITIAL_BACKTEST_AMOUNT;
|
|
5182
|
+
let executionTimestamp = null;
|
|
4819
5183
|
const positionsBySymbol = /* @__PURE__ */ new Map();
|
|
4820
5184
|
const currentPriceBySymbol = /* @__PURE__ */ new Map();
|
|
4821
5185
|
const orderLog = [];
|
|
@@ -5077,6 +5441,7 @@ var createPortfolioReplayConnector = (connector) => {
|
|
|
5077
5441
|
});
|
|
5078
5442
|
const nextPosition = {
|
|
5079
5443
|
...order,
|
|
5444
|
+
timestamp: executionTimestamp ?? order.timestamp,
|
|
5080
5445
|
price: entryPrice,
|
|
5081
5446
|
signal: compactReplaySignal(order.signal),
|
|
5082
5447
|
amount,
|
|
@@ -5101,6 +5466,7 @@ var createPortfolioReplayConnector = (connector) => {
|
|
|
5101
5466
|
positionState,
|
|
5102
5467
|
data: {
|
|
5103
5468
|
...order,
|
|
5469
|
+
timestamp: executionTimestamp ?? order.timestamp,
|
|
5104
5470
|
price: entryPrice,
|
|
5105
5471
|
profit,
|
|
5106
5472
|
fee,
|
|
@@ -5164,6 +5530,7 @@ var createPortfolioReplayConnector = (connector) => {
|
|
|
5164
5530
|
positionState,
|
|
5165
5531
|
data: {
|
|
5166
5532
|
...order,
|
|
5533
|
+
timestamp: executionTimestamp ?? order.timestamp,
|
|
5167
5534
|
price: executionPrice,
|
|
5168
5535
|
qty: position.qty,
|
|
5169
5536
|
profit,
|
|
@@ -5172,11 +5539,12 @@ var createPortfolioReplayConnector = (connector) => {
|
|
|
5172
5539
|
...getExecutionSlippageLogData(slippageBreakdown, "exit")
|
|
5173
5540
|
}
|
|
5174
5541
|
});
|
|
5175
|
-
clearPosition(order.symbol, order.timestamp);
|
|
5542
|
+
clearPosition(order.symbol, executionTimestamp ?? order.timestamp);
|
|
5176
5543
|
return true;
|
|
5177
5544
|
},
|
|
5178
5545
|
getTickers: connector.getTickers,
|
|
5179
5546
|
advanceMarket: async ({ symbol, candle }) => {
|
|
5547
|
+
executionTimestamp = candle.timestamp;
|
|
5180
5548
|
currentPriceBySymbol.set(symbol, candle.close);
|
|
5181
5549
|
await checkSl({ symbol, candle });
|
|
5182
5550
|
await checkTp({ symbol, candle });
|
|
@@ -5217,244 +5585,8 @@ var invokeAfterSignalsHooks = async (hooks, params) => {
|
|
|
5217
5585
|
}
|
|
5218
5586
|
};
|
|
5219
5587
|
|
|
5220
|
-
// src/lib/marketData/windows.ts
|
|
5221
|
-
var import_indicators2 = require("@tradejs/core/indicators");
|
|
5222
|
-
var splitCandlesForReplayWindow = (candles, start, preloadStart) => {
|
|
5223
|
-
const prevData = [];
|
|
5224
|
-
const replayData = [];
|
|
5225
|
-
for (const candle of candles) {
|
|
5226
|
-
if (candle.timestamp < preloadStart) {
|
|
5227
|
-
continue;
|
|
5228
|
-
}
|
|
5229
|
-
if (candle.timestamp < start) {
|
|
5230
|
-
prevData.push(candle);
|
|
5231
|
-
continue;
|
|
5232
|
-
}
|
|
5233
|
-
replayData.push(candle);
|
|
5234
|
-
}
|
|
5235
|
-
return { prevData, replayData };
|
|
5236
|
-
};
|
|
5237
|
-
var alignSymbolWithBtcReference = (symbolCandles, btcCandles) => (0, import_indicators2.alignSortedCandlesByTimestamp)(symbolCandles, btcCandles);
|
|
5238
|
-
|
|
5239
|
-
// src/lib/replay/historicalSignalsReplayPreparation.ts
|
|
5240
|
-
var buildPreparedData = ({
|
|
5241
|
-
data,
|
|
5242
|
-
btcData,
|
|
5243
|
-
ethData,
|
|
5244
|
-
btcBinanceData,
|
|
5245
|
-
btcCoinbaseData,
|
|
5246
|
-
start,
|
|
5247
|
-
preloadStart
|
|
5248
|
-
}) => {
|
|
5249
|
-
const { prevData: prevDataRaw, replayData: replayDataRaw } = splitCandlesForReplayWindow(data, start, preloadStart);
|
|
5250
|
-
const { prevData: btcPrevDataRaw, replayData: btcReplayDataRaw } = splitCandlesForReplayWindow(btcData, start, preloadStart);
|
|
5251
|
-
const { prevData: ethPrevDataRaw, replayData: ethReplayDataRaw } = splitCandlesForReplayWindow(ethData, start, preloadStart);
|
|
5252
|
-
const { prevData: btcBinancePrevDataRaw } = splitCandlesForReplayWindow(
|
|
5253
|
-
btcBinanceData,
|
|
5254
|
-
start,
|
|
5255
|
-
preloadStart
|
|
5256
|
-
);
|
|
5257
|
-
const { prevData: btcCoinbasePrevDataRaw } = splitCandlesForReplayWindow(
|
|
5258
|
-
btcCoinbaseData,
|
|
5259
|
-
start,
|
|
5260
|
-
preloadStart
|
|
5261
|
-
);
|
|
5262
|
-
const { alignedCoinCandles: prevData, alignedBtcCandles: btcPrevData } = alignSymbolWithBtcReference(prevDataRaw, btcPrevDataRaw);
|
|
5263
|
-
const { alignedCoinCandles: replayData, alignedBtcCandles: btcReplayData } = alignSymbolWithBtcReference(replayDataRaw, btcReplayDataRaw);
|
|
5264
|
-
const { alignedBtcCandles: ethPrevData } = alignSymbolWithBtcReference(
|
|
5265
|
-
prevData,
|
|
5266
|
-
ethPrevDataRaw
|
|
5267
|
-
);
|
|
5268
|
-
const { alignedBtcCandles: ethReplayData } = alignSymbolWithBtcReference(
|
|
5269
|
-
replayData,
|
|
5270
|
-
ethReplayDataRaw
|
|
5271
|
-
);
|
|
5272
|
-
const { alignedBtcCandles: btcBinancePrevData } = alignSymbolWithBtcReference(
|
|
5273
|
-
prevDataRaw,
|
|
5274
|
-
btcBinancePrevDataRaw
|
|
5275
|
-
);
|
|
5276
|
-
const { alignedBtcCandles: btcCoinbasePrevData } = alignSymbolWithBtcReference(prevDataRaw, btcCoinbasePrevDataRaw);
|
|
5277
|
-
return {
|
|
5278
|
-
prevData,
|
|
5279
|
-
btcPrevData,
|
|
5280
|
-
ethPrevData,
|
|
5281
|
-
replayData,
|
|
5282
|
-
btcReplayData,
|
|
5283
|
-
ethReplayData,
|
|
5284
|
-
btcBinancePrevData,
|
|
5285
|
-
btcCoinbasePrevData
|
|
5286
|
-
};
|
|
5287
|
-
};
|
|
5288
|
-
var strategyIncludesSymbol = (strategy, symbol) => {
|
|
5289
|
-
const tickers = strategy.selection?.tickers;
|
|
5290
|
-
if (!tickers) return true;
|
|
5291
|
-
const normalizedSymbol = symbol.trim().toUpperCase();
|
|
5292
|
-
return tickers.some(
|
|
5293
|
-
(ticker) => ticker.trim().toUpperCase() === normalizedSymbol
|
|
5294
|
-
);
|
|
5295
|
-
};
|
|
5296
|
-
var buildSymbolSharedReplayKey = ({
|
|
5297
|
-
userName,
|
|
5298
|
-
connectorName,
|
|
5299
|
-
interval,
|
|
5300
|
-
symbol,
|
|
5301
|
-
start,
|
|
5302
|
-
end
|
|
5303
|
-
}) => ["replay", userName, connectorName, symbol, interval, start, end].join(":");
|
|
5304
|
-
var prepareHistoricalReplay = async (context, adapters) => {
|
|
5305
|
-
const {
|
|
5306
|
-
userName,
|
|
5307
|
-
projectRoot,
|
|
5308
|
-
preparedRun,
|
|
5309
|
-
interval,
|
|
5310
|
-
connectorName,
|
|
5311
|
-
replayConnector,
|
|
5312
|
-
strategies,
|
|
5313
|
-
references
|
|
5314
|
-
} = context;
|
|
5315
|
-
const symbolRuntimes = [];
|
|
5316
|
-
const orderedTimestamps = /* @__PURE__ */ new Set();
|
|
5317
|
-
const sharedReplayKeyPrefixes = [];
|
|
5318
|
-
const runtimeLineages = [];
|
|
5319
|
-
let skippedSymbols = 0;
|
|
5320
|
-
for (const symbol of preparedRun.tickers) {
|
|
5321
|
-
const data = await preparedRun.marketConnector.kline({
|
|
5322
|
-
symbol,
|
|
5323
|
-
start: preparedRun.preloadStart,
|
|
5324
|
-
end: preparedRun.window.end,
|
|
5325
|
-
cacheOnly: true,
|
|
5326
|
-
interval
|
|
5327
|
-
});
|
|
5328
|
-
if (symbolRuntimes.length % 50 === 0)
|
|
5329
|
-
adapters.sampleMemory?.("history:loaded", {
|
|
5330
|
-
symbols: symbolRuntimes.length + 1,
|
|
5331
|
-
candles: data.length
|
|
5332
|
-
});
|
|
5333
|
-
const preparedData = buildPreparedData({
|
|
5334
|
-
data,
|
|
5335
|
-
btcData: references.btcMarketData,
|
|
5336
|
-
ethData: references.ethMarketData,
|
|
5337
|
-
btcBinanceData: references.btcBinanceData,
|
|
5338
|
-
btcCoinbaseData: references.btcCoinbaseData,
|
|
5339
|
-
start: preparedRun.window.start,
|
|
5340
|
-
preloadStart: preparedRun.preloadStart
|
|
5341
|
-
});
|
|
5342
|
-
if (!preparedData.replayData.length || !preparedData.btcReplayData.length) {
|
|
5343
|
-
skippedSymbols += 1;
|
|
5344
|
-
adapters.progress.tick({
|
|
5345
|
-
skipped: adapters.display.skipped(skippedSymbols),
|
|
5346
|
-
symbol: adapters.display.symbol(symbol)
|
|
5347
|
-
});
|
|
5348
|
-
continue;
|
|
5349
|
-
}
|
|
5350
|
-
const sharedIndicatorsReplayKey = buildSymbolSharedReplayKey({
|
|
5351
|
-
userName,
|
|
5352
|
-
connectorName,
|
|
5353
|
-
interval,
|
|
5354
|
-
symbol,
|
|
5355
|
-
start: preparedRun.window.start,
|
|
5356
|
-
end: preparedRun.window.end
|
|
5357
|
-
});
|
|
5358
|
-
sharedReplayKeyPrefixes.push(sharedIndicatorsReplayKey);
|
|
5359
|
-
const strategiesForSymbol = await Promise.all(
|
|
5360
|
-
strategies.filter((strategy) => strategyIncludesSymbol(strategy, symbol)).map(async (strategy) => {
|
|
5361
|
-
const replayStrategyConfig = buildReplayStrategyConfig({
|
|
5362
|
-
strategyConfig: strategy.strategyConfig,
|
|
5363
|
-
interval
|
|
5364
|
-
});
|
|
5365
|
-
const runtimeLineage = await adapters.buildLineage({
|
|
5366
|
-
projectRoot,
|
|
5367
|
-
strategyName: strategy.strategyName,
|
|
5368
|
-
strategyRevision: strategy.strategyRevision,
|
|
5369
|
-
deploymentCompositionId: strategy.deploymentCompositionId,
|
|
5370
|
-
strategyPackageVersion: strategy.strategyPackageVersion,
|
|
5371
|
-
strategyDependencyVersions: strategy.strategyDependencyVersions,
|
|
5372
|
-
runtimePackageVersion: strategy.runtimePackageVersion,
|
|
5373
|
-
config: {
|
|
5374
|
-
configId: strategy.strategyRevision,
|
|
5375
|
-
strategyConfig: strategy.strategyConfig,
|
|
5376
|
-
symbolResultConfig: strategy.strategyResults?.[symbol]?.config ?? null
|
|
5377
|
-
},
|
|
5378
|
-
runContext: {
|
|
5379
|
-
connectorName: connectorName.toLowerCase(),
|
|
5380
|
-
interval: String(interval),
|
|
5381
|
-
universe: preparedRun.universe ?? null
|
|
5382
|
-
}
|
|
5383
|
-
});
|
|
5384
|
-
runtimeLineages.push({
|
|
5385
|
-
strategy: strategy.strategyName,
|
|
5386
|
-
symbol,
|
|
5387
|
-
...preparedRun.deploymentId ? { deploymentId: preparedRun.deploymentId } : {},
|
|
5388
|
-
...preparedRun.accountId ? { accountId: preparedRun.accountId } : {},
|
|
5389
|
-
lineage: runtimeLineage
|
|
5390
|
-
});
|
|
5391
|
-
return {
|
|
5392
|
-
strategyName: strategy.strategyName,
|
|
5393
|
-
strategyConfig: strategy.strategyConfig,
|
|
5394
|
-
runtimeLineage,
|
|
5395
|
-
run: await strategy.strategyCreator({
|
|
5396
|
-
userName,
|
|
5397
|
-
connectorName,
|
|
5398
|
-
runtimeConfigId: strategy.strategyRevision,
|
|
5399
|
-
strategyRevision: strategy.strategyRevision,
|
|
5400
|
-
runtimeLineage,
|
|
5401
|
-
universe: preparedRun.universe,
|
|
5402
|
-
accountId: preparedRun.accountId,
|
|
5403
|
-
deploymentId: preparedRun.deploymentId,
|
|
5404
|
-
runtimeConfigSnapshot: {
|
|
5405
|
-
userConfig: replayStrategyConfig
|
|
5406
|
-
},
|
|
5407
|
-
config: replayStrategyConfig,
|
|
5408
|
-
symbol,
|
|
5409
|
-
data: preparedData.prevData,
|
|
5410
|
-
btcData: preparedData.btcPrevData,
|
|
5411
|
-
ethData: preparedData.ethPrevData,
|
|
5412
|
-
btcBinanceData: preparedData.btcBinancePrevData,
|
|
5413
|
-
btcCoinbaseData: preparedData.btcCoinbasePrevData,
|
|
5414
|
-
connector: replayConnector,
|
|
5415
|
-
sharedIndicatorsReplayKey
|
|
5416
|
-
})
|
|
5417
|
-
};
|
|
5418
|
-
})
|
|
5419
|
-
);
|
|
5420
|
-
const symbolRuntime = {
|
|
5421
|
-
symbol,
|
|
5422
|
-
replayData: preparedData.replayData,
|
|
5423
|
-
btcReplayData: preparedData.btcReplayData,
|
|
5424
|
-
ethReplayData: preparedData.ethReplayData,
|
|
5425
|
-
currentIndex: 0,
|
|
5426
|
-
strategies: strategiesForSymbol
|
|
5427
|
-
};
|
|
5428
|
-
symbolRuntimes.push(symbolRuntime);
|
|
5429
|
-
if (symbolRuntimes.length % 50 === 0 || symbol === preparedRun.tickers.at(-1))
|
|
5430
|
-
adapters.sampleMemory?.("strategies:initializing", {
|
|
5431
|
-
symbols: symbolRuntimes.length,
|
|
5432
|
-
strategiesPerSymbol: strategiesForSymbol.length
|
|
5433
|
-
});
|
|
5434
|
-
adapters.progress.tick({
|
|
5435
|
-
skipped: adapters.display.skipped(skippedSymbols),
|
|
5436
|
-
symbol: adapters.display.symbol(symbol)
|
|
5437
|
-
});
|
|
5438
|
-
for (const candle of preparedData.replayData) {
|
|
5439
|
-
orderedTimestamps.add(candle.timestamp);
|
|
5440
|
-
}
|
|
5441
|
-
}
|
|
5442
|
-
return {
|
|
5443
|
-
symbolRuntimes,
|
|
5444
|
-
orderedTimestamps: [...orderedTimestamps].sort(
|
|
5445
|
-
(left, right) => left - right
|
|
5446
|
-
),
|
|
5447
|
-
sharedReplayKeyPrefixes,
|
|
5448
|
-
runtimeLineages
|
|
5449
|
-
};
|
|
5450
|
-
};
|
|
5451
|
-
var releaseHistoricalReplayPlan = (plan) => {
|
|
5452
|
-
plan.symbolRuntimes.length = 0;
|
|
5453
|
-
plan.sharedReplayKeyPrefixes.length = 0;
|
|
5454
|
-
};
|
|
5455
|
-
|
|
5456
5588
|
// src/lib/replay/historicalSignalsReplaySkipEvidence.ts
|
|
5457
|
-
var
|
|
5589
|
+
var import_data2 = require("@tradejs/core/data");
|
|
5458
5590
|
var replayScopeKey = (strategy, symbol) => `${strategy}::${symbol}`;
|
|
5459
5591
|
var appendReplaySignalEvaluationRun = ({
|
|
5460
5592
|
runs = [],
|
|
@@ -5462,7 +5594,7 @@ var appendReplaySignalEvaluationRun = ({
|
|
|
5462
5594
|
timestamp,
|
|
5463
5595
|
interval
|
|
5464
5596
|
}) => {
|
|
5465
|
-
const stepMs = (0,
|
|
5597
|
+
const stepMs = (0, import_data2.intervalToMs)(interval);
|
|
5466
5598
|
const last = runs.at(-1);
|
|
5467
5599
|
if (last?.reason === reason && last.stepMs === stepMs && timestamp === last.lastTimestamp + stepMs) {
|
|
5468
5600
|
return [...runs.slice(0, -1), { ...last, lastTimestamp: timestamp }];
|
|
@@ -5534,11 +5666,12 @@ var createHistoricalReplaySkipEvidence = (runtimeLineages, enabled = true) => {
|
|
|
5534
5666
|
// src/lib/replay/historicalSignalsReplayExecution.ts
|
|
5535
5667
|
var advanceCycleMarket = async (plan, connector, timestamp) => {
|
|
5536
5668
|
const cycleSymbols = plan.symbolRuntimes.filter(
|
|
5537
|
-
(symbolRuntime) => symbolRuntime.replayData[symbolRuntime.currentIndex]?.timestamp === timestamp
|
|
5669
|
+
(symbolRuntime) => (symbolRuntime.replayData[symbolRuntime.currentIndex]?.timestamp ?? NaN) + (symbolRuntime.timestampOffsetMs ?? 0) === timestamp
|
|
5538
5670
|
);
|
|
5539
5671
|
for (const symbolRuntime of cycleSymbols) {
|
|
5540
5672
|
const candle = symbolRuntime.replayData[symbolRuntime.currentIndex];
|
|
5541
|
-
if (candle
|
|
5673
|
+
if (candle && candle.timestamp + (symbolRuntime.timestampOffsetMs ?? 0) === timestamp) {
|
|
5674
|
+
if (symbolRuntime.advanceMarket === false) continue;
|
|
5542
5675
|
await connector.advanceMarket({
|
|
5543
5676
|
symbol: symbolRuntime.symbol,
|
|
5544
5677
|
candle
|
|
@@ -5550,7 +5683,7 @@ var advanceCycleMarket = async (plan, connector, timestamp) => {
|
|
|
5550
5683
|
var advanceAbortedCycle = (cycleSymbols, timestamp) => {
|
|
5551
5684
|
for (const symbolRuntime of cycleSymbols ?? []) {
|
|
5552
5685
|
const candle = symbolRuntime.replayData[symbolRuntime.currentIndex];
|
|
5553
|
-
if (candle
|
|
5686
|
+
if (candle && candle.timestamp + (symbolRuntime.timestampOffsetMs ?? 0) === timestamp) {
|
|
5554
5687
|
symbolRuntime.currentIndex += 1;
|
|
5555
5688
|
}
|
|
5556
5689
|
}
|
|
@@ -5599,21 +5732,21 @@ var executeHistoricalReplay = async (context, adapters) => {
|
|
|
5599
5732
|
const candle = symbolRuntime.replayData[symbolRuntime.currentIndex];
|
|
5600
5733
|
const btcCandle = symbolRuntime.btcReplayData[symbolRuntime.currentIndex];
|
|
5601
5734
|
const ethCandle = symbolRuntime.ethReplayData[symbolRuntime.currentIndex];
|
|
5602
|
-
if (!candle || !btcCandle || candle.timestamp !== timestamp || btcCandle.timestamp !== timestamp) {
|
|
5735
|
+
if (!candle || !btcCandle || candle.timestamp + (symbolRuntime.timestampOffsetMs ?? 0) !== timestamp || btcCandle.timestamp !== candle.timestamp) {
|
|
5603
5736
|
continue;
|
|
5604
5737
|
}
|
|
5605
5738
|
for (const strategyRuntime of symbolRuntime.strategies) {
|
|
5606
5739
|
const result = await strategyRuntime.run(
|
|
5607
5740
|
candle,
|
|
5608
5741
|
btcCandle,
|
|
5609
|
-
ethCandle?.timestamp === timestamp ? ethCandle : void 0
|
|
5742
|
+
ethCandle?.timestamp === candle.timestamp ? ethCandle : void 0
|
|
5610
5743
|
);
|
|
5611
5744
|
skipEvidence.record({
|
|
5612
5745
|
strategyName: strategyRuntime.strategyName,
|
|
5613
5746
|
symbol: symbolRuntime.symbol,
|
|
5614
5747
|
strategyConfig: strategyRuntime.strategyConfig,
|
|
5615
5748
|
runtimeLineage: strategyRuntime.runtimeLineage,
|
|
5616
|
-
timestamp,
|
|
5749
|
+
timestamp: candle.timestamp,
|
|
5617
5750
|
result
|
|
5618
5751
|
});
|
|
5619
5752
|
if (result && typeof result !== "string") {
|
|
@@ -5634,10 +5767,7 @@ var executeHistoricalReplay = async (context, adapters) => {
|
|
|
5634
5767
|
reportProgress(timestamp);
|
|
5635
5768
|
}
|
|
5636
5769
|
} finally {
|
|
5637
|
-
|
|
5638
|
-
adapters.releaseIndicatorsCache(keyPrefix);
|
|
5639
|
-
adapters.releaseReplayCache(keyPrefix);
|
|
5640
|
-
}
|
|
5770
|
+
releaseHistoricalReplayCaches(context.plan, adapters);
|
|
5641
5771
|
}
|
|
5642
5772
|
return {
|
|
5643
5773
|
signals,
|
|
@@ -5911,7 +6041,19 @@ var runHistoricalSignalsReplay = async ({
|
|
|
5911
6041
|
}
|
|
5912
6042
|
);
|
|
5913
6043
|
memory?.("references:loaded");
|
|
5914
|
-
const
|
|
6044
|
+
const cacheAdapters = {
|
|
6045
|
+
releaseIndicatorsCache: (keyPrefix) => releaseReplayCacheCopies(
|
|
6046
|
+
keyPrefix,
|
|
6047
|
+
import_strategies3.releaseStrategyIndicatorsReplayCache,
|
|
6048
|
+
getProjectReplayCacheReleasers()?.releaseStrategyIndicatorsReplayCache
|
|
6049
|
+
),
|
|
6050
|
+
releaseReplayCache: (keyPrefix) => releaseReplayCacheCopies(
|
|
6051
|
+
keyPrefix,
|
|
6052
|
+
import_strategies3.releaseStrategyReplayCache,
|
|
6053
|
+
getProjectReplayCacheReleasers()?.releaseStrategyReplayCache
|
|
6054
|
+
)
|
|
6055
|
+
};
|
|
6056
|
+
const plan = await prepareMultiIntervalHistoricalReplay(
|
|
5915
6057
|
{
|
|
5916
6058
|
userName: replayUserName,
|
|
5917
6059
|
projectRoot: replayProjectRoot,
|
|
@@ -5928,6 +6070,7 @@ var runHistoricalSignalsReplay = async ({
|
|
|
5928
6070
|
}
|
|
5929
6071
|
},
|
|
5930
6072
|
{
|
|
6073
|
+
...cacheAdapters,
|
|
5931
6074
|
progress: {
|
|
5932
6075
|
tick: (tokens) => prepareBar.tick(1, tokens)
|
|
5933
6076
|
},
|
|
@@ -5937,7 +6080,11 @@ var runHistoricalSignalsReplay = async ({
|
|
|
5937
6080
|
},
|
|
5938
6081
|
buildLineage: buildRuntimeLineage,
|
|
5939
6082
|
sampleMemory: memory
|
|
5940
|
-
}
|
|
6083
|
+
},
|
|
6084
|
+
(nativeInterval, nativeRun) => loadHistoricalReplayReferences({
|
|
6085
|
+
preparedRun: nativeRun,
|
|
6086
|
+
interval: nativeInterval
|
|
6087
|
+
})
|
|
5941
6088
|
);
|
|
5942
6089
|
memory?.("strategies:prepared", { symbols: plan.symbolRuntimes.length });
|
|
5943
6090
|
const hookContext = buildAfterSignalsContext({
|
|
@@ -5964,6 +6111,7 @@ var runHistoricalSignalsReplay = async ({
|
|
|
5964
6111
|
hookContext
|
|
5965
6112
|
},
|
|
5966
6113
|
{
|
|
6114
|
+
...cacheAdapters,
|
|
5967
6115
|
clock: { now: Date.now },
|
|
5968
6116
|
progress: {
|
|
5969
6117
|
enabled: showProgress,
|
|
@@ -5976,17 +6124,7 @@ var runHistoricalSignalsReplay = async ({
|
|
|
5976
6124
|
},
|
|
5977
6125
|
invokeBeforeSignals: invokeBeforeSignalsHooks,
|
|
5978
6126
|
invokeAfterSignals: invokeAfterSignalsHooks,
|
|
5979
|
-
enrichSignal: (signal) => (0, import_strategies4.enrichSignalWithBinanceMarketContext)({ signal, env: "PARITY" })
|
|
5980
|
-
releaseIndicatorsCache: (keyPrefix) => releaseReplayCacheCopies(
|
|
5981
|
-
keyPrefix,
|
|
5982
|
-
import_strategies3.releaseStrategyIndicatorsReplayCache,
|
|
5983
|
-
getProjectReplayCacheReleasers()?.releaseStrategyIndicatorsReplayCache
|
|
5984
|
-
),
|
|
5985
|
-
releaseReplayCache: (keyPrefix) => releaseReplayCacheCopies(
|
|
5986
|
-
keyPrefix,
|
|
5987
|
-
import_strategies3.releaseStrategyReplayCache,
|
|
5988
|
-
getProjectReplayCacheReleasers()?.releaseStrategyReplayCache
|
|
5989
|
-
)
|
|
6127
|
+
enrichSignal: (signal) => (0, import_strategies4.enrichSignalWithBinanceMarketContext)({ signal, env: "PARITY" })
|
|
5990
6128
|
}
|
|
5991
6129
|
);
|
|
5992
6130
|
memory?.("execution:complete", { signals: execution.signals.length });
|
|
@@ -6446,6 +6584,7 @@ var saveAndPrintReplayResultsByStrategy = async ({
|
|
|
6446
6584
|
|
|
6447
6585
|
// src/lib/replay/runtimeComparison.ts
|
|
6448
6586
|
var import_chalk12 = __toESM(require("chalk"));
|
|
6587
|
+
var import_data4 = require("@tradejs/core/data");
|
|
6449
6588
|
|
|
6450
6589
|
// src/lib/paritySummary.ts
|
|
6451
6590
|
var summarizeRuntimeTradesByStrategy = (trades) => {
|
|
@@ -6524,7 +6663,7 @@ var import_redis4 = require("@tradejs/infra/redis");
|
|
|
6524
6663
|
|
|
6525
6664
|
// src/lib/runtimeSignalsStorage.ts
|
|
6526
6665
|
var import_constants5 = require("@tradejs/core/constants");
|
|
6527
|
-
var
|
|
6666
|
+
var import_data3 = require("@tradejs/core/data");
|
|
6528
6667
|
var import_time4 = require("@tradejs/core/time");
|
|
6529
6668
|
var isRuntimeSignalBucketRef = (value) => {
|
|
6530
6669
|
if (!value || typeof value !== "object") {
|
|
@@ -8049,7 +8188,10 @@ var findExchangeRuntimeOutcome = ({
|
|
|
8049
8188
|
return candidates.map((candidate) => ({
|
|
8050
8189
|
...candidate,
|
|
8051
8190
|
timestampDiffMs: Math.abs(
|
|
8052
|
-
candidate.timestamp +
|
|
8191
|
+
candidate.timestamp + resolveTradeParityTimestampOffset(
|
|
8192
|
+
strategyName,
|
|
8193
|
+
signalTimestampOffsetMs
|
|
8194
|
+
) - exchangeEntry.entryTimestamp
|
|
8053
8195
|
)
|
|
8054
8196
|
})).filter((candidate) => candidate.timestampDiffMs <= toleranceMs).sort(
|
|
8055
8197
|
(left, right) => left.timestampDiffMs - right.timestampDiffMs || Number(right.orderStatus === "failed") - Number(left.orderStatus === "failed")
|
|
@@ -8289,10 +8431,13 @@ var writeReplayComparisonReport = (lines) => {
|
|
|
8289
8431
|
};
|
|
8290
8432
|
|
|
8291
8433
|
// src/lib/replay/runtimeComparison.ts
|
|
8292
|
-
var
|
|
8293
|
-
const
|
|
8294
|
-
|
|
8295
|
-
};
|
|
8434
|
+
var replayStrategyIntervals = (summaries) => summaries.map(({ strategyName, strategyConfig }) => {
|
|
8435
|
+
const durationMs = (0, import_data4.intervalToMs)(strategyConfig.INTERVAL);
|
|
8436
|
+
if (!Number.isFinite(durationMs) || durationMs <= 0) {
|
|
8437
|
+
throw new Error(`Invalid replay interval for ${strategyName}`);
|
|
8438
|
+
}
|
|
8439
|
+
return { strategyName, interval: String(durationMs / 6e4) };
|
|
8440
|
+
});
|
|
8296
8441
|
var buildReplaySignalEvaluations = (signals) => signals.map((signal) => ({
|
|
8297
8442
|
evaluationId: `${signal.strategy}:${signal.symbol}:${signal.timestamp}`,
|
|
8298
8443
|
userName: replayUserName,
|
|
@@ -8509,7 +8654,8 @@ var saveAndPrintReplayExchangeComparison = async ({
|
|
|
8509
8654
|
runtimeSignals,
|
|
8510
8655
|
runtimeSignalEvaluations,
|
|
8511
8656
|
runtimeLineageScopes,
|
|
8512
|
-
lineage
|
|
8657
|
+
lineage,
|
|
8658
|
+
timestampOffsetMs
|
|
8513
8659
|
}) => {
|
|
8514
8660
|
const { connector, connectorName, window } = getRuntimeCompareContext();
|
|
8515
8661
|
const rawExchangeEntries = await loadExchangeEntriesForComparison({
|
|
@@ -8534,7 +8680,7 @@ var saveAndPrintReplayExchangeComparison = async ({
|
|
|
8534
8680
|
runtimeSignals,
|
|
8535
8681
|
runtimeSignalEvaluations,
|
|
8536
8682
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8537
|
-
signalTimestampOffsetMs:
|
|
8683
|
+
signalTimestampOffsetMs: timestampOffsetMs
|
|
8538
8684
|
});
|
|
8539
8685
|
});
|
|
8540
8686
|
const comparisonLineage = {
|
|
@@ -8556,7 +8702,7 @@ var saveAndPrintReplayExchangeComparison = async ({
|
|
|
8556
8702
|
backtestEntries,
|
|
8557
8703
|
strategyNameByOrderLinkKey,
|
|
8558
8704
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8559
|
-
backtestTimestampOffsetMs:
|
|
8705
|
+
backtestTimestampOffsetMs: timestampOffsetMs,
|
|
8560
8706
|
runtimeSignals,
|
|
8561
8707
|
runtimeSignalEvaluations,
|
|
8562
8708
|
replaySignals,
|
|
@@ -8595,7 +8741,7 @@ var saveAndPrintReplayExchangeComparison = async ({
|
|
|
8595
8741
|
exchangeEntries,
|
|
8596
8742
|
backtestEntries,
|
|
8597
8743
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8598
|
-
backtestTimestampOffsetMs:
|
|
8744
|
+
backtestTimestampOffsetMs: timestampOffsetMs
|
|
8599
8745
|
});
|
|
8600
8746
|
const { completed: matched, orderFailed } = splitExchangeMatchesByRuntimeOrderStatus({
|
|
8601
8747
|
matched: comparison.matched,
|
|
@@ -8603,7 +8749,7 @@ var saveAndPrintReplayExchangeComparison = async ({
|
|
|
8603
8749
|
runtimeSignals,
|
|
8604
8750
|
runtimeSignalEvaluations,
|
|
8605
8751
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8606
|
-
signalTimestampOffsetMs:
|
|
8752
|
+
signalTimestampOffsetMs: timestampOffsetMs
|
|
8607
8753
|
});
|
|
8608
8754
|
const details = buildReplayExchangeComparisonDetails({
|
|
8609
8755
|
matched,
|
|
@@ -8614,7 +8760,7 @@ var saveAndPrintReplayExchangeComparison = async ({
|
|
|
8614
8760
|
backtestEntries,
|
|
8615
8761
|
strategyNameByOrderLinkKey,
|
|
8616
8762
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8617
|
-
backtestTimestampOffsetMs:
|
|
8763
|
+
backtestTimestampOffsetMs: timestampOffsetMs,
|
|
8618
8764
|
runtimeSignals,
|
|
8619
8765
|
runtimeSignalEvaluations,
|
|
8620
8766
|
replaySignals,
|
|
@@ -8675,9 +8821,16 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8675
8821
|
expectedUserName: replayUserName,
|
|
8676
8822
|
expectedWindow: window
|
|
8677
8823
|
}) : null;
|
|
8678
|
-
const
|
|
8679
|
-
|
|
8680
|
-
|
|
8824
|
+
const declaredIntervals = runtimeEvidence ? [] : replayStrategyIntervals(liveStrategySummaries);
|
|
8825
|
+
const replayTimestampOffset = buildTradeParityTimestampOffset(
|
|
8826
|
+
runtimeEvidence ? (0, runtimeEvidenceDeployment_exports.activeRuntimeEvidenceStrategies)(runtimeEvidence.deployment) : declaredIntervals
|
|
8827
|
+
);
|
|
8828
|
+
const metadataStart = runtimeEvidence ? window.start : window.start - Math.max(
|
|
8829
|
+
0,
|
|
8830
|
+
...declaredIntervals.map(
|
|
8831
|
+
({ interval }) => Number(interval) * 6e4 - (0, import_data4.intervalToMs)(replayInterval)
|
|
8832
|
+
)
|
|
8833
|
+
);
|
|
8681
8834
|
const [
|
|
8682
8835
|
rawRuntimeTrades,
|
|
8683
8836
|
runtimeSignals,
|
|
@@ -8694,15 +8847,15 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8694
8847
|
endTime: window.end
|
|
8695
8848
|
}),
|
|
8696
8849
|
loadRuntimeSignals(replayUserName, {
|
|
8697
|
-
startTime:
|
|
8850
|
+
startTime: metadataStart,
|
|
8698
8851
|
endTime: window.end
|
|
8699
8852
|
}),
|
|
8700
8853
|
loadRuntimeSignalEvaluations(replayUserName, {
|
|
8701
|
-
startTime:
|
|
8854
|
+
startTime: metadataStart,
|
|
8702
8855
|
endTime: window.end
|
|
8703
8856
|
}),
|
|
8704
8857
|
loadRuntimeLineageScopes(replayUserName, {
|
|
8705
|
-
startTime:
|
|
8858
|
+
startTime: metadataStart,
|
|
8706
8859
|
endTime: window.end
|
|
8707
8860
|
})
|
|
8708
8861
|
]);
|
|
@@ -8770,6 +8923,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8770
8923
|
);
|
|
8771
8924
|
return saveAndPrintReplayExchangeComparison({
|
|
8772
8925
|
liveStrategySummaries,
|
|
8926
|
+
timestampOffsetMs: replayTimestampOffset,
|
|
8773
8927
|
backtestEntries: comparableBacktestEntries,
|
|
8774
8928
|
replaySignals,
|
|
8775
8929
|
replaySignalEvaluations,
|
|
@@ -9919,16 +10073,10 @@ var replayBacktest = async () => {
|
|
|
9919
10073
|
if (!replayStrategies.length) {
|
|
9920
10074
|
throw new Error(`No enabled strategies in deployment ${deployment.id}`);
|
|
9921
10075
|
}
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
)
|
|
10076
|
+
resolveHistoricalReplayIntervals(
|
|
10077
|
+
replayInterval,
|
|
10078
|
+
replayStrategies.map(({ strategyConfig }) => strategyConfig.INTERVAL)
|
|
9926
10079
|
);
|
|
9927
|
-
if (strategyIntervals.size !== 1 || !strategyIntervals.has(replayInterval)) {
|
|
9928
|
-
throw new Error(
|
|
9929
|
-
`Replay interval does not match deployment ${deployment.id}: replay=${replayInterval}, strategies=${[...strategyIntervals].join(",")}`
|
|
9930
|
-
);
|
|
9931
|
-
}
|
|
9932
10080
|
const strategyUniverses = new Set(
|
|
9933
10081
|
replayStrategies.map(
|
|
9934
10082
|
({ strategyConfig }) => String(strategyConfig.UNIVERSE)
|
|
@@ -9974,13 +10122,13 @@ var replayBacktest = async () => {
|
|
|
9974
10122
|
deploymentId: deployment.id,
|
|
9975
10123
|
assetClasses: deployment.assetClasses,
|
|
9976
10124
|
deployment,
|
|
9977
|
-
closedIntervalMs: (0,
|
|
10125
|
+
closedIntervalMs: (0, import_data5.intervalToMs)(replayInterval)
|
|
9978
10126
|
});
|
|
9979
10127
|
if (!preparedRun || isReplayUpdateOnlyRun) {
|
|
9980
10128
|
return;
|
|
9981
10129
|
}
|
|
9982
10130
|
if (useAllData) {
|
|
9983
|
-
const intervalMs = (0,
|
|
10131
|
+
const intervalMs = (0, import_data5.intervalToMs)(replayInterval);
|
|
9984
10132
|
const edges = await (0, import_candles.getDataEdgesForSymbols)(
|
|
9985
10133
|
deployment.provider,
|
|
9986
10134
|
preparedRun.tickers,
|