@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.
@@ -37,7 +37,7 @@ module.exports = __toCommonJS(replay_exports);
37
37
  // src/scripts/replayRunner.ts
38
38
  var import_chalk13 = __toESM(require("chalk"));
39
39
  var import_constants6 = require("@tradejs/core/constants");
40
- var import_data3 = require("@tradejs/core/data");
40
+ var import_data4 = require("@tradejs/core/data");
41
41
  var import_time7 = require("@tradejs/core/time");
42
42
  var import_redis6 = require("@tradejs/infra/redis");
43
43
 
@@ -5611,6 +5611,78 @@ var prepareHistoricalReplay = async (context, adapters) => {
5611
5611
  };
5612
5612
  };
5613
5613
 
5614
+ // src/lib/replay/historicalSignalsReplaySkipEvidence.ts
5615
+ var import_data = require("@tradejs/core/data");
5616
+ var replayScopeKey = (strategy, symbol) => `${strategy}::${symbol}`;
5617
+ var appendReplaySignalEvaluationRun = ({
5618
+ runs = [],
5619
+ reason,
5620
+ timestamp,
5621
+ interval
5622
+ }) => {
5623
+ const stepMs = (0, import_data.intervalToMs)(interval);
5624
+ const last = runs.at(-1);
5625
+ if (last?.reason === reason && last.stepMs === stepMs && timestamp === last.lastTimestamp + stepMs) {
5626
+ return [...runs.slice(0, -1), { ...last, lastTimestamp: timestamp }];
5627
+ }
5628
+ return [
5629
+ ...runs,
5630
+ {
5631
+ status: "skip",
5632
+ reason,
5633
+ firstTimestamp: timestamp,
5634
+ lastTimestamp: timestamp,
5635
+ stepMs
5636
+ }
5637
+ ];
5638
+ };
5639
+ var createHistoricalReplaySkipEvidence = (runtimeLineages) => {
5640
+ const lineageByStrategySymbol = new Map(
5641
+ runtimeLineages.map((record2) => [
5642
+ replayScopeKey(record2.strategy, record2.symbol),
5643
+ record2
5644
+ ])
5645
+ );
5646
+ const scopes = /* @__PURE__ */ new Map();
5647
+ const record = ({
5648
+ strategyName,
5649
+ symbol,
5650
+ strategyConfig,
5651
+ runtimeLineage,
5652
+ timestamp,
5653
+ result
5654
+ }) => {
5655
+ const scopeKey = replayScopeKey(strategyName, symbol);
5656
+ const lineageRecord = lineageByStrategySymbol.get(scopeKey);
5657
+ if (!lineageRecord) return;
5658
+ const existing = scopes.get(scopeKey);
5659
+ const skipReason = typeof result === "string" ? result.trim() || "NO_SIGNAL" : result ? null : "NO_SIGNAL";
5660
+ scopes.set(scopeKey, {
5661
+ strategy: strategyName,
5662
+ symbol,
5663
+ ...lineageRecord.deploymentId ? { deploymentId: lineageRecord.deploymentId } : {},
5664
+ ...lineageRecord.accountId ? { accountId: lineageRecord.accountId } : {},
5665
+ strategyRevision: runtimeLineage.strategyRevision,
5666
+ lineage: runtimeLineage,
5667
+ firstTimestamp: Math.min(
5668
+ existing?.firstTimestamp ?? timestamp,
5669
+ timestamp
5670
+ ),
5671
+ lastTimestamp: Math.max(existing?.lastTimestamp ?? timestamp, timestamp),
5672
+ evaluationRuns: skipReason ? appendReplaySignalEvaluationRun({
5673
+ runs: existing?.evaluationRuns,
5674
+ reason: skipReason,
5675
+ timestamp,
5676
+ interval: strategyConfig.INTERVAL ?? "15"
5677
+ }) : existing?.evaluationRuns
5678
+ });
5679
+ };
5680
+ const values = () => [...scopes.values()].sort(
5681
+ (left, right) => left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol)
5682
+ );
5683
+ return { record, values };
5684
+ };
5685
+
5614
5686
  // src/lib/replay/historicalSignalsReplayExecution.ts
