@tradejs/cli 3.1.29-beta.289 → 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.
- package/dist/cli.js +239 -70
- package/dist/lib/runtimeFeedbackNotification.js +6 -2
- package/dist/lib/runtimeFeedbackReplay.js +44 -29
- package/dist/lib/runtimeParity.js +1 -0
- package/dist/lib/runtimeParityDetails.js +51 -21
- package/dist/scripts/replay.js +170 -29
- package/dist/scripts/replayRunner.js +170 -29
- package/dist/scripts/runtimeFeedbackNotify.js +11 -3
- package/dist/scripts/runtimeFeedbackReplay.js +44 -29
- package/dist/scripts/runtimeParity.js +1 -0
- package/dist/scripts/signalsSummary.js +5 -6
- package/package.json +7 -7
|
@@ -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 = ({
|
|
@@ -5593,6 +5599,73 @@ var invokeAfterSignalsHooks = async (hooks, params) => {
|
|
|
5593
5599
|
}
|
|
5594
5600
|
};
|
|
5595
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
|
+
|
|
5596
5669
|
// src/lib/replay/historicalSignalsReplaySkipEvidence.ts
|
|
5597
5670
|
var import_data2 = require("@tradejs/core/data");
|
|
5598
5671
|
var replayScopeKey = (strategy, symbol) => `${strategy}::${symbol}`;
|
|
@@ -5703,6 +5776,7 @@ var executeHistoricalReplay = async (context, adapters) => {
|
|
|
5703
5776
|
context.plan.runtimeLineages,
|
|
5704
5777
|
context.collectSkipEvidence
|
|
5705
5778
|
);
|
|
5779
|
+
const decisionTrace = createReplayDecisionTrace(context.decisionTraceTargets);
|
|
5706
5780
|
const reportProgress = (timestamp) => {
|
|
5707
5781
|
if (adapters.progress.enabled === false) return;
|
|
5708
5782
|
adapters.progress.tick({
|
|
@@ -5731,6 +5805,20 @@ var executeHistoricalReplay = async (context, adapters) => {
|
|
|
5731
5805
|
status: "completed",
|
|
5732
5806
|
durationMs: adapters.clock.now() - cycleStartedAt
|
|
5733
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
|
+
}
|
|
5734
5822
|
advanceAbortedCycle(cycleSymbols, timestamp);
|
|
5735
5823
|
reportProgress(timestamp);
|
|
5736
5824
|
continue;
|
|
@@ -5741,6 +5829,17 @@ var executeHistoricalReplay = async (context, adapters) => {
|
|
|
5741
5829
|
const btcCandle = symbolRuntime.btcReplayData[symbolRuntime.currentIndex];
|
|
5742
5830
|
const ethCandle = symbolRuntime.ethReplayData[symbolRuntime.currentIndex];
|
|
5743
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
|
+
}
|
|
5744
5843
|
continue;
|
|
5745
5844
|
}
|
|
5746
5845
|
for (const strategyRuntime of symbolRuntime.strategies) {
|
|
@@ -5749,6 +5848,12 @@ var executeHistoricalReplay = async (context, adapters) => {
|
|
|
5749
5848
|
btcCandle,
|
|
5750
5849
|
ethCandle?.timestamp === candle.timestamp ? ethCandle : void 0
|
|
5751
5850
|
);
|
|
5851
|
+
decisionTrace.record(
|
|
5852
|
+
strategyRuntime.strategyName,
|
|
5853
|
+
symbolRuntime.symbol,
|
|
5854
|
+
candle,
|
|
5855
|
+
result
|
|
5856
|
+
);
|
|
5752
5857
|
skipEvidence.record({
|
|
5753
5858
|
strategyName: strategyRuntime.strategyName,
|
|
5754
5859
|
symbol: symbolRuntime.symbol,
|
|
@@ -5780,7 +5885,8 @@ var executeHistoricalReplay = async (context, adapters) => {
|
|
|
5780
5885
|
return {
|
|
5781
5886
|
signals,
|
|
5782
5887
|
abortedCycles,
|
|
5783
|
-
replayLineageScopes: skipEvidence.values()
|
|
5888
|
+
replayLineageScopes: skipEvidence.values(),
|
|
5889
|
+
replayDecisionTraces: decisionTrace.values()
|
|
5784
5890
|
};
|
|
5785
5891
|
};
|
|
5786
5892
|
|
|
@@ -5831,7 +5937,8 @@ var collectHistoricalReplayResult = (context) => ({
|
|
|
5831
5937
|
cycleCount: context.cycleCount,
|
|
5832
5938
|
abortedCycles: context.abortedCycles,
|
|
5833
5939
|
runtimeLineages: context.runtimeLineages,
|
|
5834
|
-
replayLineageScopes: context.replayLineageScopes
|
|
5940
|
+
replayLineageScopes: context.replayLineageScopes,
|
|
5941
|
+
...context.replayDecisionTraces?.length ? { replayDecisionTraces: context.replayDecisionTraces } : {}
|
|
5835
5942
|
});
|
|
5836
5943
|
var compactHistoricalReplayResultForPortfolio = (result) => ({
|
|
5837
5944
|
...result,
|
|
@@ -5877,7 +5984,10 @@ var mergeHistoricalReplayResults = (parts) => {
|
|
|
5877
5984
|
cycleCount: parts.reduce((sum, part) => sum + part.cycleCount, 0),
|
|
5878
5985
|
abortedCycles: parts.reduce((sum, part) => sum + part.abortedCycles, 0),
|
|
5879
5986
|
runtimeLineages: parts.flatMap((part) => part.runtimeLineages),
|
|
5880
|
-
replayLineageScopes: parts.flatMap((part) => part.replayLineageScopes)
|
|
5987
|
+
replayLineageScopes: parts.flatMap((part) => part.replayLineageScopes),
|
|
5988
|
+
replayDecisionTraces: parts.flatMap(
|
|
5989
|
+
(part) => part.replayDecisionTraces ?? []
|
|
5990
|
+
)
|
|
5881
5991
|
};
|
|
5882
5992
|
};
|
|
5883
5993
|
|
|
@@ -6028,7 +6138,8 @@ var runHistoricalSignalsReplay = async ({
|
|
|
6028
6138
|
references,
|
|
6029
6139
|
showProgress = true,
|
|
6030
6140
|
collectSkipEvidence = true,
|
|
6031
|
-
collectSignals = true
|
|
6141
|
+
collectSignals = true,
|
|
6142
|
+
decisionTraceTargets
|
|
6032
6143
|
}) => {
|
|
6033
6144
|
const memory = createReplayMemoryProfile();
|
|
6034
6145
|
memory?.("prepare:start");
|
|
@@ -6115,6 +6226,7 @@ var runHistoricalSignalsReplay = async ({
|
|
|
6115
6226
|
connector: replayConnector,
|
|
6116
6227
|
collectSkipEvidence,
|
|
6117
6228
|
collectSignals,
|
|
6229
|
+
decisionTraceTargets,
|
|
6118
6230
|
hooks: projectHooks,
|
|
6119
6231
|
hookContext
|
|
6120
6232
|
},
|
|
@@ -6151,7 +6263,8 @@ var runHistoricalSignalsReplay = async ({
|
|
|
6151
6263
|
cycleCount: plan.orderedTimestamps.length,
|
|
6152
6264
|
abortedCycles: execution.abortedCycles,
|
|
6153
6265
|
runtimeLineages: plan.runtimeLineages,
|
|
6154
|
-
replayLineageScopes: execution.replayLineageScopes
|
|
6266
|
+
replayLineageScopes: execution.replayLineageScopes,
|
|
6267
|
+
replayDecisionTraces: execution.replayDecisionTraces
|
|
6155
6268
|
});
|
|
6156
6269
|
memory?.("result:collected");
|
|
6157
6270
|
return result;
|
|
@@ -6379,6 +6492,7 @@ var extractRuntimeParityEntries = (trades) => trades.filter(
|
|
|
6379
6492
|
direction: trade.direction,
|
|
6380
6493
|
qty: toFiniteNumberOrNull2(trade.qty),
|
|
6381
6494
|
timestamp: trade.entryTimestamp,
|
|
6495
|
+
...Number.isFinite(trade.signalTimestamp) ? { signalTimestamp: trade.signalTimestamp } : {},
|
|
6382
6496
|
price: toFiniteNumberOrNull2(trade.entryPrice),
|
|
6383
6497
|
orderId: trade.orderId,
|
|
6384
6498
|
signalId: typeof trade.signalId === "string" ? trade.signalId : void 0,
|
|
@@ -7124,6 +7238,7 @@ var toRuntimeParityDetail = (entry) => ({
|
|
|
7124
7238
|
direction: entry.direction,
|
|
7125
7239
|
qty: entry.qty ?? null,
|
|
7126
7240
|
timestamp: entry.timestamp,
|
|
7241
|
+
...entry.signalTimestamp != null ? { signalTimestamp: entry.signalTimestamp } : {},
|
|
7127
7242
|
price: entry.price,
|
|
7128
7243
|
exitType: entry.exitType ?? null,
|
|
7129
7244
|
exitTimestamp: entry.exitTimestamp ?? null,
|
|
@@ -7197,7 +7312,8 @@ var findNearestEvaluation = ({
|
|
|
7197
7312
|
evaluations,
|
|
7198
7313
|
toleranceMs,
|
|
7199
7314
|
timestampOffsetMs,
|
|
7200
|
-
entryTimestampOffsetMs = 0
|
|
7315
|
+
entryTimestampOffsetMs = 0,
|
|
7316
|
+
matchDirection = false
|
|
7201
7317
|
}) => {
|
|
7202
7318
|
const entryStrategy2 = getEntryStrategy(entry);
|
|
7203
7319
|
if (!entryStrategy2) {
|
|
@@ -7207,7 +7323,7 @@ var findNearestEvaluation = ({
|
|
|
7207
7323
|
let bestDiff = Number.POSITIVE_INFINITY;
|
|
7208
7324
|
const entryTimestamp = entry.timestamp + entryTimestampOffsetMs;
|
|
7209
7325
|
for (const evaluation of evaluations) {
|
|
7210
|
-
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) {
|
|
7211
7327
|
continue;
|
|
7212
7328
|
}
|
|
7213
7329
|
const diff = Math.abs(
|
|
@@ -7553,6 +7669,7 @@ var buildReplayMismatchDrilldown = ({
|
|
|
7553
7669
|
replaySignals = [],
|
|
7554
7670
|
replaySignalEvaluations = [],
|
|
7555
7671
|
replayLineageScopes = [],
|
|
7672
|
+
replayDecisionTraces = [],
|
|
7556
7673
|
runtimeLineageScopes = [],
|
|
7557
7674
|
toleranceMs,
|
|
7558
7675
|
backtestTimestampOffsetMs,
|
|
@@ -7561,48 +7678,66 @@ var buildReplayMismatchDrilldown = ({
|
|
|
7561
7678
|
const nearestByEntry = new Map(
|
|
7562
7679
|
nearestCandidates.map((candidate) => [candidate.entry, candidate])
|
|
7563
7680
|
);
|
|
7681
|
+
const traceByCandle = new Map(
|
|
7682
|
+
replayDecisionTraces.map((trace) => [replayDecisionTraceKey(trace), trace])
|
|
7683
|
+
);
|
|
7564
7684
|
const artifactLimit = Math.min(limit, resolveReplayParityArtifactLimit());
|
|
7565
7685
|
let runtimeOnlyArtifactsIncluded = 0;
|
|
7566
7686
|
let backtestOnlyArtifactsIncluded = 0;
|
|
7567
7687
|
const runtimeOnlyDiagnostics = runtimeOnly.map((entry, index) => {
|
|
7568
|
-
const
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
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
|
+
};
|
|
7572
7697
|
const includeArtifacts = index < artifactLimit;
|
|
7573
7698
|
const nearestCandidate = nearestByEntry.get(entry);
|
|
7574
|
-
const nearestReplaySignal = findNearestSignal({
|
|
7575
|
-
entry,
|
|
7699
|
+
const nearestReplaySignal = decisionEntry && findNearestSignal({
|
|
7700
|
+
entry: decisionEntry,
|
|
7576
7701
|
signals: replaySignals,
|
|
7577
|
-
|
|
7578
|
-
timestampOffsetMs: entryOffsetMs
|
|
7702
|
+
...decisionClock
|
|
7579
7703
|
});
|
|
7580
|
-
const nearestReplayEvaluation = findNearestEvaluation({
|
|
7581
|
-
entry,
|
|
7704
|
+
const nearestReplayEvaluation = decisionEntry && findNearestEvaluation({
|
|
7705
|
+
entry: decisionEntry,
|
|
7582
7706
|
evaluations: replaySignalEvaluations,
|
|
7583
|
-
|
|
7584
|
-
|
|
7707
|
+
matchDirection: isRuntimeTrade,
|
|
7708
|
+
...decisionClock
|
|
7585
7709
|
});
|
|
7586
|
-
const nearestReplayEvaluationOutcome = findNearestReplayEvaluationOutcome({
|
|
7587
|
-
entry,
|
|
7710
|
+
const nearestReplayEvaluationOutcome = decisionEntry && findNearestReplayEvaluationOutcome({
|
|
7711
|
+
entry: decisionEntry,
|
|
7588
7712
|
lineageScopes: replayLineageScopes,
|
|
7589
|
-
|
|
7590
|
-
timestampOffsetMs: entryOffsetMs
|
|
7713
|
+
...decisionClock
|
|
7591
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";
|
|
7592
7724
|
const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestReplayEvaluationOutcome ? {
|
|
7593
|
-
classification:
|
|
7725
|
+
classification: nearestReplayEvaluationOutcome.reason.startsWith(
|
|
7726
|
+
"BACKTEST_ENTRY_DELAY_"
|
|
7727
|
+
) ? "entry_delayed" : "core_skipped",
|
|
7594
7728
|
reason: nearestReplayEvaluationOutcome.reason
|
|
7595
|
-
} : nearestCandidate?.nearest ? {
|
|
7729
|
+
} : !isRuntimeTrade && nearestCandidate?.nearest ? {
|
|
7596
7730
|
classification: "timing_or_price_drift",
|
|
7597
7731
|
reason: nearestCandidate.reason
|
|
7598
7732
|
} : {
|
|
7599
7733
|
classification: "no_replay_candidate",
|
|
7600
|
-
reason:
|
|
7734
|
+
reason: unavailableReason
|
|
7601
7735
|
};
|
|
7602
7736
|
return {
|
|
7603
7737
|
entry,
|
|
7604
7738
|
...classification,
|
|
7605
7739
|
...nearestCandidate ? { nearestCandidate } : {},
|
|
7740
|
+
...replayDecisionTrace ? { replayDecisionTrace } : {},
|
|
7606
7741
|
...nearestReplaySignal ? {
|
|
7607
7742
|
replaySignal: toSignalDiagnostic({
|
|
7608
7743
|
signal: nearestReplaySignal.signal,
|
|
@@ -7728,6 +7863,7 @@ var buildReplayRuntimeComparisonDetails = ({
|
|
|
7728
7863
|
replaySignals,
|
|
7729
7864
|
replaySignalEvaluations,
|
|
7730
7865
|
replayLineageScopes,
|
|
7866
|
+
replayDecisionTraces,
|
|
7731
7867
|
runtimeLineageScopes
|
|
7732
7868
|
}) => {
|
|
7733
7869
|
const runtimeDetails = runtimeEntries.map(toRuntimeParityDetail);
|
|
@@ -7788,6 +7924,7 @@ var buildReplayRuntimeComparisonDetails = ({
|
|
|
7788
7924
|
replaySignals,
|
|
7789
7925
|
replaySignalEvaluations,
|
|
7790
7926
|
replayLineageScopes,
|
|
7927
|
+
replayDecisionTraces,
|
|
7791
7928
|
runtimeLineageScopes,
|
|
7792
7929
|
toleranceMs,
|
|
7793
7930
|
backtestTimestampOffsetMs,
|
|
@@ -8813,6 +8950,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8813
8950
|
replaySignals,
|
|
8814
8951
|
replayLineages,
|
|
8815
8952
|
replayLineageScopes,
|
|
8953
|
+
replayDecisionTraces,
|
|
8816
8954
|
runtimeEvidencePath
|
|
8817
8955
|
}) => {
|
|
8818
8956
|
const { connector, connectorName, window } = getRuntimeCompareContext();
|
|
@@ -8964,7 +9102,8 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8964
9102
|
replaySignals,
|
|
8965
9103
|
replaySignalEvaluations,
|
|
8966
9104
|
runtimeLineageScopes: comparable.runtimeLineageScopes,
|
|
8967
|
-
replayLineageScopes
|
|
9105
|
+
replayLineageScopes,
|
|
9106
|
+
replayDecisionTraces
|
|
8968
9107
|
});
|
|
8969
9108
|
const parityRows = summarizeTradeParityByStrategy({
|
|
8970
9109
|
runtimeEntries: runtimeDedupe.entries,
|
|
@@ -9891,6 +10030,7 @@ var finishReplay = async ({
|
|
|
9891
10030
|
replaySignals: replayResult.signals,
|
|
9892
10031
|
replayLineages: replayResult.runtimeLineages,
|
|
9893
10032
|
replayLineageScopes: replayResult.replayLineageScopes,
|
|
10033
|
+
replayDecisionTraces: replayResult.replayDecisionTraces,
|
|
9894
10034
|
runtimeEvidencePath: replayRuntimeEvidencePath
|
|
9895
10035
|
});
|
|
9896
10036
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
@@ -10240,7 +10380,8 @@ var replayBacktest = async () => {
|
|
|
10240
10380
|
references: historicalReplayReferences,
|
|
10241
10381
|
showProgress: !replayFlags.portfolioReport,
|
|
10242
10382
|
collectSkipEvidence: !replayFlags.portfolioReport,
|
|
10243
|
-
collectSignals: !replayFlags.portfolioReport
|
|
10383
|
+
collectSignals: !replayFlags.portfolioReport,
|
|
10384
|
+
decisionTraceTargets: replayFlags.portfolioReport ? void 0 : evidenceMetadata?.decisionTraceTargets
|
|
10244
10385
|
});
|
|
10245
10386
|
return replayFlags.portfolioReport ? compactHistoricalReplayResultForPortfolio(result) : result;
|
|
10246
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
|
|
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(
|
|
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.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
219
|
-
|
|
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
|
-
}
|
|
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.
|
|
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.
|
|
41
|
-
"@tradejs/core": "^3.1.29-beta.
|
|
42
|
-
"@tradejs/indicators": "^3.1.29-beta.
|
|
43
|
-
"@tradejs/infra": "^3.1.29-beta.
|
|
44
|
-
"@tradejs/node": "^3.1.29-beta.
|
|
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.
|
|
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",
|