@tradejs/cli 1.0.8 → 1.0.10

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.
Files changed (76) hide show
  1. package/dist/cli.js +29748 -13641
  2. package/dist/lib/aiPocketSearch.js +1316 -0
  3. package/dist/lib/aiTrainCharts.js +711 -0
  4. package/dist/lib/aiTrainDataset.js +40 -0
  5. package/dist/lib/aiTrainEvaluationDump.js +76 -0
  6. package/dist/lib/aiTrainMetrics.js +148 -8
  7. package/dist/lib/aiTrainOptions.js +130 -0
  8. package/dist/lib/aiTrainQuarantine.js +199 -0
  9. package/dist/lib/aiTrainResearch.js +467 -0
  10. package/dist/lib/binanceMarketContextBackfill.js +841 -0
  11. package/dist/lib/binanceMarketData.js +392 -0
  12. package/dist/lib/coinMarketCapContextBackfill.js +1088 -0
  13. package/dist/lib/continuity.js +60 -0
  14. package/dist/lib/derivativesContextBackfill.js +490 -77
  15. package/dist/lib/executionCalibration.js +705 -0
  16. package/dist/lib/marketContextPrepare.js +4403 -0
  17. package/dist/lib/paritySummary.js +101 -0
  18. package/dist/lib/runBot.js +1 -0
  19. package/dist/lib/runEnvironment.js +2254 -0
  20. package/dist/lib/runFormatting.js +1657 -0
  21. package/dist/lib/runtimeDebugEvidence.js +526 -0
  22. package/dist/lib/runtimeModeConfig.js +53 -0
  23. package/dist/lib/runtimeParity.js +88 -11
  24. package/dist/lib/runtimeParityDetails.js +751 -0
  25. package/dist/lib/runtimeRedis.js +157 -0
  26. package/dist/lib/runtimeSignalsLoader.js +187 -0
  27. package/dist/lib/runtimeSignalsStorage.js +185 -0
  28. package/dist/lib/runtimeStrategyBacktest.js +134 -0
  29. package/dist/lib/runtimeTradeSync.js +263 -0
  30. package/dist/lib/telegramReports.js +9 -0
  31. package/dist/lib/tickerUniverseCache.js +116 -0
  32. package/dist/lib/timeWindow.js +3 -3
  33. package/dist/scripts/agentRun.js +28 -8
  34. package/dist/scripts/aiExport.js +1721 -7
  35. package/dist/scripts/aiExportSelect.js +16 -4
  36. package/dist/scripts/aiPocketSearch.js +4190 -0
  37. package/dist/scripts/aiTrain.js +1866 -87
  38. package/dist/scripts/backtest.js +4509 -818
  39. package/dist/scripts/binanceMarketIngest.js +565 -0
  40. package/dist/scripts/bot.js +16 -2
  41. package/dist/scripts/candlesMigrateProvider.js +32 -22
  42. package/dist/scripts/cleanDir.js +15 -2
  43. package/dist/scripts/cleanRedis.js +15 -2
  44. package/dist/scripts/cleanTests.js +38 -2
  45. package/dist/scripts/cleanupMarketContext.js +69 -0
  46. package/dist/scripts/continuity.js +148 -67
  47. package/dist/scripts/derivativesIngest.js +14 -4
  48. package/dist/scripts/derivativesIngestCoinalyzeAll.js +14 -4
  49. package/dist/scripts/doctor.js +14 -4
  50. package/dist/scripts/executionCalibration.js +792 -0
  51. package/dist/scripts/infraCommon.js +10 -0
  52. package/dist/scripts/infraDown.js +14 -4
  53. package/dist/scripts/infraInit.js +24 -4
  54. package/dist/scripts/infraUp.js +14 -4
  55. package/dist/scripts/marketWs.js +222 -0
  56. package/dist/scripts/migration.js +15 -2
  57. package/dist/scripts/mlExport.js +1698 -10
  58. package/dist/scripts/mlExportSelect.js +16 -4
  59. package/dist/scripts/mlInspect.js +13 -3
  60. package/dist/scripts/mlTrainLatestSelect.js +14 -4
  61. package/dist/scripts/replay.js +8824 -0
  62. package/dist/scripts/replayRunner.js +8827 -0
  63. package/dist/scripts/replayRuntimeEvidence.js +868 -0
  64. package/dist/scripts/researchAuto.js +289 -60
  65. package/dist/scripts/results.js +3 -3
  66. package/dist/scripts/runtimeEvidence.js +605 -0
  67. package/dist/scripts/runtimeParity.js +6360 -1004
  68. package/dist/scripts/serverHealth.js +588 -0
  69. package/dist/scripts/signals.js +5623 -486
  70. package/dist/scripts/signalsDaemon.js +6167 -0
  71. package/dist/scripts/signalsSummary.js +954 -312
  72. package/dist/scripts/test-ml.js +25 -3
  73. package/dist/scripts/test.js +26 -1
  74. package/dist/scripts/user-add.js +15 -2
  75. package/dist/workers/testerWorker.js +130 -21
  76. package/package.json +18 -12
