@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.
- package/dist/cli.js +250 -73
- 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 +181 -32
- package/dist/scripts/replayRunner.js +181 -32
- 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 = ({
|
|
@@ -5391,7 +5397,10 @@ var createPortfolioReplayConnector = (connector) => {
|
|
|
5391
5397
|
return {
|
|
5392
5398
|
__tradejsReplayConnector: true,
|
|
5393
5399
|
__tradejsTestConnector: true,
|
|
5394
|
-
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
|
-
...
|
|
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
|
-
...
|
|
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
|
|
7561
|
-
|
|
7562
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7576
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
|
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",
|