5615
5687
  var advanceCycleMarket = async (plan, connector, timestamp) => {
5616
5688
  const cycleSymbols = plan.cycleSymbolsByTimestamp.get(timestamp) ?? [];
@@ -5636,6 +5708,9 @@ var advanceAbortedCycle = (cycleSymbols, timestamp) => {
5636
5708
  var executeHistoricalReplay = async (context, adapters) => {
5637
5709
  const signals = [];
5638
5710
  let abortedCycles = 0;
5711
+ const skipEvidence = createHistoricalReplaySkipEvidence(
5712
+ context.plan.runtimeLineages
5713
+ );
5639
5714
  try {
5640
5715
  for (const timestamp of context.plan.orderedTimestamps) {
5641
5716
  const cycleStartedAt = adapters.clock.now();
@@ -5678,6 +5753,14 @@ var executeHistoricalReplay = async (context, adapters) => {
5678
5753
  btcCandle,
5679
5754
  ethCandle?.timestamp === timestamp ? ethCandle : void 0
5680
5755
  );
5756
+ skipEvidence.record({
5757
+ strategyName: strategyRuntime.strategyName,
5758
+ symbol: symbolRuntime.symbol,
5759
+ strategyConfig: strategyRuntime.strategyConfig,
5760
+ runtimeLineage: strategyRuntime.runtimeLineage,
5761
+ timestamp,
5762
+ result
5763
+ });
5681
5764
  if (result && typeof result !== "string") {
5682
5765
  result.runtimeLineage = strategyRuntime.runtimeLineage;
5683
5766
  await adapters.enrichSignal(result);
@@ -5705,7 +5788,11 @@ var executeHistoricalReplay = async (context, adapters) => {
5705
5788
  adapters.releaseReplayCache(keyPrefix);
5706
5789
  }
5707
5790
  }
5708
- return { signals, abortedCycles };
5791
+ return {
5792
+ signals,
5793
+ abortedCycles,
5794
+ replayLineageScopes: skipEvidence.values()
5795
+ };
5709
5796
  };
5710
5797
 
5711
5798
  // src/lib/replay/historicalSignalsReplayResults.ts
@@ -5734,7 +5821,8 @@ var collectHistoricalReplayResult = (context) => ({
5734
5821
  positionLog: context.artifacts.positionLog,
5735
5822
  cycleCount: context.cycleCount,
5736
5823
  abortedCycles: context.abortedCycles,
5737
- runtimeLineages: context.runtimeLineages
5824
+ runtimeLineages: context.runtimeLineages,
5825
+ replayLineageScopes: context.replayLineageScopes
5738
5826
  });
5739
5827
 
5740
5828
  // src/lib/replay/historicalSignalsReplay.ts
@@ -5953,7 +6041,8 @@ var runHistoricalSignalsReplay = async ({
5953
6041
  signals: execution.signals,
5954
6042
  cycleCount: plan.orderedTimestamps.length,
5955
6043
  abortedCycles: execution.abortedCycles,
5956
- runtimeLineages: plan.runtimeLineages
6044
+ runtimeLineages: plan.runtimeLineages,
6045
+ replayLineageScopes: execution.replayLineageScopes
5957
6046
  });
5958
6047
  };
5959
6048
 
@@ -6440,7 +6529,7 @@ var import_redis4 = require("@tradejs/infra/redis");
6440
6529
 
6441
6530
  // src/lib/runtimeSignalsStorage.ts
6442
6531
  var import_constants4 = require("@tradejs/core/constants");
6443
- var import_data = require("@tradejs/core/data");
6532
+ var import_data2 = require("@tradejs/core/data");
6444
6533
  var import_time4 = require("@tradejs/core/time");
6445
6534
  var isRuntimeSignalBucketRef = (value) => {
6446
6535
  if (!value || typeof value !== "object") {
@@ -7023,6 +7112,46 @@ var findNearestRuntimeEvaluationOutcome = ({
7023
7112
  }
7024
7113
  return nearest;
7025
7114
  };
7115
+ var findNearestReplayEvaluationOutcome = ({
7116
+ entry,
7117
+ lineageScopes,
7118
+ toleranceMs,
7119
+ timestampOffsetMs
7120
+ }) => {
7121
+ const strategy = getEntryStrategy(entry);
7122
+ if (!strategy) return null;
7123
+ let nearest = null;
7124
+ for (const scope of lineageScopes) {
7125
+ if (scope.strategy !== strategy || scope.symbol !== entry.symbol) {
7126
+ continue;
7127
+ }
7128
+ for (const run of scope.evaluationRuns ?? []) {
7129
+ if (run.stepMs <= 0) continue;
7130
+ const targetTimestamp = entry.timestamp - timestampOffsetMs;
7131
+ if (targetTimestamp < run.firstTimestamp - toleranceMs || targetTimestamp > run.lastTimestamp + toleranceMs) {
7132
+ continue;
7133
+ }
7134
+ const step = Math.round(
7135
+ (targetTimestamp - run.firstTimestamp) / run.stepMs
7136
+ );
7137
+ const candidateTimestamp = run.firstTimestamp + step * run.stepMs;
7138
+ const timestampDiffMs = Math.abs(
7139
+ candidateTimestamp + timestampOffsetMs - entry.timestamp
7140
+ );
7141
+ if (candidateTimestamp < run.firstTimestamp || candidateTimestamp > run.lastTimestamp || timestampDiffMs > toleranceMs || nearest && timestampDiffMs >= nearest.timestampDiffMs) {
7142
+ continue;
7143
+ }
7144
+ nearest = {
7145
+ status: run.status,
7146
+ reason: run.reason,
7147
+ timestamp: candidateTimestamp,
7148
+ timestampDiffMs,
7149
+ source: "replay_scope_compact"
7150
+ };
7151
+ }
7152
+ }
7153
+ return nearest;
7154
+ };
7026
7155
  var hasRuntimeLineageCoverage = ({
7027
7156
  entry,
7028
7157
  lineageScopes
@@ -7277,6 +7406,7 @@ var buildReplayMismatchDrilldown = ({
7277
7406
  runtimeSignalEvaluations = [],
7278
7407
  replaySignals = [],
7279
7408
  replaySignalEvaluations = [],
7409
+ replayLineageScopes = [],
7280
7410
  runtimeLineageScopes = [],
7281
7411
  toleranceMs,
7282
7412
  backtestTimestampOffsetMs,
@@ -7303,7 +7433,16 @@ var buildReplayMismatchDrilldown = ({
7303
7433
  toleranceMs,
7304
7434
  timestampOffsetMs: backtestTimestampOffsetMs
7305
7435
  });
7306
- const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestCandidate?.nearest ? {
7436
+ const nearestReplayEvaluationOutcome = findNearestReplayEvaluationOutcome({
7437
+ entry,
7438
+ lineageScopes: replayLineageScopes,
7439
+ toleranceMs,
7440
+ timestampOffsetMs: backtestTimestampOffsetMs
7441
+ });
7442
+ const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestReplayEvaluationOutcome ? {
7443
+ classification: "core_skipped",
7444
+ reason: nearestReplayEvaluationOutcome.reason
7445
+ } : nearestCandidate?.nearest ? {
7307
7446
  classification: "timing_or_price_drift",
7308
7447
  reason: nearestCandidate.reason
7309
7448
  } : {
@@ -7327,7 +7466,8 @@ var buildReplayMismatchDrilldown = ({
7327
7466
  timestampDiffMs: nearestReplayEvaluation.timestampDiffMs
7328
7467
  }),
7329
7468
  ...includeArtifacts ? { replayEvaluationArtifact: nearestReplayEvaluation.evaluation } : {}
7330
- } : {}
7469
+ } : {},
7470
+ ...nearestReplayEvaluationOutcome ? { replayEvaluationOutcome: nearestReplayEvaluationOutcome } : {}
7331
7471
  };
7332
7472
  });
7333
7473
  const backtestOnlyDiagnostics = backtestOnly.map((entry, index) => {
@@ -7437,6 +7577,7 @@ var buildReplayRuntimeComparisonDetails = ({
7437
7577
  runtimeSignalEvaluations,
7438
7578
  replaySignals,
7439
7579
  replaySignalEvaluations,
7580
+ replayLineageScopes,
7440
7581
  runtimeLineageScopes
7441
7582
  }) => {
7442
7583
  const runtimeDetails = runtimeEntries.map(toRuntimeParityDetail);
@@ -7496,6 +7637,7 @@ var buildReplayRuntimeComparisonDetails = ({
7496
7637
  runtimeSignalEvaluations,
7497
7638
  replaySignals,
7498
7639
  replaySignalEvaluations,
7640
+ replayLineageScopes,
7499
7641
  runtimeLineageScopes,
7500
7642
  toleranceMs,
7501
7643
  backtestTimestampOffsetMs,
@@ -7518,6 +7660,7 @@ var buildReplayExchangeComparisonDetails = ({
7518
7660
  runtimeSignalEvaluations,
7519
7661
  replaySignals,
7520
7662
  replaySignalEvaluations,
7663
+ replayLineageScopes,
7521
7664
  runtimeLineageScopes
7522
7665
  }) => {
7523
7666
  const toExchangeDetail = (entry) => toExchangeParityDetail({
@@ -7605,6 +7748,7 @@ var buildReplayExchangeComparisonDetails = ({
7605
7748
  runtimeSignalEvaluations,
7606
7749
  replaySignals,
7607
7750
  replaySignalEvaluations,
7751
+ replayLineageScopes,
7608
7752
  runtimeLineageScopes,
7609
7753
  toleranceMs,
7610
7754
  backtestTimestampOffsetMs,
@@ -7728,14 +7872,35 @@ var filterReplayComparisonByLineage = ({
7728
7872
  lineageScopeKey(record)
7729
7873
  ])
7730
7874
  );
7731
- const comparableBacktestEntries = backtestEntries.filter((entry) => {
7875
+ const comparableBacktestEntries = [];
7876
+ const excludedBacktestEntryDetails = [];
7877
+ for (const entry of backtestEntries) {
7732
7878
  const replayScope = replayScopeByStrategySymbol.get(
7733
7879
  `${entry.strategy}::${entry.symbol}`
7734
7880
  );
7735
7881
  const window = replayScope ? deploymentWindows.get(replayScope) : null;
7736
7882
  const signalTimestamp = entry.signalTimestamp ?? entry.timestamp;
7737
- return window != null && signalTimestamp >= window.firstTimestamp && signalTimestamp <= window.lastTimestamp;
7738
- });
7883
+ if (window != null && signalTimestamp >= window.firstTimestamp && signalTimestamp <= window.lastTimestamp) {
7884
+ comparableBacktestEntries.push(entry);
7885
+ continue;
7886
+ }
7887
+ const reason = !replayScope ? "replay_scope_missing" : !window ? "runtime_scope_missing" : signalTimestamp < window.firstTimestamp ? "before_runtime_window" : "after_runtime_window";
7888
+ excludedBacktestEntryDetails.push({
7889
+ id: entry.id,
7890
+ strategy: entry.strategy,
7891
+ symbol: entry.symbol,
7892
+ direction: entry.direction,
7893
+ ...entry.qty != null ? { qty: entry.qty } : {},
7894
+ timestamp: entry.timestamp,
7895
+ ...entry.signalTimestamp != null ? { signalTimestamp: entry.signalTimestamp } : {},
7896
+ price: entry.price,
7897
+ ...entry.orderId ? { orderId: entry.orderId } : {},
7898
+ ...entry.signalId ? { signalId: entry.signalId } : {},
7899
+ ...entry.expectedPnl != null ? { expectedPnl: entry.expectedPnl } : {},
7900
+ reason,
7901
+ runtimeWindow: window ?? null
7902
+ });
7903
+ }
7739
7904
  const comparableScopeKeys = new Set(deploymentWindows.keys());
7740
7905
  return {
7741
7906
  runtimeTrades: comparableRuntimeTrades,
@@ -7753,6 +7918,7 @@ var filterReplayComparisonByLineage = ({
7753
7918
  excludedRuntimeLineageScopes: runtimeLineageScopes.length - comparableRuntimeLineageScopes.length,
7754
7919
  excludedExchangeEntries: 0,
7755
7920
  excludedBacktestEntries: backtestEntries.length - comparableBacktestEntries.length,
7921
+ excludedBacktestEntryDetails,
7756
7922
  reason: comparableScopeKeys.size > 0 ? null : expectedByScope.size === 0 ? "replay_produced_no_lineage_scopes" : "no_runtime_artifacts_with_matching_lineage",
7757
7923
  replay: [...expectedByScope.entries()].map(([scope, lineage]) => {
7758
7924
  const [deploymentId, accountId, strategy, symbol] = scope.split("::");
@@ -8336,6 +8502,7 @@ var saveAndPrintReplayExchangeComparison = async ({
8336
8502
  backtestEntries,
8337
8503
  replaySignals,
8338
8504
  replaySignalEvaluations,
8505
+ replayLineageScopes,
8339
8506
  runtimeSignals,
8340
8507
  runtimeSignalEvaluations,
8341
8508
  runtimeLineageScopes,
@@ -8391,7 +8558,8 @@ var saveAndPrintReplayExchangeComparison = async ({
8391
8558
  runtimeSignalEvaluations,
8392
8559
  replaySignals,
8393
8560
  replaySignalEvaluations,
8394
- runtimeLineageScopes
8561
+ runtimeLineageScopes,
8562
+ replayLineageScopes
8395
8563
  });
8396
8564
  const emptyRowsByStrategy = new Map(
8397
8565
  buildExchangeComparisonRows({
@@ -8448,7 +8616,8 @@ var saveAndPrintReplayExchangeComparison = async ({
8448
8616
  runtimeSignalEvaluations,
8449
8617
  replaySignals,
8450
8618
  replaySignalEvaluations,
8451
- runtimeLineageScopes
8619
+ runtimeLineageScopes,
8620
+ replayLineageScopes
8452
8621
  });
8453
8622
  const rows = buildExchangeComparisonRows({
8454
8623
  liveStrategySummaries,
@@ -8486,6 +8655,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
8486
8655
  backtestEntries,
8487
8656
  replaySignals,
8488
8657
  replayLineages,
8658
+ replayLineageScopes,
8489
8659
  runtimeEvidencePath
8490
8660
  }) => {
8491
8661
  const { connector, connectorName, window } = getRuntimeCompareContext();
@@ -8597,6 +8767,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
8597
8767
  backtestEntries: comparableBacktestEntries,
8598
8768
  replaySignals,
8599
8769
  replaySignalEvaluations,
8770
+ replayLineageScopes,
8600
8771
  runtimeSignals: comparable.runtimeSignals,
8601
8772
  runtimeSignalEvaluations: comparable.runtimeSignalEvaluations,
8602
8773
  runtimeLineageScopes: comparable.runtimeLineageScopes,
@@ -8624,7 +8795,8 @@ var saveAndPrintReplayRuntimeComparison = async ({
8624
8795
  runtimeSignalEvaluations: comparable.runtimeSignalEvaluations,
8625
8796
  replaySignals,
8626
8797
  replaySignalEvaluations,
8627
- runtimeLineageScopes: comparable.runtimeLineageScopes
8798
+ runtimeLineageScopes: comparable.runtimeLineageScopes,
8799
+ replayLineageScopes
8628
8800
  });
8629
8801
  const parityRows = summarizeTradeParityByStrategy({
8630
8802
  runtimeEntries: runtimeDedupe.entries,
@@ -8667,7 +8839,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
8667
8839
  // src/lib/replay/outputReport.ts
8668
8840
  var import_promises2 = __toESM(require("fs/promises"));
8669
8841
  var import_node_path2 = __toESM(require("path"));
8670
- var import_data2 = require("@tradejs/core/data");
8842
+ var import_data3 = require("@tradejs/core/data");
8671
8843
  var formatMsk = (timestamp) => typeof timestamp === "number" && Number.isFinite(timestamp) ? new Intl.DateTimeFormat("ru-RU", {
8672
8844
  timeZone: "Europe/Moscow",
8673
8845
  dateStyle: "short",
@@ -9074,7 +9246,7 @@ var writeReplayOutputReport = async ({
9074
9246
  ];
9075
9247
  await Promise.all([
9076
9248
  import_promises2.default.writeFile(markdownPath, lines.join("\n"), "utf8"),
9077
- import_promises2.default.writeFile(jsonPath, `${(0, import_data2.toJson)(report, true)}
9249
+ import_promises2.default.writeFile(jsonPath, `${(0, import_data3.toJson)(report, true)}
9078
9250
  `, "utf8")
9079
9251
  ]);
9080
9252
  return {
@@ -9116,6 +9288,7 @@ var finishReplay = async ({
9116
9288
  backtestEntries: replayStrategySnapshot.backtestEntries,
9117
9289
  replaySignals: replayResult.signals,
9118
9290
  replayLineages: replayResult.runtimeLineages,
9291
+ replayLineageScopes: replayResult.replayLineageScopes,
9119
9292
  runtimeEvidencePath: replayRuntimeEvidencePath
9120
9293
  });
9121
9294
  const finishedAt = /* @__PURE__ */ new Date();
@@ -9282,7 +9455,7 @@ var replayBacktest = async () => {
9282
9455
  deploymentId: deployment.id,
9283
9456
  assetClasses: deployment.assetClasses,
9284
9457
  deployment,
9285
- closedIntervalMs: (0, import_data3.intervalToMs)(replayInterval)
9458
+ closedIntervalMs: (0, import_data4.intervalToMs)(replayInterval)
9286
9459
  });
9287
9460
  if (!preparedRun || isReplayUpdateOnlyRun) {
9288
9461
  return;