@@ -0,0 +1,751 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/lib/runtimeParityDetails.ts
21
+ var runtimeParityDetails_exports = {};
22
+ __export(runtimeParityDetails_exports, {
23
+ buildReplayExchangeComparisonDetails: () => buildReplayExchangeComparisonDetails,
24
+ buildReplayMismatchDrilldown: () => buildReplayMismatchDrilldown,
25
+ buildReplayRuntimeComparisonDetails: () => buildReplayRuntimeComparisonDetails,
26
+ buildStrategyNameByOrderLinkKey: () => buildStrategyNameByOrderLinkKey,
27
+ resolveReplayStrategyNameFromExchangeEntry: () => resolveReplayStrategyNameFromExchangeEntry
28
+ });
29
+ module.exports = __toCommonJS(runtimeParityDetails_exports);
30
+ var import_trade = require("@tradejs/core/trade");
31
+
32
+ // src/lib/runtimeParity.ts
33
+ var getBacktestParityComparisonTimestamp = (entry, timestampOffsetMs = 0) => {
34
+ if (timestampOffsetMs > 0 && entry.signalTimestamp != null && entry.timestamp >= entry.signalTimestamp + timestampOffsetMs) {
35
+ return entry.timestamp;
36
+ }
37
+ return (entry.signalTimestamp ?? entry.timestamp) + timestampOffsetMs;
38
+ };
39
+
40
+ // src/lib/runtimeParityDetails.ts
41
+ var DEFAULT_REPLAY_PARITY_DETAILS_LIMIT = 500;
42
+ var DEFAULT_REPLAY_PARITY_ARTIFACT_LIMIT = 100;
43
+ var buildStrategyNameByOrderLinkKey = (strategyNames) => new Map(
44
+ strategyNames.flatMap((strategyName) => {
45
+ const strategyKey = (0, import_trade.normalizeStrategyOrderLinkKey)(strategyName);
46
+ return strategyKey ? [[strategyKey, strategyName]] : [];
47
+ })
48
+ );
49
+ var resolveReplayStrategyNameFromExchangeEntry = ({
50
+ exchangeEntry,
51
+ strategyNameByOrderLinkKey
52
+ }) => {
53
+ const strategyKey = (0, import_trade.parseStrategyOrderLinkKey)(exchangeEntry.orderLinkId);
54
+ if (!strategyKey) {
55
+ return null;
56
+ }
57
+ return strategyNameByOrderLinkKey.get(strategyKey) ?? null;
58
+ };
59
+ var resolveReplayParityDetailsLimit = () => {
60
+ const parsed = Number.parseInt(
61
+ String(process.env.REPLAY_PARITY_DETAILS_LIMIT ?? ""),
62
+ 10
63
+ );
64
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_REPLAY_PARITY_DETAILS_LIMIT;
65
+ };
66
+ var resolveReplayParityArtifactLimit = () => {
67
+ const parsed = Number.parseInt(
68
+ String(process.env.REPLAY_PARITY_ARTIFACT_LIMIT ?? ""),
69
+ 10
70
+ );
71
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_REPLAY_PARITY_ARTIFACT_LIMIT;
72
+ };
73
+ var capReplayDetails = (items, limit) => items.slice(0, Math.max(0, limit));
74
+ var buildCostDetail = (entry) => ({
75
+ entryFee: entry.entryFee ?? null,
76
+ exitFee: entry.exitFee ?? null,
77
+ fundingFee: entry.fundingFee ?? null,
78
+ totalFee: entry.totalFee ?? null
79
+ });
80
+ var toBacktestParityDetail = (entry, timestampOffsetMs = 0) => {
81
+ const comparisonTimestamp = getBacktestParityComparisonTimestamp(
82
+ entry,
83
+ timestampOffsetMs
84
+ );
85
+ return {
86
+ source: "backtest",
87
+ strategy: entry.strategy,
88
+ symbol: entry.symbol,
89
+ direction: entry.direction,
90
+ qty: entry.qty ?? null,
91
+ timestamp: entry.timestamp,
92
+ signalTimestamp: entry.signalTimestamp ?? null,
93
+ comparisonTimestamp: timestampOffsetMs === 0 ? null : comparisonTimestamp,
94
+ price: entry.price,
95
+ exitType: entry.exitType ?? null,
96
+ exitTimestamp: entry.exitTimestamp ?? null,
97
+ exitPrice: entry.exitPrice ?? null,
98
+ pnl: entry.expectedPnl ?? null,
99
+ costs: buildCostDetail(entry),
100
+ orderId: entry.orderId,
101
+ signalId: entry.signalId
102
+ };
103
+ };
104
+ var toRuntimeParityDetail = (entry) => ({
105
+ source: "runtime",
106
+ strategy: entry.strategy,
107
+ symbol: entry.symbol,
108
+ direction: entry.direction,
109
+ qty: entry.qty ?? null,
110
+ timestamp: entry.timestamp,
111
+ price: entry.price,
112
+ exitType: entry.exitType ?? null,
113
+ exitTimestamp: entry.exitTimestamp ?? null,
114
+ exitPrice: entry.exitPrice ?? null,
115
+ pnl: entry.realizedPnl ?? null,
116
+ costs: buildCostDetail(entry),
117
+ orderId: entry.orderId,
118
+ signalId: entry.signalId
119
+ });
120
+ var toExchangeParityDetail = ({
121
+ entry,
122
+ inferredStrategy = null
123
+ }) => ({
124
+ source: "exchange",
125
+ inferredStrategy,
126
+ symbol: entry.symbol,
127
+ direction: entry.direction,
128
+ qty: entry.qty,
129
+ timestamp: entry.entryTimestamp,
130
+ price: entry.entryPrice,
131
+ exitType: null,
132
+ exitTimestamp: entry.exitTimestamp ?? null,
133
+ exitPrice: entry.exitPrice ?? null,
134
+ pnl: entry.closedPnl ?? null,
135
+ costs: buildCostDetail({
136
+ entryFee: entry.openFee,
137
+ exitFee: entry.closeFee,
138
+ fundingFee: entry.fundingFee,
139
+ totalFee: entry.totalFee
140
+ }),
141
+ orderId: entry.orderId,
142
+ orderLinkId: entry.orderLinkId
143
+ });
144
+ var buildTimestampDelta = (left, right) => typeof left === "number" && Number.isFinite(left) && typeof right === "number" && Number.isFinite(right) ? Math.abs(left - right) : null;
145
+ var getComparisonTimestamp = (entry) => typeof entry.comparisonTimestamp === "number" && Number.isFinite(entry.comparisonTimestamp) ? entry.comparisonTimestamp : entry.timestamp;
146
+ var getEntryStrategy = (entry) => entry.strategy ?? entry.inferredStrategy ?? null;
147
+ var findNearestSignal = ({
148
+ entry,
149
+ signals,
150
+ toleranceMs,
151
+ timestampOffsetMs,
152
+ entryTimestampOffsetMs = 0
153
+ }) => {
154
+ const entryStrategy = getEntryStrategy(entry);
155
+ if (!entryStrategy) {
156
+ return null;
157
+ }
158
+ let bestSignal = null;
159
+ let bestDiff = Number.POSITIVE_INFINITY;
160
+ const entryTimestamp = entry.timestamp + entryTimestampOffsetMs;
161
+ for (const signal of signals) {
162
+ if (signal.strategy !== entryStrategy || signal.symbol !== entry.symbol || signal.direction !== entry.direction) {
163
+ continue;
164
+ }
165
+ const diff = Math.abs(
166
+ signal.timestamp + timestampOffsetMs - entryTimestamp
167
+ );
168
+ if (diff > toleranceMs || diff >= bestDiff) {
169
+ continue;
170
+ }
171
+ bestSignal = signal;
172
+ bestDiff = diff;
173
+ }
174
+ return bestSignal ? {
175
+ signal: bestSignal,
176
+ timestampDiffMs: bestDiff
177
+ } : null;
178
+ };
179
+ var findNearestEvaluation = ({
180
+ entry,
181
+ evaluations,
182
+ toleranceMs,
183
+ timestampOffsetMs,
184
+ entryTimestampOffsetMs = 0
185
+ }) => {
186
+ const entryStrategy = getEntryStrategy(entry);
187
+ if (!entryStrategy) {
188
+ return null;
189
+ }
190
+ let bestEvaluation = null;
191
+ let bestDiff = Number.POSITIVE_INFINITY;
192
+ const entryTimestamp = entry.timestamp + entryTimestampOffsetMs;
193
+ for (const evaluation of evaluations) {
194
+ if (evaluation.strategy !== entryStrategy || evaluation.symbol !== entry.symbol) {
195
+ continue;
196
+ }
197
+ const diff = Math.abs(
198
+ evaluation.timestamp + timestampOffsetMs - entryTimestamp
199
+ );
200
+ if (diff > toleranceMs || diff >= bestDiff) {
201
+ continue;
202
+ }
203
+ bestEvaluation = evaluation;
204
+ bestDiff = diff;
205
+ }
206
+ return bestEvaluation ? {
207
+ evaluation: bestEvaluation,
208
+ timestampDiffMs: bestDiff
209
+ } : null;
210
+ };
211
+ var classifySignalDiagnostic = (signal) => {
212
+ if (signal.orderStatus === "failed") {
213
+ return {
214
+ classification: "order_failed",
215
+ reason: signal.orderFailureReason || signal.orderSkipReason || "failed"
216
+ };
217
+ }
218
+ if (signal.orderStatus === "skipped" || signal.orderStatus === "canceled" || typeof signal.orderSkipReason === "string" && signal.orderSkipReason.trim() || signal.ml?.passed === false) {
219
+ return {
220
+ classification: "gated_or_policy_blocked",
221
+ reason: signal.orderSkipReason || (signal.ml?.passed === false ? `ml_probability=${signal.ml.probability} threshold=${signal.ml.threshold}` : `orderStatus=${signal.orderStatus ?? "skipped"}`)
222
+ };
223
+ }
224
+ if (signal.orderStatus === "completed") {
225
+ return {
226
+ classification: "completed_signal_without_match",
227
+ reason: "completed_signal_without_matching_trade"
228
+ };
229
+ }
230
+ return {
231
+ classification: "signal_without_order_status",
232
+ reason: "signal_found_without_order_status"
233
+ };
234
+ };
235
+ var classifyEvaluationDiagnostic = (evaluation) => {
236
+ if (evaluation.status === "skip") {
237
+ return {
238
+ classification: "core_skipped",
239
+ reason: evaluation.reason || "NO_SIGNAL"
240
+ };
241
+ }
242
+ if (evaluation.status === "error") {
243
+ return {
244
+ classification: "evaluation_error",
245
+ reason: evaluation.reason || "runtime_evaluation_error"
246
+ };
247
+ }
248
+ if (evaluation.orderStatus === "failed") {
249
+ return {
250
+ classification: "order_failed",
251
+ reason: evaluation.reason || "orderStatus=failed"
252
+ };
253
+ }
254
+ if (evaluation.orderStatus === "skipped" || evaluation.orderStatus === "canceled" || typeof evaluation.orderSkipReason === "string" && evaluation.orderSkipReason.trim()) {
255
+ return {
256
+ classification: "gated_or_policy_blocked",
257
+ reason: evaluation.orderSkipReason || evaluation.reason || `orderStatus=${evaluation.orderStatus}`
258
+ };
259
+ }
260
+ if (evaluation.orderStatus === "completed") {
261
+ return {
262
+ classification: "completed_signal_without_match",
263
+ reason: "completed_signal_without_matching_trade"
264
+ };
265
+ }
266
+ return {
267
+ classification: "signal_evaluation_without_order_status",
268
+ reason: evaluation.reason || "signal_evaluation_without_order_status"
269
+ };
270
+ };
271
+ var summarizeMismatchDiagnostics = (items) => Object.fromEntries(
272
+ [
273
+ ...items.reduce((counts, item) => {
274
+ counts.set(
275
+ item.classification,
276
+ (counts.get(item.classification) ?? 0) + 1
277
+ );
278
+ return counts;
279
+ }, /* @__PURE__ */ new Map()).entries()
280
+ ].sort(([left], [right]) => left.localeCompare(right))
281
+ );
282
+ var buildPnlDelta = (expectedPnl, realizedPnl) => typeof expectedPnl === "number" && Number.isFinite(expectedPnl) && typeof realizedPnl === "number" && Number.isFinite(realizedPnl) ? realizedPnl - expectedPnl : null;
283
+ var buildPriceDeltaPct = (leftPrice, rightPrice) => {
284
+ if (leftPrice == null || rightPrice == null || !Number.isFinite(leftPrice) || !Number.isFinite(rightPrice) || leftPrice === 0) {
285
+ return null;
286
+ }
287
+ return Math.abs((rightPrice - leftPrice) / leftPrice * 100);
288
+ };
289
+ var toFiniteNumberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
290
+ var toAiDiagnostic = (analysis) => {
291
+ if (!analysis || typeof analysis !== "object") {
292
+ return null;
293
+ }
294
+ return {
295
+ direction: typeof analysis.direction === "string" ? analysis.direction : null,
296
+ quality: toFiniteNumberOrNull(analysis.quality),
297
+ needRetest: typeof analysis.needRetest === "boolean" ? analysis.needRetest : null,
298
+ gateDecision: typeof analysis.gateDecision === "string" ? analysis.gateDecision : null,
299
+ llmDecision: typeof analysis.llmDecision === "string" ? analysis.llmDecision : null,
300
+ qualityReason: typeof analysis.qualityReason === "string" ? analysis.qualityReason : null
301
+ };
302
+ };
303
+ var toSignalDiagnostic = ({
304
+ signal,
305
+ timestampDiffMs
306
+ }) => ({
307
+ signalId: signal.signalId,
308
+ timestamp: signal.timestamp,
309
+ timestampDiffMs,
310
+ direction: signal.direction,
311
+ orderStatus: signal.orderStatus,
312
+ orderSkipReason: signal.orderSkipReason,
313
+ ai: toAiDiagnostic(signal.aiAnalysis),
314
+ ml: signal.ml ?? null
315
+ });
316
+ var toEvaluationDiagnostic = ({
317
+ evaluation,
318
+ timestampDiffMs
319
+ }) => ({
320
+ evaluationId: evaluation.evaluationId,
321
+ timestamp: evaluation.timestamp,
322
+ timestampDiffMs,
323
+ status: evaluation.status,
324
+ reason: evaluation.reason,
325
+ signalId: evaluation.signalId,
326
+ direction: evaluation.direction,
327
+ orderStatus: evaluation.orderStatus,
328
+ orderSkipReason: evaluation.orderSkipReason,
329
+ ai: toAiDiagnostic(evaluation.aiAnalysis),
330
+ ml: evaluation.ml ?? null
331
+ });
332
+ var buildSlippageCost = ({
333
+ direction,
334
+ expectedPrice,
335
+ actualPrice,
336
+ qty,
337
+ stage
338
+ }) => {
339
+ if (typeof expectedPrice !== "number" || !Number.isFinite(expectedPrice) || typeof actualPrice !== "number" || !Number.isFinite(actualPrice) || typeof qty !== "number" || !Number.isFinite(qty)) {
340
+ return null;
341
+ }
342
+ const sideMultiplier = direction === "SHORT" ? -1 : 1;
343
+ const priceDelta = stage === "entry" ? (actualPrice - expectedPrice) * sideMultiplier : (expectedPrice - actualPrice) * sideMultiplier;
344
+ return Number((priceDelta * qty).toFixed(12));
345
+ };
346
+ var buildMatchedReplayDetail = ({
347
+ runtime,
348
+ backtest,
349
+ timestampDiffMs,
350
+ priceDeltaPct
351
+ }) => {
352
+ const exitPriceDeltaPct = buildPriceDeltaPct(
353
+ runtime.exitPrice ?? null,
354
+ backtest.exitPrice ?? null
355
+ );
356
+ const qty = runtime.qty ?? backtest.qty ?? null;
357
+ const entrySlippageCost = buildSlippageCost({
358
+ direction: runtime.direction,
359
+ expectedPrice: backtest.price,
360
+ actualPrice: runtime.price,
361
+ qty,
362
+ stage: "entry"
363
+ });
364
+ const exitSlippageCost = buildSlippageCost({
365
+ direction: runtime.direction,
366
+ expectedPrice: backtest.exitPrice,
367
+ actualPrice: runtime.exitPrice,
368
+ qty,
369
+ stage: "exit"
370
+ });
371
+ const totalSlippageCost = entrySlippageCost == null && exitSlippageCost == null ? null : Number(
372
+ ((entrySlippageCost ?? 0) + (exitSlippageCost ?? 0)).toFixed(12)
373
+ );
374
+ return {
375
+ runtime,
376
+ backtest,
377
+ timestampDiffMs,
378
+ priceDeltaPct,
379
+ exitTimestampDiffMs: buildTimestampDelta(
380
+ runtime.exitTimestamp,
381
+ backtest.exitTimestamp
382
+ ),
383
+ exitPriceDeltaPct,
384
+ exitType: {
385
+ expected: backtest.exitType ?? null,
386
+ actual: runtime.exitType ?? null,
387
+ matches: backtest.exitType && runtime.exitType ? backtest.exitType === runtime.exitType : null
388
+ },
389
+ pnl: {
390
+ expectedPnl: backtest.pnl ?? null,
391
+ realizedPnl: runtime.pnl ?? null,
392
+ delta: buildPnlDelta(backtest.pnl, runtime.pnl)
393
+ },
394
+ slippage: {
395
+ entryPriceDeltaPct: priceDeltaPct,
396
+ exitPriceDeltaPct,
397
+ entryCost: entrySlippageCost,
398
+ exitCost: exitSlippageCost,
399
+ totalCost: totalSlippageCost
400
+ }
401
+ };
402
+ };
403
+ var buildNearestCandidates = ({
404
+ entries,
405
+ candidates,
406
+ toleranceMs,
407
+ requireSameStrategy
408
+ }) => entries.map((entry) => {
409
+ const comparable = candidates.filter((candidate) => {
410
+ if (candidate.symbol !== entry.symbol || candidate.direction !== entry.direction) {
411
+ return false;
412
+ }
413
+ if (!requireSameStrategy) {
414
+ return true;
415
+ }
416
+ const entryStrategy = entry.strategy ?? entry.inferredStrategy ?? null;
417
+ const candidateStrategy = candidate.strategy ?? candidate.inferredStrategy ?? null;
418
+ return entryStrategy != null && entryStrategy === candidateStrategy;
419
+ });
420
+ const nearest = comparable.map((candidate) => ({
421
+ candidate,
422
+ timestampDiffMs: Math.abs(
423
+ getComparisonTimestamp(candidate) - getComparisonTimestamp(entry)
424
+ )
425
+ })).sort((left, right) => left.timestampDiffMs - right.timestampDiffMs)[0];
426
+ if (!nearest) {
427
+ return {
428
+ entry,
429
+ nearest: null,
430
+ timestampDiffMs: null,
431
+ priceDeltaPct: null,
432
+ reason: "no_candidate_same_symbol_direction"
433
+ };
434
+ }
435
+ const priceDeltaPct = buildPriceDeltaPct(
436
+ entry.price,
437
+ nearest.candidate.price
438
+ );
439
+ return {
440
+ entry,
441
+ nearest: nearest.candidate,
442
+ timestampDiffMs: nearest.timestampDiffMs,
443
+ priceDeltaPct,
444
+ reason: nearest.timestampDiffMs > toleranceMs ? "outside_tolerance" : "candidate_already_matched"
445
+ };
446
+ });
447
+ var buildReplayMismatchDrilldown = ({
448
+ runtimeOnly,
449
+ backtestOnly,
450
+ nearestCandidates,
451
+ runtimeSignals = [],
452
+ runtimeSignalEvaluations = [],
453
+ replaySignals = [],
454
+ replaySignalEvaluations = [],
455
+ toleranceMs,
456
+ backtestTimestampOffsetMs,
457
+ limit
458
+ }) => {
459
+ const nearestByEntry = new Map(
460
+ nearestCandidates.map((candidate) => [candidate.entry, candidate])
461
+ );
462
+ const artifactLimit = Math.min(limit, resolveReplayParityArtifactLimit());
463
+ let runtimeOnlyArtifactsIncluded = 0;
464
+ let backtestOnlyArtifactsIncluded = 0;
465
+ const runtimeOnlyDiagnostics = runtimeOnly.map((entry, index) => {
466
+ const includeArtifacts = index < artifactLimit;
467
+ const nearestCandidate = nearestByEntry.get(entry);
468
+ const nearestReplaySignal = findNearestSignal({
469
+ entry,
470
+ signals: replaySignals,
471
+ toleranceMs,
472
+ timestampOffsetMs: backtestTimestampOffsetMs
473
+ });
474
+ const nearestReplayEvaluation = findNearestEvaluation({
475
+ entry,
476
+ evaluations: replaySignalEvaluations,
477
+ toleranceMs,
478
+ timestampOffsetMs: backtestTimestampOffsetMs
479
+ });
480
+ const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestCandidate?.nearest ? {
481
+ classification: "timing_or_price_drift",
482
+ reason: nearestCandidate.reason
483
+ } : {
484
+ classification: "no_replay_candidate",
485
+ reason: "no_replay_signal_or_backtest_candidate"
486
+ };
487
+ return {
488
+ entry,
489
+ ...classification,
490
+ ...nearestCandidate ? { nearestCandidate } : {},
491
+ ...nearestReplaySignal ? {
492
+ replaySignal: toSignalDiagnostic({
493
+ signal: nearestReplaySignal.signal,
494
+ timestampDiffMs: nearestReplaySignal.timestampDiffMs
495
+ }),
496
+ ...includeArtifacts ? { replaySignalArtifact: nearestReplaySignal.signal } : {}
497
+ } : {},
498
+ ...nearestReplayEvaluation ? {
499
+ replayEvaluation: toEvaluationDiagnostic({
500
+ evaluation: nearestReplayEvaluation.evaluation,
501
+ timestampDiffMs: nearestReplayEvaluation.timestampDiffMs
502
+ }),
503
+ ...includeArtifacts ? { replayEvaluationArtifact: nearestReplayEvaluation.evaluation } : {}
504
+ } : {}
505
+ };
506
+ });
507
+ const backtestOnlyDiagnostics = backtestOnly.map((entry, index) => {
508
+ const includeArtifacts = index < artifactLimit;
509
+ const nearestCandidate = nearestByEntry.get(entry);
510
+ const nearestRuntimeSignal = findNearestSignal({
511
+ entry,
512
+ signals: runtimeSignals,
513
+ toleranceMs,
514
+ timestampOffsetMs: 0
515
+ });
516
+ const nearestRuntimeEvaluation = findNearestEvaluation({
517
+ entry,
518
+ evaluations: runtimeSignalEvaluations,
519
+ toleranceMs,
520
+ timestampOffsetMs: 0
521
+ });
522
+ const classification = nearestRuntimeSignal ? classifySignalDiagnostic(nearestRuntimeSignal.signal) : nearestRuntimeEvaluation ? classifyEvaluationDiagnostic(nearestRuntimeEvaluation.evaluation) : nearestCandidate?.nearest ? {
523
+ classification: "timing_or_price_drift",
524
+ reason: nearestCandidate.reason
525
+ } : {
526
+ classification: "no_runtime_evaluation",
527
+ reason: "no_runtime_signal_or_evaluation"
528
+ };
529
+ return {
530
+ entry,
531
+ ...classification,
532
+ ...nearestCandidate ? { nearestCandidate } : {},
533
+ ...nearestRuntimeSignal ? {
534
+ runtimeSignal: toSignalDiagnostic({
535
+ signal: nearestRuntimeSignal.signal,
536
+ timestampDiffMs: nearestRuntimeSignal.timestampDiffMs
537
+ }),
538
+ ...includeArtifacts ? { runtimeSignalArtifact: nearestRuntimeSignal.signal } : {}
539
+ } : {},
540
+ ...nearestRuntimeEvaluation ? {
541
+ runtimeEvaluation: toEvaluationDiagnostic({
542
+ evaluation: nearestRuntimeEvaluation.evaluation,
543
+ timestampDiffMs: nearestRuntimeEvaluation.timestampDiffMs
544
+ }),
545
+ ...includeArtifacts ? {
546
+ runtimeEvaluationArtifact: nearestRuntimeEvaluation.evaluation
547
+ } : {}
548
+ } : {}
549
+ };
550
+ });
551
+ for (const diagnostic of runtimeOnlyDiagnostics) {
552
+ if (diagnostic.replaySignalArtifact || diagnostic.replayEvaluationArtifact) {
553
+ runtimeOnlyArtifactsIncluded += 1;
554
+ }
555
+ }
556
+ for (const diagnostic of backtestOnlyDiagnostics) {
557
+ if (diagnostic.runtimeSignalArtifact || diagnostic.runtimeEvaluationArtifact) {
558
+ backtestOnlyArtifactsIncluded += 1;
559
+ }
560
+ }
561
+ return {
562
+ runtimeOnly: capReplayDetails(runtimeOnlyDiagnostics, limit),
563
+ backtestOnly: capReplayDetails(backtestOnlyDiagnostics, limit),
564
+ summary: {
565
+ runtimeOnly: summarizeMismatchDiagnostics(runtimeOnlyDiagnostics),
566
+ backtestOnly: summarizeMismatchDiagnostics(backtestOnlyDiagnostics),
567
+ artifacts: {
568
+ limit: artifactLimit,
569
+ runtimeOnlyIncluded: runtimeOnlyArtifactsIncluded,
570
+ runtimeOnlyOmitted: Math.max(
571
+ 0,
572
+ runtimeOnlyDiagnostics.length - runtimeOnlyArtifactsIncluded
573
+ ),
574
+ backtestOnlyIncluded: backtestOnlyArtifactsIncluded,
575
+ backtestOnlyOmitted: Math.max(
576
+ 0,
577
+ backtestOnlyDiagnostics.length - backtestOnlyArtifactsIncluded
578
+ )
579
+ }
580
+ }
581
+ };
582
+ };
583
+ var buildReplayRuntimeComparisonDetails = ({
584
+ matched,
585
+ runtimeOnly,
586
+ backtestOnly,
587
+ runtimeEntries,
588
+ backtestEntries,
589
+ toleranceMs,
590
+ limit = resolveReplayParityDetailsLimit(),
591
+ backtestTimestampOffsetMs = 0,
592
+ runtimeSignals,
593
+ runtimeSignalEvaluations,
594
+ replaySignals,
595
+ replaySignalEvaluations
596
+ }) => {
597
+ const runtimeDetails = runtimeEntries.map(toRuntimeParityDetail);
598
+ const backtestDetails = backtestEntries.map(
599
+ (entry) => toBacktestParityDetail(entry, backtestTimestampOffsetMs)
600
+ );
601
+ const runtimeOnlyDetails = runtimeOnly.map(toRuntimeParityDetail);
602
+ const backtestOnlyDetails = backtestOnly.map(
603
+ (entry) => toBacktestParityDetail(entry, backtestTimestampOffsetMs)
604
+ );
605
+ const nearestCandidates = [
606
+ ...buildNearestCandidates({
607
+ entries: runtimeOnlyDetails,
608
+ candidates: backtestDetails,
609
+ toleranceMs,
610
+ requireSameStrategy: true
611
+ }),
612
+ ...buildNearestCandidates({
613
+ entries: backtestOnlyDetails,
614
+ candidates: runtimeDetails,
615
+ toleranceMs,
616
+ requireSameStrategy: true
617
+ })
618
+ ];
619
+ const cappedNearestCandidates = capReplayDetails(nearestCandidates, limit);
620
+ const cappedRuntimeOnlyDetails = capReplayDetails(runtimeOnlyDetails, limit);
621
+ const cappedBacktestOnlyDetails = capReplayDetails(
622
+ backtestOnlyDetails,
623
+ limit
624
+ );
625
+ return {
626
+ capped: matched.length > limit || runtimeOnlyDetails.length > limit || backtestOnlyDetails.length > limit || nearestCandidates.length > limit,
627
+ limit,
628
+ matched: capReplayDetails(
629
+ matched.map(
630
+ (item) => buildMatchedReplayDetail({
631
+ runtime: toRuntimeParityDetail(item.runtime),
632
+ backtest: toBacktestParityDetail(
633
+ item.backtest,
634
+ backtestTimestampOffsetMs
635
+ ),
636
+ timestampDiffMs: item.timestampDiffMs,
637
+ priceDeltaPct: item.priceDeltaPct
638
+ })
639
+ ),
640
+ limit
641
+ ),
642
+ runtimeOnly: cappedRuntimeOnlyDetails,
643
+ backtestOnly: cappedBacktestOnlyDetails,
644
+ nearestCandidates: cappedNearestCandidates,
645
+ mismatchDrilldown: buildReplayMismatchDrilldown({
646
+ runtimeOnly: runtimeOnlyDetails,
647
+ backtestOnly: backtestOnlyDetails,
648
+ nearestCandidates,
649
+ runtimeSignals,
650
+ runtimeSignalEvaluations,
651
+ replaySignals,
652
+ replaySignalEvaluations,
653
+ toleranceMs,
654
+ backtestTimestampOffsetMs,
655
+ limit
656
+ })
657
+ };
658
+ };
659
+ var buildReplayExchangeComparisonDetails = ({
660
+ matched,
661
+ exchangeOnly,
662
+ backtestOnly,
663
+ exchangeEntries,
664
+ backtestEntries,
665
+ strategyNameByOrderLinkKey,
666
+ toleranceMs,
667
+ limit = resolveReplayParityDetailsLimit(),
668
+ backtestTimestampOffsetMs = 0,
669
+ runtimeSignals,
670
+ runtimeSignalEvaluations,
671
+ replaySignals,
672
+ replaySignalEvaluations
673
+ }) => {
674
+ const toExchangeDetail = (entry) => toExchangeParityDetail({
675
+ entry,
676
+ inferredStrategy: resolveReplayStrategyNameFromExchangeEntry({
677
+ exchangeEntry: entry,
678
+ strategyNameByOrderLinkKey
679
+ })
680
+ });
681
+ const exchangeDetails = exchangeEntries.map(toExchangeDetail);
682
+ const backtestDetails = backtestEntries.map(
683
+ (entry) => toBacktestParityDetail(entry, backtestTimestampOffsetMs)
684
+ );
685
+ const exchangeOnlyDetails = exchangeOnly.map(toExchangeDetail);
686
+ const backtestOnlyDetails = backtestOnly.map(
687
+ (entry) => toBacktestParityDetail(entry, backtestTimestampOffsetMs)
688
+ );
689
+ const nearestCandidates = [
690
+ ...buildNearestCandidates({
691
+ entries: exchangeOnlyDetails,
692
+ candidates: backtestDetails,
693
+ toleranceMs
694
+ }),
695
+ ...buildNearestCandidates({
696
+ entries: backtestOnlyDetails,
697
+ candidates: exchangeDetails,
698
+ toleranceMs
699
+ })
700
+ ];
701
+ const cappedNearestCandidates = capReplayDetails(nearestCandidates, limit);
702
+ const cappedExchangeOnlyDetails = capReplayDetails(
703
+ exchangeOnlyDetails,
704
+ limit
705
+ );
706
+ const cappedBacktestOnlyDetails = capReplayDetails(
707
+ backtestOnlyDetails,
708
+ limit
709
+ );
710
+ return {
711
+ capped: matched.length > limit || exchangeOnlyDetails.length > limit || backtestOnlyDetails.length > limit || nearestCandidates.length > limit,
712
+ limit,
713
+ matched: capReplayDetails(
714
+ matched.map(
715
+ (item) => buildMatchedReplayDetail({
716
+ runtime: toExchangeDetail(item.exchange),
717
+ backtest: toBacktestParityDetail(
718
+ item.backtest,
719
+ backtestTimestampOffsetMs
720
+ ),
721
+ timestampDiffMs: item.timestampDiffMs,
722
+ priceDeltaPct: item.priceDeltaPct
723
+ })
724
+ ),
725
+ limit
726
+ ),
727
+ runtimeOnly: cappedExchangeOnlyDetails,
728
+ backtestOnly: cappedBacktestOnlyDetails,
729
+ nearestCandidates: cappedNearestCandidates,
730
+ mismatchDrilldown: buildReplayMismatchDrilldown({
731
+ runtimeOnly: exchangeOnlyDetails,
732
+ backtestOnly: backtestOnlyDetails,
733
+ nearestCandidates,
734
+ runtimeSignals,
735
+ runtimeSignalEvaluations,
736
+ replaySignals,
737
+ replaySignalEvaluations,
738
+ toleranceMs,
739
+ backtestTimestampOffsetMs,
740
+ limit
741
+ })
742
+ };
743
+ };
744
+ // Annotate the CommonJS export names for ESM import in node:
745
+ 0 && (module.exports = {
746
+ buildReplayExchangeComparisonDetails,
747
+ buildReplayMismatchDrilldown,
748
+ buildReplayRuntimeComparisonDetails,
749
+ buildStrategyNameByOrderLinkKey,
750
+ resolveReplayStrategyNameFromExchangeEntry
751
+ });