@tradejs/cli 3.1.29-beta.288 → 3.1.29-beta.290

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.
@@ -4990,7 +4990,13 @@ var loadReplayRuntimeEvidenceMetadata = async (params) => {
4990
4990
  userName: artifact.userName,
4991
4991
  startTime: artifact.startTime,
4992
4992
  endTime: artifact.endTime,
4993
- deployment: artifact.deployment
4993
+ deployment: artifact.deployment,
4994
+ decisionTraceTargets: unwrapRows(
4995
+ artifact.runtime.trades,
4996
+ "trade"
4997
+ ).flatMap(
4998
+ ({ strategy, symbol, signalTimestamp }) => typeof signalTimestamp === "number" && Number.isFinite(signalTimestamp) && artifact.startTime != null && artifact.endTime != null && signalTimestamp >= artifact.startTime && signalTimestamp < artifact.endTime ? [{ strategy, symbol, signalTimestamp }] : []
4999
+ )
4994
5000
  };
4995
5001
  };
4996
5002
  var buildRuntimeEvidenceLineageScopes = ({
@@ -5391,7 +5397,10 @@ var createPortfolioReplayConnector = (connector) => {
5391
5397
  return {
5392
5398
  __tradejsReplayConnector: true,
5393
5399
  __tradejsTestConnector: true,
5394
- capabilities: connector.capabilities,
5400
+ capabilities: {
5401
+ ...connector.capabilities,
5402
+ orderSubmissionValidation: true
5403
+ },
5395
5404
  universe: connector.universe,
5396
5405
  accountId: connector.accountId,
5397
5406
  deploymentId: connector.deploymentId,
@@ -5423,6 +5432,7 @@ var createPortfolioReplayConnector = (connector) => {
5423
5432
  })
5424
5433
  ),
5425
5434
  placeOrder: async (order) => {
5435
+ const { beforeSubmit, ...submittedOrder } = order;
5426
5436
  if (positionsBySymbol.has(order.symbol)) {
5427
5437
  return false;
5428
5438
  }
@@ -5439,8 +5449,12 @@ var createPortfolioReplayConnector = (connector) => {
5439
5449
  stage: "entry",
5440
5450
  signal: order.signal
5441
5451
  });
5452
+ await beforeSubmit?.({
5453
+ qty: order.qty,
5454
+ executionPrice: entryPrice
5455
+ });
5442
5456
  const nextPosition = {
5443
- ...order,
5457
+ ...submittedOrder,
5444
5458
  timestamp: executionTimestamp ?? order.timestamp,
5445
5459
  price: entryPrice,
5446
5460
  signal: compactReplaySignal(order.signal),
@@ -5465,7 +5479,7 @@ var createPortfolioReplayConnector = (connector) => {
5465
5479
  logOrder({
5466
5480
  positionState,
5467
5481
  data: {
5468
- ...order,
5482
+ ...submittedOrder,
5469
5483
  timestamp: executionTimestamp ?? order.timestamp,
5470
5484
  price: entryPrice,
5471
5485
  profit,
@@ -5585,6 +5599,73 @@ var invokeAfterSignalsHooks = async (hooks, params) => {
5585
5599
  }
5586
5600
  };
5587
5601
 
5602
+ // src/lib/replay/historicalSignalsReplayDecisionTrace.ts
5603
+ var replayDecisionTraceKey = ({
5604
+ strategy,
5605
+ symbol,
5606
+ signalTimestamp
5607
+ }) => JSON.stringify([strategy, symbol, signalTimestamp]);
5608
+ var describeOutcome = (result) => {
5609
+ if (typeof result === "string") {
5610
+ return {
5611
+ stage: result.startsWith("BACKTEST_ENTRY_DELAY_") ? "entry_delay" : /(?:NO_PATTERN|POSITION_EXISTS)$/.test(result) ? "core" : "evaluation",
5612
+ reason: result
5613
+ };
5614
+ }
5615
+ if (!result) return { stage: "evaluation", reason: "NO_SIGNAL" };
5616
+ const reason = result.orderSkipReason || result.orderFailureReason || result.orderStatus || "SIGNAL";
5617
+ const patternContext = result.additionalIndicators?.tradingPatternsContext;
5618
+ return {
5619
+ stage: result.riskDecision?.status === "rejected" || /^ENTRY_(?:RR_BELOW|RISK_EXCEEDS)/.test(reason) ? "risk" : /^(?:AI_|ML_|HOOK_BEFORE_ENTRY_GATE|RESEARCH_ENTRY_POLICY)/.test(
5620
+ reason
5621
+ ) ? "gate" : result.orderStatus ? "execution" : "evaluation",
5622
+ reason,
5623
+ direction: result.direction,
5624
+ orderStatus: result.orderStatus,
5625
+ ...typeof patternContext?.selectedPattern === "string" ? { pattern: patternContext.selectedPattern } : {},
5626
+ ...typeof patternContext?.sourceCode === "string" ? { patternCode: patternContext.sourceCode } : {}
5627
+ };
5628
+ };
5629
+ var createReplayDecisionTrace = (targets = []) => {
5630
+ const traces = /* @__PURE__ */ new Map();
5631
+ const symbols = new Set(targets.map(({ symbol }) => symbol));
5632
+ for (const target of targets) {
5633
+ traces.set(replayDecisionTraceKey(target), {
5634
+ ...target,
5635
+ decision: {
5636
+ stage: "not_evaluated",
5637
+ reason: "REPLAY_CANDLE_NOT_EVALUATED"
5638
+ }
5639
+ });
5640
+ }
5641
+ const record = (strategy, symbol, candle, result, notEvaluatedReason) => {
5642
+ if (!symbols.has(symbol)) return;
5643
+ const { timestamp, open, high, low, close, volume } = candle;
5644
+ const trace = traces.get(
5645
+ replayDecisionTraceKey({ strategy, symbol, signalTimestamp: timestamp })
5646
+ );
5647
+ if (result && typeof result !== "string" && result.timestamp !== timestamp) {
5648
+ const original = traces.get(
5649
+ replayDecisionTraceKey({
5650
+ strategy,
5651
+ symbol,
5652
+ signalTimestamp: result.timestamp
5653
+ })
5654
+ );
5655
+ if (original)
5656
+ original.followUp = {
5657
+ ...describeOutcome(result),
5658
+ candleTimestamp: timestamp
5659
+ };
5660
+ notEvaluatedReason = "REPLAY_PROCESSED_DELAYED_ENTRY";
5661
+ }
5662
+ if (!trace) return;
5663
+ trace.candle = { timestamp, open, high, low, close, volume };
5664
+ trace.decision = notEvaluatedReason ? { stage: "not_evaluated", reason: notEvaluatedReason } : describeOutcome(result);
5665
+ };
5666
+ return { record, values: () => [...traces.values()] };
5667
+ };
5668
+
5588
5669
  // src/lib/replay/historicalSignalsReplaySkipEvidence.ts
5589
5670
  var import_data2 = require("@tradejs/core/data");
5590
5671
  var replayScopeKey = (strategy, symbol) => `${strategy}::${symbol}`;
@@ -5695,6 +5776,7 @@ var executeHistoricalReplay = async (context, adapters) => {
5695
5776
  context.plan.runtimeLineages,
5696
5777
  context.collectSkipEvidence
5697
5778
  );
5779
+ const decisionTrace = createReplayDecisionTrace(context.decisionTraceTargets);
5698
5780
  const reportProgress = (timestamp) => {
5699
5781
  if (adapters.progress.enabled === false) return;
5700
5782
  adapters.progress.tick({
@@ -5723,6 +5805,20 @@ var executeHistoricalReplay = async (context, adapters) => {
5723
5805
  status: "completed",
5724
5806
  durationMs: adapters.clock.now() - cycleStartedAt
5725
5807
  });
5808
+ if (context.decisionTraceTargets?.length) {
5809
+ for (const symbolRuntime of cycleSymbols) {
5810
+ const candle = symbolRuntime.replayData[symbolRuntime.currentIndex];
5811
+ for (const strategy of symbolRuntime.strategies) {
5812
+ decisionTrace.record(
5813
+ strategy.strategyName,
5814
+ symbolRuntime.symbol,
5815
+ candle,
5816
+ void 0,
5817
+ "REPLAY_CYCLE_ABORTED"
5818
+ );
5819
+ }
5820
+ }
5821
+ }
5726
5822
  advanceAbortedCycle(cycleSymbols, timestamp);
5727
5823
  reportProgress(timestamp);
5728
5824
  continue;
@@ -5733,6 +5829,17 @@ var executeHistoricalReplay = async (context, adapters) => {
5733
5829
  const btcCandle = symbolRuntime.btcReplayData[symbolRuntime.currentIndex];
5734
5830
  const ethCandle = symbolRuntime.ethReplayData[symbolRuntime.currentIndex];
5735
5831
  if (!candle || !btcCandle || candle.timestamp + (symbolRuntime.timestampOffsetMs ?? 0) !== timestamp || btcCandle.timestamp !== candle.timestamp) {
5832
+ if (candle && context.decisionTraceTargets?.length) {
5833
+ for (const strategy of symbolRuntime.strategies) {
5834
+ decisionTrace.record(
5835
+ strategy.strategyName,
5836
+ symbolRuntime.symbol,
5837
+ candle,
5838
+ void 0,
5839
+ "REPLAY_CANDLE_OR_REFERENCE_NOT_ALIGNED"
5840
+ );
5841
+ }
5842
+ }
5736
5843
  continue;
5737
5844
  }
5738
5845
  for (const strategyRuntime of symbolRuntime.strategies) {
@@ -5741,6 +5848,12 @@ var executeHistoricalReplay = async (context, adapters) => {
5741
5848
  btcCandle,
5742
5849
  ethCandle?.timestamp === candle.timestamp ? ethCandle : void 0
5743
5850
  );
5851
+ decisionTrace.record(
5852
+ strategyRuntime.strategyName,
5853
+ symbolRuntime.symbol,
5854
+ candle,
5855
+ result
5856
+ );
5744
5857
  skipEvidence.record({
5745
5858
  strategyName: strategyRuntime.strategyName,
5746
5859
  symbol: symbolRuntime.symbol,
@@ -5772,7 +5885,8 @@ var executeHistoricalReplay = async (context, adapters) => {
5772
5885
  return {
5773
5886
  signals,
5774
5887
  abortedCycles,
5775
- replayLineageScopes: skipEvidence.values()
5888
+ replayLineageScopes: skipEvidence.values(),
5889
+ replayDecisionTraces: decisionTrace.values()
5776
5890
  };
5777
5891
  };
5778
5892
 
@@ -5823,7 +5937,8 @@ var collectHistoricalReplayResult = (context) => ({
5823
5937
  cycleCount: context.cycleCount,
5824
5938
  abortedCycles: context.abortedCycles,
5825
5939
  runtimeLineages: context.runtimeLineages,
5826
- replayLineageScopes: context.replayLineageScopes
5940
+ replayLineageScopes: context.replayLineageScopes,
5941
+ ...context.replayDecisionTraces?.length ? { replayDecisionTraces: context.replayDecisionTraces } : {}
5827
5942
  });
5828
5943
  var compactHistoricalReplayResultForPortfolio = (result) => ({
5829
5944
  ...result,
@@ -5869,7 +5984,10 @@ var mergeHistoricalReplayResults = (parts) => {
5869
5984
  cycleCount: parts.reduce((sum, part) => sum + part.cycleCount, 0),
5870
5985
  abortedCycles: parts.reduce((sum, part) => sum + part.abortedCycles, 0),
5871
5986
  runtimeLineages: parts.flatMap((part) => part.runtimeLineages),
5872
- replayLineageScopes: parts.flatMap((part) => part.replayLineageScopes)
5987
+ replayLineageScopes: parts.flatMap((part) => part.replayLineageScopes),
5988
+ replayDecisionTraces: parts.flatMap(
5989
+ (part) => part.replayDecisionTraces ?? []
5990
+ )
5873
5991
  };
5874
5992
  };
5875
5993
 
@@ -6020,7 +6138,8 @@ var runHistoricalSignalsReplay = async ({
6020
6138
  references,
6021
6139
  showProgress = true,
6022
6140
  collectSkipEvidence = true,
6023
- collectSignals = true
6141
+ collectSignals = true,
6142
+ decisionTraceTargets
6024
6143
  }) => {
6025
6144
  const memory = createReplayMemoryProfile();
6026
6145
  memory?.("prepare:start");
@@ -6107,6 +6226,7 @@ var runHistoricalSignalsReplay = async ({
6107
6226
  connector: replayConnector,
6108
6227
  collectSkipEvidence,
6109
6228
  collectSignals,
6229
+ decisionTraceTargets,
6110
6230
  hooks: projectHooks,
6111
6231
  hookContext
6112
6232
  },
@@ -6143,7 +6263,8 @@ var runHistoricalSignalsReplay = async ({
6143
6263
  cycleCount: plan.orderedTimestamps.length,
6144
6264
  abortedCycles: execution.abortedCycles,
6145
6265
  runtimeLineages: plan.runtimeLineages,
6146
- replayLineageScopes: execution.replayLineageScopes
6266
+ replayLineageScopes: execution.replayLineageScopes,
6267
+ replayDecisionTraces: execution.replayDecisionTraces
6147
6268
  });
6148
6269
  memory?.("result:collected");
6149
6270
  return result;
@@ -6371,6 +6492,7 @@ var extractRuntimeParityEntries = (trades) => trades.filter(
6371
6492
  direction: trade.direction,
6372
6493
  qty: toFiniteNumberOrNull2(trade.qty),
6373
6494
  timestamp: trade.entryTimestamp,
6495
+ ...Number.isFinite(trade.signalTimestamp) ? { signalTimestamp: trade.signalTimestamp } : {},
6374
6496
  price: toFiniteNumberOrNull2(trade.entryPrice),
6375
6497
  orderId: trade.orderId,
6376
6498
  signalId: typeof trade.signalId === "string" ? trade.signalId : void 0,
@@ -7116,6 +7238,7 @@ var toRuntimeParityDetail = (entry) => ({
7116
7238
  direction: entry.direction,
7117
7239
  qty: entry.qty ?? null,
7118
7240
  timestamp: entry.timestamp,
7241
+ ...entry.signalTimestamp != null ? { signalTimestamp: entry.signalTimestamp } : {},
7119
7242
  price: entry.price,
7120
7243
  exitType: entry.exitType ?? null,
7121
7244
  exitTimestamp: entry.exitTimestamp ?? null,
@@ -7189,7 +7312,8 @@ var findNearestEvaluation = ({
7189
7312
  evaluations,
7190
7313
  toleranceMs,
7191
7314
  timestampOffsetMs,
7192
- entryTimestampOffsetMs = 0
7315
+ entryTimestampOffsetMs = 0,
7316
+ matchDirection = false
7193
7317
  }) => {
7194
7318
  const entryStrategy2 = getEntryStrategy(entry);
7195
7319
  if (!entryStrategy2) {
@@ -7199,7 +7323,7 @@ var findNearestEvaluation = ({
7199
7323
  let bestDiff = Number.POSITIVE_INFINITY;
7200
7324
  const entryTimestamp = entry.timestamp + entryTimestampOffsetMs;
7201
7325
  for (const evaluation of evaluations) {
7202
- if (evaluation.strategy !== entryStrategy2 || evaluation.symbol !== entry.symbol) {
7326
+ if (evaluation.strategy !== entryStrategy2 || evaluation.symbol !== entry.symbol || matchDirection && evaluation.direction != null && evaluation.direction !== entry.direction) {
7203
7327
  continue;
7204
7328
  }
7205
7329
  const diff = Math.abs(
@@ -7545,6 +7669,7 @@ var buildReplayMismatchDrilldown = ({
7545
7669
  replaySignals = [],
7546
7670
  replaySignalEvaluations = [],
7547
7671
  replayLineageScopes = [],
7672
+ replayDecisionTraces = [],
7548
7673
  runtimeLineageScopes = [],
7549
7674
  toleranceMs,
7550
7675
  backtestTimestampOffsetMs,
@@ -7553,48 +7678,66 @@ var buildReplayMismatchDrilldown = ({
7553
7678
  const nearestByEntry = new Map(
7554
7679
  nearestCandidates.map((candidate) => [candidate.entry, candidate])
7555
7680
  );
7681
+ const traceByCandle = new Map(
7682
+ replayDecisionTraces.map((trace) => [replayDecisionTraceKey(trace), trace])
7683
+ );
7556
7684
  const artifactLimit = Math.min(limit, resolveReplayParityArtifactLimit());
7557
7685
  let runtimeOnlyArtifactsIncluded = 0;
7558
7686
  let backtestOnlyArtifactsIncluded = 0;
7559
7687
  const runtimeOnlyDiagnostics = runtimeOnly.map((entry, index) => {
7560
- const entryOffsetMs = resolveTradeParityTimestampOffset(
7561
- getEntryStrategy(entry) ?? "",
7562
- backtestTimestampOffsetMs
7563
- );
7688
+ const isRuntimeTrade = entry.source === "runtime";
7689
+ const decisionEntry = isRuntimeTrade ? entry.signalTimestamp == null ? null : { ...entry, timestamp: entry.signalTimestamp } : entry;
7690
+ const decisionClock = {
7691
+ toleranceMs: isRuntimeTrade ? 0 : toleranceMs,
7692
+ timestampOffsetMs: isRuntimeTrade ? 0 : resolveTradeParityTimestampOffset(
7693
+ getEntryStrategy(entry) ?? "",
7694
+ backtestTimestampOffsetMs
7695
+ )
7696
+ };
7564
7697
  const includeArtifacts = index < artifactLimit;
7565
7698
  const nearestCandidate = nearestByEntry.get(entry);
7566
- const nearestReplaySignal = findNearestSignal({
7567
- entry,
7699
+ const nearestReplaySignal = decisionEntry && findNearestSignal({
7700
+ entry: decisionEntry,
7568
7701
  signals: replaySignals,
7569
- toleranceMs,
7570
- timestampOffsetMs: entryOffsetMs
7702
+ ...decisionClock
7571
7703
  });
7572
- const nearestReplayEvaluation = findNearestEvaluation({
7573
- entry,
7704
+ const nearestReplayEvaluation = decisionEntry && findNearestEvaluation({
7705
+ entry: decisionEntry,
7574
7706
  evaluations: replaySignalEvaluations,
7575
- toleranceMs,
7576
- timestampOffsetMs: entryOffsetMs
7707
+ matchDirection: isRuntimeTrade,
7708
+ ...decisionClock
7577
7709
  });
7578
- const nearestReplayEvaluationOutcome = findNearestReplayEvaluationOutcome({
7579
- entry,
7710
+ const nearestReplayEvaluationOutcome = decisionEntry && findNearestReplayEvaluationOutcome({
7711
+ entry: decisionEntry,
7580
7712
  lineageScopes: replayLineageScopes,
7581
- toleranceMs,
7582
- timestampOffsetMs: entryOffsetMs
7713
+ ...decisionClock
7583
7714
  });
7715
+ const strategy = getEntryStrategy(entry);
7716
+ const replayDecisionTrace = isRuntimeTrade && strategy && decisionEntry ? traceByCandle.get(
7717
+ replayDecisionTraceKey({
7718
+ strategy,
7719
+ symbol: entry.symbol,
7720
+ signalTimestamp: decisionEntry.timestamp
7721
+ })
7722
+ ) : void 0;
7723
+ const unavailableReason = !isRuntimeTrade ? "no_replay_signal_or_backtest_candidate" : !decisionEntry ? "replay_signal_timestamp_unavailable" : replayDecisionTrace?.decision.stage === "not_evaluated" ? replayDecisionTrace.decision.reason : "replay_evaluation_unavailable";
7584
7724
  const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestReplayEvaluationOutcome ? {
7585
- classification: "core_skipped",
7725
+ classification: nearestReplayEvaluationOutcome.reason.startsWith(
7726
+ "BACKTEST_ENTRY_DELAY_"
7727
+ ) ? "entry_delayed" : "core_skipped",
7586
7728
  reason: nearestReplayEvaluationOutcome.reason
7587
- } : nearestCandidate?.nearest ? {
7729
+ } : !isRuntimeTrade && nearestCandidate?.nearest ? {
7588
7730
  classification: "timing_or_price_drift",
7589
7731
  reason: nearestCandidate.reason
7590
7732
  } : {
7591
7733
  classification: "no_replay_candidate",
7592
- reason: "no_replay_signal_or_backtest_candidate"
7734
+ reason: unavailableReason
7593
7735
  };
7594
7736
  return {
7595
7737
  entry,
7596
7738
  ...classification,
7597
7739
  ...nearestCandidate ? { nearestCandidate } : {},
7740
+ ...replayDecisionTrace ? { replayDecisionTrace } : {},
7598
7741
  ...nearestReplaySignal ? {
7599
7742
  replaySignal: toSignalDiagnostic({
7600
7743
  signal: nearestReplaySignal.signal,
@@ -7720,6 +7863,7 @@ var buildReplayRuntimeComparisonDetails = ({
7720
7863
  replaySignals,
7721
7864
  replaySignalEvaluations,
7722
7865
  replayLineageScopes,
7866
+ replayDecisionTraces,
7723
7867
  runtimeLineageScopes
7724
7868
  }) => {
7725
7869
  const runtimeDetails = runtimeEntries.map(toRuntimeParityDetail);
@@ -7780,6 +7924,7 @@ var buildReplayRuntimeComparisonDetails = ({
7780
7924
  replaySignals,
7781
7925
  replaySignalEvaluations,
7782
7926
  replayLineageScopes,
7927
+ replayDecisionTraces,
7783
7928
  runtimeLineageScopes,
7784
7929
  toleranceMs,
7785
7930
  backtestTimestampOffsetMs,
@@ -8805,6 +8950,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
8805
8950
  replaySignals,
8806
8951
  replayLineages,
8807
8952
  replayLineageScopes,
8953
+ replayDecisionTraces,
8808
8954
  runtimeEvidencePath
8809
8955
  }) => {
8810
8956
  const { connector, connectorName, window } = getRuntimeCompareContext();
@@ -8956,7 +9102,8 @@ var saveAndPrintReplayRuntimeComparison = async ({
8956
9102
  replaySignals,
8957
9103
  replaySignalEvaluations,
8958
9104
  runtimeLineageScopes: comparable.runtimeLineageScopes,
8959
- replayLineageScopes
9105
+ replayLineageScopes,
9106
+ replayDecisionTraces
8960
9107
  });
8961
9108
  const parityRows = summarizeTradeParityByStrategy({
8962
9109
  runtimeEntries: runtimeDedupe.entries,
@@ -9883,6 +10030,7 @@ var finishReplay = async ({
9883
10030
  replaySignals: replayResult.signals,
9884
10031
  replayLineages: replayResult.runtimeLineages,
9885
10032
  replayLineageScopes: replayResult.replayLineageScopes,
10033
+ replayDecisionTraces: replayResult.replayDecisionTraces,
9886
10034
  runtimeEvidencePath: replayRuntimeEvidencePath
9887
10035
  });
9888
10036
  const finishedAt = /* @__PURE__ */ new Date();
@@ -10232,7 +10380,8 @@ var replayBacktest = async () => {
10232
10380
  references: historicalReplayReferences,
10233
10381
  showProgress: !replayFlags.portfolioReport,
10234
10382
  collectSkipEvidence: !replayFlags.portfolioReport,
10235
- collectSignals: !replayFlags.portfolioReport
10383
+ collectSignals: !replayFlags.portfolioReport,
10384
+ decisionTraceTargets: replayFlags.portfolioReport ? void 0 : evidenceMetadata?.decisionTraceTargets
10236
10385
  });
10237
10386
  return replayFlags.portfolioReport ? compactHistoricalReplayResultForPortfolio(result) : result;
10238
10387
  };
@@ -39,6 +39,9 @@ var import_node_path = __toESM(require("path"));
39
39
  var import_args = __toESM(require("args"));
40
40
  var import_chalk = __toESM(require("chalk"));
41
41
 
42
+ // src/lib/runtimeFeedbackNotification.ts
43
+ var import_userSettings = require("@tradejs/infra/userSettings");
44
+
42
45
  // src/lib/runtimeFeedbackArtifacts.ts
43
46
  var runtimeFeedbackArtifacts_exports = {};
44
47
  __reExport(runtimeFeedbackArtifacts_exports, require("@tradejs/node/diagnostics"));
@@ -223,11 +226,14 @@ var notifyRuntimeFeedbackBundle = async ({
223
226
  }) => {
224
227
  const verified = await (0, runtimeFeedbackArtifacts_exports.verifyRuntimeFeedbackReplayBundle)(bundleDir);
225
228
  const message = buildRuntimeFeedbackTelegramMessage(verified.replayEvidence);
226
- await send(message, { userName: verified.manifest.userName });
229
+ const settings = await (0, import_userSettings.getUserSettings)(verified.manifest.userName);
230
+ const sent = settings.NOTIFY_RUNTIME_DEBUG !== false;
231
+ if (sent) await send(message, { userName: verified.manifest.userName });
227
232
  return {
228
233
  artifactId: verified.manifest.artifactId,
229
234
  userName: verified.manifest.userName,
230
- message
235
+ message,
236
+ sent
231
237
  };
232
238
  };
233
239
 
@@ -249,7 +255,9 @@ var runtimeFeedbackNotify = async () => {
249
255
  }
250
256
  const result = await notifyRuntimeFeedbackBundle({ bundleDir });
251
257
  console.log(
252
- import_chalk.default.green(`Runtime feedback notification sent: ${result.artifactId}`)
258
+ import_chalk.default.green(
259
+ `Runtime feedback notification ${result.sent ? "sent" : "disabled"}: ${result.artifactId}`
260
+ )
253
261
  );
254
262
  };
255
263
  var main = runtimeFeedbackNotify;
@@ -43,6 +43,7 @@ var import_chalk = __toESM(require("chalk"));
43
43
  var import_node_child_process = require("child_process");
44
44
  var import_promises = __toESM(require("fs/promises"));
45
45
  var import_node_path = __toESM(require("path"));
46
+ var import_mcp = require("@tradejs/node/mcp");
46
47
 
47
48
  // src/lib/runtimeEvidenceDeployment.ts
48
49
  var runtimeEvidenceDeployment_exports = {};
@@ -190,44 +191,58 @@ var runLoggedCommand = async ({
190
191
  env,
191
192
  logHandle
192
193
  }) => {
193
- await logHandle.write(`
194
- $ ${[executable, ...command].join(" ")}
195
- `);
196
- await new Promise((resolve, reject) => {
197
- let pendingLogWrite = Promise.resolve();
198
- const appendLog = (chunk) => {
199
- pendingLogWrite = pendingLogWrite.then(async () => {
200
- await logHandle.write(chunk);
201
- });
202
- };
203
- const child = (0, import_node_child_process.spawn)(executable, command, {
204
- cwd,
205
- env,
206
- stdio: ["ignore", "pipe", "pipe"]
207
- });
208
- child.stdout.on("data", (chunk) => {
209
- process.stdout.write(chunk);
210
- appendLog(chunk);
211
- });
212
- child.stderr.on("data", (chunk) => {
213
- process.stderr.write(chunk);
214
- appendLog(chunk);
215
- });
194
+ await logHandle.writeFile(
195
+ "\n" + (0, import_mcp.formatMcpArtifactLogLine)(`$ ${[executable, ...command].join(" ")}`)
196
+ );
197
+ const child = (0, import_node_child_process.spawn)(executable, command, {
198
+ cwd,
199
+ env,
200
+ stdio: ["ignore", "pipe", "pipe"]
201
+ });
202
+ const exit = new Promise((resolve, reject) => {
216
203
  child.once("error", reject);
217
204
  child.once("close", (code, signal) => {
218
- void pendingLogWrite.then(() => {
219
- if (code === 0) {
220
- resolve();
221
- return;
222
- }
205
+ if (code === 0) resolve();
206
+ else {
223
207
  reject(
224
208
  new Error(
225
209
  `Runtime feedback command failed: exit=${String(code)} signal=${String(signal)}`
226
210
  )
227
211
  );
228
- }, reject);
212
+ }
229
213
  });
230
214
  });
215
+ let pendingLogWrite = Promise.resolve();
216
+ const copyLog = async (input, output) => {
217
+ const writeLine = async (line) => {
218
+ const formatted = (0, import_mcp.formatMcpArtifactLogLine)(line);
219
+ pendingLogWrite = pendingLogWrite.then(
220
+ () => logHandle.writeFile(formatted)
221
+ );
222
+ await pendingLogWrite;
223
+ output.write(formatted);
224
+ };
225
+ let pendingLine = "";
226
+ try {
227
+ for await (const chunk of input.setEncoding("utf8")) {
228
+ const lines = (pendingLine + chunk).split("\n");
229
+ pendingLine = lines.pop() ?? "";
230
+ for (const line of lines) await writeLine(line);
231
+ }
232
+ if (pendingLine) await writeLine(pendingLine);
233
+ } catch (error) {
234
+ child.kill();
235
+ throw error;
236
+ }
237
+ };
238
+ const results = await Promise.allSettled([
239
+ copyLog(child.stdout, process.stdout),
240
+ copyLog(child.stderr, process.stderr),
241
+ exit
242
+ ]);
243
+ for (const result of results) {
244
+ if (result.status === "rejected") throw result.reason;
245
+ }
231
246
  };
232
247
  var runRuntimeFeedbackReplay = async ({
233
248
  runtimeEvidencePath,
@@ -159,6 +159,7 @@ var extractRuntimeParityEntries = (trades) => trades.filter(
159
159
  direction: trade.direction,
160
160
  qty: toFiniteNumberOrNull(trade.qty),
161
161
  timestamp: trade.entryTimestamp,
162
+ ...Number.isFinite(trade.signalTimestamp) ? { signalTimestamp: trade.signalTimestamp } : {},
162
163
  price: toFiniteNumberOrNull(trade.entryPrice),
163
164
  orderId: trade.orderId,
164
165
  signalId: typeof trade.signalId === "string" ? trade.signalId : void 0,
@@ -35,6 +35,7 @@ __export(signalsSummary_exports, {
35
35
  });
36
36
  module.exports = __toCommonJS(signalsSummary_exports);
37
37
  var import_config = require("dotenv/config");
38
+ var import_userSettings = require("@tradejs/infra/userSettings");
38
39
  var import_args = __toESM(require("args"));
39
40
  var import_logger = require("@tradejs/infra/logger");
40
41
  var import_connectors = require("@tradejs/node/connectors");
@@ -1295,6 +1296,7 @@ none`
1295
1296
  };
1296
1297
  };
1297
1298
  var signalsSummary = async () => {
1299
+ const includeDebug = flags.printOnly || (await (0, import_userSettings.getUserSettings)(flags.user)).NOTIFY_RUNTIME_DEBUG !== false;
1298
1300
  const hours = Math.max(
1299
1301
  1,
1300
1302
  Number.parseInt(String(flags.hours ?? 24), 10) || 24
@@ -1415,18 +1417,15 @@ var signalsSummary = async () => {
1415
1417
  content: signalsMessage,
1416
1418
  deploymentId: scope.deploymentId
1417
1419
  });
1418
- const tradesMessageWithAttachmentSummary = appendReportAttachmentSummary({
1420
+ const tradesMessageWithAttachmentSummary = includeDebug ? appendReportAttachmentSummary({
1419
1421
  message: tradesMessage,
1420
1422
  signalsFilename: signalsAttachment.filename,
1421
1423
  filename: debugAttachment?.filename,
1422
1424
  tradesCount: debugAttachment ? scopeTrades.length : void 0,
1423
1425
  signalsCount: debugAttachment ? debugAttachment.summary?.signals ?? debugSignals.length : void 0,
1424
1426
  evaluationsCount: debugAttachment ? debugAttachment.summary?.evaluations ?? 0 : void 0
1425
- });
1426
- const attachments = [
1427
- signalsAttachment,
1428
- ...debugAttachment ? [debugAttachment] : []
1429
- ];
1427
+ }) : tradesMessage;
1428
+ const attachments = includeDebug ? [signalsAttachment, ...debugAttachment ? [debugAttachment] : []] : [];
1430
1429
  if (flags.printOnly) {
1431
1430
  console.log(tradesMessageWithAttachmentSummary);
1432
1431
  console.log("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/cli",
3
- "version": "3.1.29-beta.288",
3
+ "version": "3.1.29-beta.290",
4
4
  "description": "Official CLI for the TradeJS TypeScript framework: infra setup, backtests, signals, bots, and ML workflows.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -37,13 +37,13 @@
37
37
  "tradejs": "dist/cli.js"
38
38
  },
39
39
  "dependencies": {
40
- "@tradejs/connectors": "^3.1.29-beta.288",
41
- "@tradejs/core": "^3.1.29-beta.288",
42
- "@tradejs/indicators": "^3.1.29-beta.288",
43
- "@tradejs/infra": "^3.1.29-beta.288",
44
- "@tradejs/node": "^3.1.29-beta.288",
40
+ "@tradejs/connectors": "^3.1.29-beta.290",
41
+ "@tradejs/core": "^3.1.29-beta.290",
42
+ "@tradejs/indicators": "^3.1.29-beta.290",
43
+ "@tradejs/infra": "^3.1.29-beta.290",
44
+ "@tradejs/node": "^3.1.29-beta.290",
45
45
  "@tradejs/strategy-kit": "3.0.6",
46
- "@tradejs/types": "^3.1.29-beta.288",
46
+ "@tradejs/types": "^3.1.29-beta.290",
47
47
  "args": "^5.0.3",
48
48
  "bcryptjs": "^2.4.3",
49
49
  "chalk": "4.1.2",