@tradejs/cli 3.1.27-beta.245 → 3.1.27-beta.248

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 CHANGED
@@ -24893,9 +24893,21 @@ var init_replayRuntimeEvidence = __esm({
24893
24893
  const replayLineage = asArray2(comparisonLineage.replay).filter(
24894
24894
  (entry) => !useFilter || strategySet.has(String(asRecord4(entry).strategy ?? ""))
24895
24895
  );
24896
+ const hasExcludedBacktestEntryDetails = Array.isArray(
24897
+ comparisonLineage.excludedBacktestEntryDetails
24898
+ );
24899
+ const excludedBacktestEntryDetails = asArray2(
24900
+ comparisonLineage.excludedBacktestEntryDetails
24901
+ ).filter(
24902
+ (entry) => !useFilter || strategySet.has(String(asRecord4(entry).strategy ?? ""))
24903
+ );
24896
24904
  return {
24897
24905
  mode: comparison.mode ?? null,
24898
- lineage: comparisonLineage ? { ...comparisonLineage, replay: replayLineage } : null,
24906
+ lineage: comparisonLineage ? {
24907
+ ...comparisonLineage,
24908
+ replay: replayLineage,
24909
+ ...hasExcludedBacktestEntryDetails ? { excludedBacktestEntryDetails } : {}
24910
+ } : null,
24899
24911
  rows: asArray2(comparison.rows).filter(
24900
24912
  (row) => !useFilter || strategySet.has(String(row.strategyName ?? ""))
24901
24913
  ),
@@ -35380,11 +35392,90 @@ var init_historicalSignalsReplayPreparation = __esm({
35380
35392
  }
35381
35393
  });
35382
35394
 
35395
+ // src/lib/replay/historicalSignalsReplaySkipEvidence.ts
35396
+ var import_data5, replayScopeKey, appendReplaySignalEvaluationRun, createHistoricalReplaySkipEvidence;
35397
+ var init_historicalSignalsReplaySkipEvidence = __esm({
35398
+ "src/lib/replay/historicalSignalsReplaySkipEvidence.ts"() {
35399
+ "use strict";
35400
+ import_data5 = require("@tradejs/core/data");
35401
+ replayScopeKey = (strategy, symbol) => `${strategy}::${symbol}`;
35402
+ appendReplaySignalEvaluationRun = ({
35403
+ runs = [],
35404
+ reason,
35405
+ timestamp,
35406
+ interval: interval4
35407
+ }) => {
35408
+ const stepMs = (0, import_data5.intervalToMs)(interval4);
35409
+ const last = runs.at(-1);
35410
+ if (last?.reason === reason && last.stepMs === stepMs && timestamp === last.lastTimestamp + stepMs) {
35411
+ return [...runs.slice(0, -1), { ...last, lastTimestamp: timestamp }];
35412
+ }
35413
+ return [
35414
+ ...runs,
35415
+ {
35416
+ status: "skip",
35417
+ reason,
35418
+ firstTimestamp: timestamp,
35419
+ lastTimestamp: timestamp,
35420
+ stepMs
35421
+ }
35422
+ ];
35423
+ };
35424
+ createHistoricalReplaySkipEvidence = (runtimeLineages) => {
35425
+ const lineageByStrategySymbol = new Map(
35426
+ runtimeLineages.map((record2) => [
35427
+ replayScopeKey(record2.strategy, record2.symbol),
35428
+ record2
35429
+ ])
35430
+ );
35431
+ const scopes = /* @__PURE__ */ new Map();
35432
+ const record = ({
35433
+ strategyName,
35434
+ symbol,
35435
+ strategyConfig,
35436
+ runtimeLineage,
35437
+ timestamp,
35438
+ result
35439
+ }) => {
35440
+ const scopeKey = replayScopeKey(strategyName, symbol);
35441
+ const lineageRecord = lineageByStrategySymbol.get(scopeKey);
35442
+ if (!lineageRecord) return;
35443
+ const existing = scopes.get(scopeKey);
35444
+ const skipReason = typeof result === "string" ? result.trim() || "NO_SIGNAL" : result ? null : "NO_SIGNAL";
35445
+ scopes.set(scopeKey, {
35446
+ strategy: strategyName,
35447
+ symbol,
35448
+ ...lineageRecord.deploymentId ? { deploymentId: lineageRecord.deploymentId } : {},
35449
+ ...lineageRecord.accountId ? { accountId: lineageRecord.accountId } : {},
35450
+ strategyRevision: runtimeLineage.strategyRevision,
35451
+ lineage: runtimeLineage,
35452
+ firstTimestamp: Math.min(
35453
+ existing?.firstTimestamp ?? timestamp,
35454
+ timestamp
35455
+ ),
35456
+ lastTimestamp: Math.max(existing?.lastTimestamp ?? timestamp, timestamp),
35457
+ evaluationRuns: skipReason ? appendReplaySignalEvaluationRun({
35458
+ runs: existing?.evaluationRuns,
35459
+ reason: skipReason,
35460
+ timestamp,
35461
+ interval: strategyConfig.INTERVAL ?? "15"
35462
+ }) : existing?.evaluationRuns
35463
+ });
35464
+ };
35465
+ const values = () => [...scopes.values()].sort(
35466
+ (left, right) => left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol)
35467
+ );
35468
+ return { record, values };
35469
+ };
35470
+ }
35471
+ });
35472
+
35383
35473
  // src/lib/replay/historicalSignalsReplayExecution.ts
35384
35474
  var advanceCycleMarket, advanceAbortedCycle, executeHistoricalReplay;
35385
35475
  var init_historicalSignalsReplayExecution = __esm({
35386
35476
  "src/lib/replay/historicalSignalsReplayExecution.ts"() {
35387
35477
  "use strict";
35478
+ init_historicalSignalsReplaySkipEvidence();
35388
35479
  advanceCycleMarket = async (plan, connector, timestamp) => {
35389
35480
  const cycleSymbols = plan.cycleSymbolsByTimestamp.get(timestamp) ?? [];
35390
35481
  for (const symbolRuntime of cycleSymbols) {
@@ -35409,6 +35500,9 @@ var init_historicalSignalsReplayExecution = __esm({
35409
35500
  executeHistoricalReplay = async (context, adapters) => {
35410
35501
  const signals2 = [];
35411
35502
  let abortedCycles = 0;
35503
+ const skipEvidence = createHistoricalReplaySkipEvidence(
35504
+ context.plan.runtimeLineages
35505
+ );
35412
35506
  try {
35413
35507
  for (const timestamp of context.plan.orderedTimestamps) {
35414
35508
  const cycleStartedAt = adapters.clock.now();
@@ -35451,6 +35545,14 @@ var init_historicalSignalsReplayExecution = __esm({
35451
35545
  btcCandle,
35452
35546
  ethCandle?.timestamp === timestamp ? ethCandle : void 0
35453
35547
  );
35548
+ skipEvidence.record({
35549
+ strategyName: strategyRuntime.strategyName,
35550
+ symbol: symbolRuntime.symbol,
35551
+ strategyConfig: strategyRuntime.strategyConfig,
35552
+ runtimeLineage: strategyRuntime.runtimeLineage,
35553
+ timestamp,
35554
+ result
35555
+ });
35454
35556
  if (result && typeof result !== "string") {
35455
35557
  result.runtimeLineage = strategyRuntime.runtimeLineage;
35456
35558
  await adapters.enrichSignal(result);
@@ -35478,7 +35580,11 @@ var init_historicalSignalsReplayExecution = __esm({
35478
35580
  adapters.releaseReplayCache(keyPrefix);
35479
35581
  }
35480
35582
  }
35481
- return { signals: signals2, abortedCycles };
35583
+ return {
35584
+ signals: signals2,
35585
+ abortedCycles,
35586
+ replayLineageScopes: skipEvidence.values()
35587
+ };
35482
35588
  };
35483
35589
  }
35484
35590
  });
@@ -35513,7 +35619,8 @@ var init_historicalSignalsReplayResults = __esm({
35513
35619
  positionLog: context.artifacts.positionLog,
35514
35620
  cycleCount: context.cycleCount,
35515
35621
  abortedCycles: context.abortedCycles,
35516
- runtimeLineages: context.runtimeLineages
35622
+ runtimeLineages: context.runtimeLineages,
35623
+ replayLineageScopes: context.replayLineageScopes
35517
35624
  });
35518
35625
  }
35519
35626
  });
@@ -35755,7 +35862,8 @@ var init_historicalSignalsReplay = __esm({
35755
35862
  signals: execution.signals,
35756
35863
  cycleCount: plan.orderedTimestamps.length,
35757
35864
  abortedCycles: execution.abortedCycles,
35758
- runtimeLineages: plan.runtimeLineages
35865
+ runtimeLineages: plan.runtimeLineages,
35866
+ replayLineageScopes: execution.replayLineageScopes
35759
35867
  });
35760
35868
  };
35761
35869
  }
@@ -36203,7 +36311,7 @@ var init_runtimeTradeSync = __esm({
36203
36311
  });
36204
36312
 
36205
36313
  // src/lib/runtimeParityDetails.ts
36206
- var import_trade3, DEFAULT_REPLAY_PARITY_DETAILS_LIMIT, DEFAULT_REPLAY_PARITY_ARTIFACT_LIMIT, buildStrategyNameByOrderLinkKey, resolveReplayStrategyNameFromExchangeEntry, resolveReplayParityDetailsLimit, resolveReplayParityArtifactLimit, capReplayDetails, buildCostDetail, toBacktestParityDetail, toRuntimeParityDetail, toExchangeParityDetail, buildTimestampDelta, getComparisonTimestamp, getEntryStrategy, findNearestSignal, findNearestEvaluation, findNearestRuntimeEvaluationOutcome, hasRuntimeLineageCoverage, classifySignalDiagnostic, classifyEvaluationDiagnostic, summarizeMismatchDiagnostics, buildPnlDelta, buildPriceDeltaPct2, toFiniteNumberOrNull3, toAiDiagnostic, toSignalDiagnostic, toEvaluationDiagnostic, buildSlippageCost, buildMatchedReplayDetail, buildNearestCandidates, buildReplayMismatchDrilldown, buildReplayRuntimeComparisonDetails, buildReplayExchangeComparisonDetails;
36314
+ var import_trade3, DEFAULT_REPLAY_PARITY_DETAILS_LIMIT, DEFAULT_REPLAY_PARITY_ARTIFACT_LIMIT, buildStrategyNameByOrderLinkKey, resolveReplayStrategyNameFromExchangeEntry, resolveReplayParityDetailsLimit, resolveReplayParityArtifactLimit, capReplayDetails, buildCostDetail, toBacktestParityDetail, toRuntimeParityDetail, toExchangeParityDetail, buildTimestampDelta, getComparisonTimestamp, getEntryStrategy, findNearestSignal, findNearestEvaluation, findNearestRuntimeEvaluationOutcome, findNearestReplayEvaluationOutcome, hasRuntimeLineageCoverage, classifySignalDiagnostic, classifyEvaluationDiagnostic, summarizeMismatchDiagnostics, buildPnlDelta, buildPriceDeltaPct2, toFiniteNumberOrNull3, toAiDiagnostic, toSignalDiagnostic, toEvaluationDiagnostic, buildSlippageCost, buildMatchedReplayDetail, buildNearestCandidates, buildReplayMismatchDrilldown, buildReplayRuntimeComparisonDetails, buildReplayExchangeComparisonDetails;
36207
36315
  var init_runtimeParityDetails = __esm({
36208
36316
  "src/lib/runtimeParityDetails.ts"() {
36209
36317
  "use strict";
@@ -36413,6 +36521,46 @@ var init_runtimeParityDetails = __esm({
36413
36521
  }
36414
36522
  return nearest;
36415
36523
  };
36524
+ findNearestReplayEvaluationOutcome = ({
36525
+ entry,
36526
+ lineageScopes,
36527
+ toleranceMs,
36528
+ timestampOffsetMs
36529
+ }) => {
36530
+ const strategy = getEntryStrategy(entry);
36531
+ if (!strategy) return null;
36532
+ let nearest = null;
36533
+ for (const scope of lineageScopes) {
36534
+ if (scope.strategy !== strategy || scope.symbol !== entry.symbol) {
36535
+ continue;
36536
+ }
36537
+ for (const run of scope.evaluationRuns ?? []) {
36538
+ if (run.stepMs <= 0) continue;
36539
+ const targetTimestamp = entry.timestamp - timestampOffsetMs;
36540
+ if (targetTimestamp < run.firstTimestamp - toleranceMs || targetTimestamp > run.lastTimestamp + toleranceMs) {
36541
+ continue;
36542
+ }
36543
+ const step = Math.round(
36544
+ (targetTimestamp - run.firstTimestamp) / run.stepMs
36545
+ );
36546
+ const candidateTimestamp = run.firstTimestamp + step * run.stepMs;
36547
+ const timestampDiffMs = Math.abs(
36548
+ candidateTimestamp + timestampOffsetMs - entry.timestamp
36549
+ );
36550
+ if (candidateTimestamp < run.firstTimestamp || candidateTimestamp > run.lastTimestamp || timestampDiffMs > toleranceMs || nearest && timestampDiffMs >= nearest.timestampDiffMs) {
36551
+ continue;
36552
+ }
36553
+ nearest = {
36554
+ status: run.status,
36555
+ reason: run.reason,
36556
+ timestamp: candidateTimestamp,
36557
+ timestampDiffMs,
36558
+ source: "replay_scope_compact"
36559
+ };
36560
+ }
36561
+ }
36562
+ return nearest;
36563
+ };
36416
36564
  hasRuntimeLineageCoverage = ({
36417
36565
  entry,
36418
36566
  lineageScopes
@@ -36667,6 +36815,7 @@ var init_runtimeParityDetails = __esm({
36667
36815
  runtimeSignalEvaluations = [],
36668
36816
  replaySignals = [],
36669
36817
  replaySignalEvaluations = [],
36818
+ replayLineageScopes = [],
36670
36819
  runtimeLineageScopes = [],
36671
36820
  toleranceMs,
36672
36821
  backtestTimestampOffsetMs,
@@ -36693,7 +36842,16 @@ var init_runtimeParityDetails = __esm({
36693
36842
  toleranceMs,
36694
36843
  timestampOffsetMs: backtestTimestampOffsetMs
36695
36844
  });
36696
- const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestCandidate?.nearest ? {
36845
+ const nearestReplayEvaluationOutcome = findNearestReplayEvaluationOutcome({
36846
+ entry,
36847
+ lineageScopes: replayLineageScopes,
36848
+ toleranceMs,
36849
+ timestampOffsetMs: backtestTimestampOffsetMs
36850
+ });
36851
+ const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestReplayEvaluationOutcome ? {
36852
+ classification: "core_skipped",
36853
+ reason: nearestReplayEvaluationOutcome.reason
36854
+ } : nearestCandidate?.nearest ? {
36697
36855
  classification: "timing_or_price_drift",
36698
36856
  reason: nearestCandidate.reason
36699
36857
  } : {
@@ -36717,7 +36875,8 @@ var init_runtimeParityDetails = __esm({
36717
36875
  timestampDiffMs: nearestReplayEvaluation.timestampDiffMs
36718
36876
  }),
36719
36877
  ...includeArtifacts ? { replayEvaluationArtifact: nearestReplayEvaluation.evaluation } : {}
36720
- } : {}
36878
+ } : {},
36879
+ ...nearestReplayEvaluationOutcome ? { replayEvaluationOutcome: nearestReplayEvaluationOutcome } : {}
36721
36880
  };
36722
36881
  });
36723
36882
  const backtestOnlyDiagnostics = backtestOnly.map((entry, index) => {
@@ -36827,6 +36986,7 @@ var init_runtimeParityDetails = __esm({
36827
36986
  runtimeSignalEvaluations,
36828
36987
  replaySignals,
36829
36988
  replaySignalEvaluations,
36989
+ replayLineageScopes,
36830
36990
  runtimeLineageScopes
36831
36991
  }) => {
36832
36992
  const runtimeDetails = runtimeEntries.map(toRuntimeParityDetail);
@@ -36886,6 +37046,7 @@ var init_runtimeParityDetails = __esm({
36886
37046
  runtimeSignalEvaluations,
36887
37047
  replaySignals,
36888
37048
  replaySignalEvaluations,
37049
+ replayLineageScopes,
36889
37050
  runtimeLineageScopes,
36890
37051
  toleranceMs,
36891
37052
  backtestTimestampOffsetMs,
@@ -36908,6 +37069,7 @@ var init_runtimeParityDetails = __esm({
36908
37069
  runtimeSignalEvaluations,
36909
37070
  replaySignals,
36910
37071
  replaySignalEvaluations,
37072
+ replayLineageScopes,
36911
37073
  runtimeLineageScopes
36912
37074
  }) => {
36913
37075
  const toExchangeDetail = (entry) => toExchangeParityDetail({
@@ -36995,6 +37157,7 @@ var init_runtimeParityDetails = __esm({
36995
37157
  runtimeSignalEvaluations,
36996
37158
  replaySignals,
36997
37159
  replaySignalEvaluations,
37160
+ replayLineageScopes,
36998
37161
  runtimeLineageScopes,
36999
37162
  toleranceMs,
37000
37163
  backtestTimestampOffsetMs,
@@ -37127,14 +37290,35 @@ var init_runtimeComparisonCalculations = __esm({
37127
37290
  lineageScopeKey(record)
37128
37291
  ])
37129
37292
  );
37130
- const comparableBacktestEntries = backtestEntries.filter((entry) => {
37293
+ const comparableBacktestEntries = [];
37294
+ const excludedBacktestEntryDetails = [];
37295
+ for (const entry of backtestEntries) {
37131
37296
  const replayScope = replayScopeByStrategySymbol.get(
37132
37297
  `${entry.strategy}::${entry.symbol}`
37133
37298
  );
37134
37299
  const window = replayScope ? deploymentWindows.get(replayScope) : null;
37135
37300
  const signalTimestamp = entry.signalTimestamp ?? entry.timestamp;
37136
- return window != null && signalTimestamp >= window.firstTimestamp && signalTimestamp <= window.lastTimestamp;
37137
- });
37301
+ if (window != null && signalTimestamp >= window.firstTimestamp && signalTimestamp <= window.lastTimestamp) {
37302
+ comparableBacktestEntries.push(entry);
37303
+ continue;
37304
+ }
37305
+ const reason = !replayScope ? "replay_scope_missing" : !window ? "runtime_scope_missing" : signalTimestamp < window.firstTimestamp ? "before_runtime_window" : "after_runtime_window";
37306
+ excludedBacktestEntryDetails.push({
37307
+ id: entry.id,
37308
+ strategy: entry.strategy,
37309
+ symbol: entry.symbol,
37310
+ direction: entry.direction,
37311
+ ...entry.qty != null ? { qty: entry.qty } : {},
37312
+ timestamp: entry.timestamp,
37313
+ ...entry.signalTimestamp != null ? { signalTimestamp: entry.signalTimestamp } : {},
37314
+ price: entry.price,
37315
+ ...entry.orderId ? { orderId: entry.orderId } : {},
37316
+ ...entry.signalId ? { signalId: entry.signalId } : {},
37317
+ ...entry.expectedPnl != null ? { expectedPnl: entry.expectedPnl } : {},
37318
+ reason,
37319
+ runtimeWindow: window ?? null
37320
+ });
37321
+ }
37138
37322
  const comparableScopeKeys = new Set(deploymentWindows.keys());
37139
37323
  return {
37140
37324
  runtimeTrades: comparableRuntimeTrades,
@@ -37152,6 +37336,7 @@ var init_runtimeComparisonCalculations = __esm({
37152
37336
  excludedRuntimeLineageScopes: runtimeLineageScopes.length - comparableRuntimeLineageScopes.length,
37153
37337
  excludedExchangeEntries: 0,
37154
37338
  excludedBacktestEntries: backtestEntries.length - comparableBacktestEntries.length,
37339
+ excludedBacktestEntryDetails,
37155
37340
  reason: comparableScopeKeys.size > 0 ? null : expectedByScope.size === 0 ? "replay_produced_no_lineage_scopes" : "no_runtime_artifacts_with_matching_lineage",
37156
37341
  replay: [...expectedByScope.entries()].map(([scope, lineage]) => {
37157
37342
  const [deploymentId, accountId, strategy, symbol] = scope.split("::");
@@ -37763,6 +37948,7 @@ var init_runtimeComparison = __esm({
37763
37948
  backtestEntries,
37764
37949
  replaySignals,
37765
37950
  replaySignalEvaluations,
37951
+ replayLineageScopes,
37766
37952
  runtimeSignals,
37767
37953
  runtimeSignalEvaluations,
37768
37954
  runtimeLineageScopes,
@@ -37818,7 +38004,8 @@ var init_runtimeComparison = __esm({
37818
38004
  runtimeSignalEvaluations,
37819
38005
  replaySignals,
37820
38006
  replaySignalEvaluations,
37821
- runtimeLineageScopes
38007
+ runtimeLineageScopes,
38008
+ replayLineageScopes
37822
38009
  });
37823
38010
  const emptyRowsByStrategy = new Map(
37824
38011
  buildExchangeComparisonRows({
@@ -37875,7 +38062,8 @@ var init_runtimeComparison = __esm({
37875
38062
  runtimeSignalEvaluations,
37876
38063
  replaySignals,
37877
38064
  replaySignalEvaluations,
37878
- runtimeLineageScopes
38065
+ runtimeLineageScopes,
38066
+ replayLineageScopes
37879
38067
  });
37880
38068
  const rows = buildExchangeComparisonRows({
37881
38069
  liveStrategySummaries,
@@ -37913,6 +38101,7 @@ var init_runtimeComparison = __esm({
37913
38101
  backtestEntries,
37914
38102
  replaySignals,
37915
38103
  replayLineages,
38104
+ replayLineageScopes,
37916
38105
  runtimeEvidencePath
37917
38106
  }) => {
37918
38107
  const { connector, connectorName, window } = getRuntimeCompareContext();
@@ -38024,6 +38213,7 @@ var init_runtimeComparison = __esm({
38024
38213
  backtestEntries: comparableBacktestEntries,
38025
38214
  replaySignals,
38026
38215
  replaySignalEvaluations,
38216
+ replayLineageScopes,
38027
38217
  runtimeSignals: comparable.runtimeSignals,
38028
38218
  runtimeSignalEvaluations: comparable.runtimeSignalEvaluations,
38029
38219
  runtimeLineageScopes: comparable.runtimeLineageScopes,
@@ -38051,7 +38241,8 @@ var init_runtimeComparison = __esm({
38051
38241
  runtimeSignalEvaluations: comparable.runtimeSignalEvaluations,
38052
38242
  replaySignals,
38053
38243
  replaySignalEvaluations,
38054
- runtimeLineageScopes: comparable.runtimeLineageScopes
38244
+ runtimeLineageScopes: comparable.runtimeLineageScopes,
38245
+ replayLineageScopes
38055
38246
  });
38056
38247
  const parityRows = summarizeTradeParityByStrategy({
38057
38248
  runtimeEntries: runtimeDedupe.entries,
@@ -38094,13 +38285,13 @@ var init_runtimeComparison = __esm({
38094
38285
  });
38095
38286
 
38096
38287
  // src/lib/replay/outputReport.ts
38097
- var import_promises33, import_node_path30, import_data5, formatMsk, formatNumber5, formatMoney2, formatPct, tableCell, createMarkdownTable2, entryStrategy, entryKey, buildDrilldownMap, buildPerTradeAnalysis, renderMatchedRows, renderRuntimeOnlyRows, renderOrderFailedRows, renderBacktestOnlyRows, renderStrategyRows, renderLineageRows, writeReplayOutputReport;
38288
+ var import_promises33, import_node_path30, import_data6, formatMsk, formatNumber5, formatMoney2, formatPct, tableCell, createMarkdownTable2, entryStrategy, entryKey, buildDrilldownMap, buildPerTradeAnalysis, renderMatchedRows, renderRuntimeOnlyRows, renderOrderFailedRows, renderBacktestOnlyRows, renderStrategyRows, renderLineageRows, writeReplayOutputReport;
38098
38289
  var init_outputReport = __esm({
38099
38290
  "src/lib/replay/outputReport.ts"() {
38100
38291
  "use strict";
38101
38292
  import_promises33 = __toESM(require("fs/promises"));
38102
38293
  import_node_path30 = __toESM(require("path"));
38103
- import_data5 = require("@tradejs/core/data");
38294
+ import_data6 = require("@tradejs/core/data");
38104
38295
  init_support();
38105
38296
  formatMsk = (timestamp) => typeof timestamp === "number" && Number.isFinite(timestamp) ? new Intl.DateTimeFormat("ru-RU", {
38106
38297
  timeZone: "Europe/Moscow",
@@ -38508,7 +38699,7 @@ var init_outputReport = __esm({
38508
38699
  ];
38509
38700
  await Promise.all([
38510
38701
  import_promises33.default.writeFile(markdownPath, lines.join("\n"), "utf8"),
38511
- import_promises33.default.writeFile(jsonPath, `${(0, import_data5.toJson)(report, true)}
38702
+ import_promises33.default.writeFile(jsonPath, `${(0, import_data6.toJson)(report, true)}
38512
38703
  `, "utf8")
38513
38704
  ]);
38514
38705
  return {
@@ -38520,13 +38711,13 @@ var init_outputReport = __esm({
38520
38711
  });
38521
38712
 
38522
38713
  // src/scripts/replayRunner.ts
38523
- var import_chalk53, import_constants19, import_data6, import_time14, import_redis21, prepareReplayBinanceMarketContext, finishReplay, replayBacktest;
38714
+ var import_chalk53, import_constants19, import_data7, import_time14, import_redis21, prepareReplayBinanceMarketContext, finishReplay, replayBacktest;
38524
38715
  var init_replayRunner = __esm({
38525
38716
  "src/scripts/replayRunner.ts"() {
38526
38717
  "use strict";
38527
38718
  import_chalk53 = __toESM(require("chalk"));
38528
38719
  import_constants19 = require("@tradejs/core/constants");
38529
- import_data6 = require("@tradejs/core/data");
38720
+ import_data7 = require("@tradejs/core/data");
38530
38721
  import_time14 = require("@tradejs/core/time");
38531
38722
  import_redis21 = require("@tradejs/infra/redis");
38532
38723
  init_runFormatting();
@@ -38576,6 +38767,7 @@ var init_replayRunner = __esm({
38576
38767
  backtestEntries: replayStrategySnapshot.backtestEntries,
38577
38768
  replaySignals: replayResult.signals,
38578
38769
  replayLineages: replayResult.runtimeLineages,
38770
+ replayLineageScopes: replayResult.replayLineageScopes,
38579
38771
  runtimeEvidencePath: replayRuntimeEvidencePath
38580
38772
  });
38581
38773
  const finishedAt = /* @__PURE__ */ new Date();
@@ -38742,7 +38934,7 @@ var init_replayRunner = __esm({
38742
38934
  deploymentId: deployment.id,
38743
38935
  assetClasses: deployment.assetClasses,
38744
38936
  deployment,
38745
- closedIntervalMs: (0, import_data6.intervalToMs)(replayInterval)
38937
+ closedIntervalMs: (0, import_data7.intervalToMs)(replayInterval)
38746
38938
  });
38747
38939
  if (!preparedRun || isReplayUpdateOnlyRun) {
38748
38940
  return;
@@ -242,6 +242,46 @@ var findNearestRuntimeEvaluationOutcome = ({
242
242
  }
243
243
  return nearest;
244
244
  };
245
+ var findNearestReplayEvaluationOutcome = ({
246
+ entry,
247
+ lineageScopes,
248
+ toleranceMs,
249
+ timestampOffsetMs
250
+ }) => {
251
+ const strategy = getEntryStrategy(entry);
252
+ if (!strategy) return null;
253
+ let nearest = null;
254
+ for (const scope of lineageScopes) {
255
+ if (scope.strategy !== strategy || scope.symbol !== entry.symbol) {
256
+ continue;
257
+ }
258
+ for (const run of scope.evaluationRuns ?? []) {
259
+ if (run.stepMs <= 0) continue;
260
+ const targetTimestamp = entry.timestamp - timestampOffsetMs;
261
+ if (targetTimestamp < run.firstTimestamp - toleranceMs || targetTimestamp > run.lastTimestamp + toleranceMs) {
262
+ continue;
263
+ }
264
+ const step = Math.round(
265
+ (targetTimestamp - run.firstTimestamp) / run.stepMs
266
+ );
267
+ const candidateTimestamp = run.firstTimestamp + step * run.stepMs;
268
+ const timestampDiffMs = Math.abs(
269
+ candidateTimestamp + timestampOffsetMs - entry.timestamp
270
+ );
271
+ if (candidateTimestamp < run.firstTimestamp || candidateTimestamp > run.lastTimestamp || timestampDiffMs > toleranceMs || nearest && timestampDiffMs >= nearest.timestampDiffMs) {
272
+ continue;
273
+ }
274
+ nearest = {
275
+ status: run.status,
276
+ reason: run.reason,
277
+ timestamp: candidateTimestamp,
278
+ timestampDiffMs,
279
+ source: "replay_scope_compact"
280
+ };
281
+ }
282
+ }
283
+ return nearest;
284
+ };
245
285
  var hasRuntimeLineageCoverage = ({
246
286
  entry,
247
287
  lineageScopes
@@ -496,6 +536,7 @@ var buildReplayMismatchDrilldown = ({
496
536
  runtimeSignalEvaluations = [],
497
537
  replaySignals = [],
498
538
  replaySignalEvaluations = [],
539
+ replayLineageScopes = [],
499
540
  runtimeLineageScopes = [],
500
541
  toleranceMs,
501
542
  backtestTimestampOffsetMs,
@@ -522,7 +563,16 @@ var buildReplayMismatchDrilldown = ({
522
563
  toleranceMs,
523
564
  timestampOffsetMs: backtestTimestampOffsetMs
524
565
  });
525
- const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestCandidate?.nearest ? {
566
+ const nearestReplayEvaluationOutcome = findNearestReplayEvaluationOutcome({
567
+ entry,
568
+ lineageScopes: replayLineageScopes,
569
+ toleranceMs,
570
+ timestampOffsetMs: backtestTimestampOffsetMs
571
+ });
572
+ const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestReplayEvaluationOutcome ? {
573
+ classification: "core_skipped",
574
+ reason: nearestReplayEvaluationOutcome.reason
575
+ } : nearestCandidate?.nearest ? {
526
576
  classification: "timing_or_price_drift",
527
577
  reason: nearestCandidate.reason
528
578
  } : {
@@ -546,7 +596,8 @@ var buildReplayMismatchDrilldown = ({
546
596
  timestampDiffMs: nearestReplayEvaluation.timestampDiffMs
547
597
  }),
548
598
  ...includeArtifacts ? { replayEvaluationArtifact: nearestReplayEvaluation.evaluation } : {}
549
- } : {}
599
+ } : {},
600
+ ...nearestReplayEvaluationOutcome ? { replayEvaluationOutcome: nearestReplayEvaluationOutcome } : {}
550
601
  };
551
602
  });
552
603
  const backtestOnlyDiagnostics = backtestOnly.map((entry, index) => {
@@ -656,6 +707,7 @@ var buildReplayRuntimeComparisonDetails = ({
656
707
  runtimeSignalEvaluations,
657
708
  replaySignals,
658
709
  replaySignalEvaluations,
710
+ replayLineageScopes,
659
711
  runtimeLineageScopes
660
712
  }) => {
661
713
  const runtimeDetails = runtimeEntries.map(toRuntimeParityDetail);
@@ -715,6 +767,7 @@ var buildReplayRuntimeComparisonDetails = ({
715
767
  runtimeSignalEvaluations,
716
768
  replaySignals,
717
769
  replaySignalEvaluations,
770
+ replayLineageScopes,
718
771
  runtimeLineageScopes,
719
772
  toleranceMs,
720
773
  backtestTimestampOffsetMs,
@@ -737,6 +790,7 @@ var buildReplayExchangeComparisonDetails = ({
737
790
  runtimeSignalEvaluations,
738
791
  replaySignals,
739
792
  replaySignalEvaluations,
793
+ replayLineageScopes,
740
794
  runtimeLineageScopes
741
795
  }) => {
742
796
  const toExchangeDetail = (entry) => toExchangeParityDetail({
@@ -824,6 +878,7 @@ var buildReplayExchangeComparisonDetails = ({
824
878
  runtimeSignalEvaluations,
825
879
  replaySignals,
826
880
  replaySignalEvaluations,
881
+ replayLineageScopes,
827
882
  runtimeLineageScopes,
828
883
  toleranceMs,
829
884
  backtestTimestampOffsetMs,