@tradejs/cli 3.1.29-beta.289 → 3.1.29-beta.291
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 +2574 -1236
- package/dist/lib/aiPocketSearch.js +37 -3
- package/dist/lib/aiTrainDataset.js +1 -0
- package/dist/lib/aiTrainEvaluationDump.js +7 -2
- package/dist/lib/aiTrainResearch.js +11 -7
- package/dist/lib/coreResearch.js +12 -4
- package/dist/lib/researchMetricSnapshot.js +74 -0
- package/dist/lib/researchVisuals.js +910 -0
- 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/lib/strategyRelease.js +8 -2
- package/dist/lib/strategyResearch.js +133 -33
- package/dist/lib/tickerProfiles.js +76 -0
- package/dist/scripts/aiPocketSearch.js +38 -3
- package/dist/scripts/aiTrain.js +29 -17
- package/dist/scripts/coreResearch.js +12 -4
- package/dist/scripts/replay.js +223 -33
- package/dist/scripts/replayRunner.js +223 -33
- package/dist/scripts/researchVisuals.js +955 -0
- 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/dist/scripts/strategyRelease.js +59 -7
- package/dist/scripts/strategyResearch.js +136 -36
- package/dist/scripts/tickerProfiles.js +114 -0
- package/package.json +8 -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 = ({
|
|
@@ -5182,6 +5188,9 @@ var createPortfolioReplayConnector = (connector) => {
|
|
|
5182
5188
|
let executionTimestamp = null;
|
|
5183
5189
|
const positionsBySymbol = /* @__PURE__ */ new Map();
|
|
5184
5190
|
const currentPriceBySymbol = /* @__PURE__ */ new Map();
|
|
5191
|
+
const markedAtBySymbol = /* @__PURE__ */ new Map();
|
|
5192
|
+
const entryFacts = /* @__PURE__ */ new Map();
|
|
5193
|
+
let portfolioObserved = false;
|
|
5185
5194
|
const orderLog = [];
|
|
5186
5195
|
const positionLog = [];
|
|
5187
5196
|
const orderLogByStrategy = /* @__PURE__ */ new Map();
|
|
@@ -5419,14 +5428,51 @@ var createPortfolioReplayConnector = (connector) => {
|
|
|
5419
5428
|
const { strategyName: _strategyName, ...nextPosition } = position;
|
|
5420
5429
|
return nextPosition;
|
|
5421
5430
|
}),
|
|
5431
|
+
getPortfolioObservation: async (asOf, reservations) => {
|
|
5432
|
+
portfolioObserved = true;
|
|
5433
|
+
const pendingIds = new Set(reservations.map((r) => r.id));
|
|
5434
|
+
for (const id of entryFacts.keys())
|
|
5435
|
+
if (!pendingIds.has(id)) entryFacts.delete(id);
|
|
5436
|
+
return {
|
|
5437
|
+
asOf,
|
|
5438
|
+
positions: Array.from(positionsBySymbol.values()).map(
|
|
5439
|
+
({ position, stopLossPrice }) => ({
|
|
5440
|
+
id: position.orderId ?? `${position.strategyName}:${position.symbol}:${position.timestamp}`,
|
|
5441
|
+
strategy: position.strategyName === UNKNOWN_STRATEGY ? null : position.strategyName,
|
|
5442
|
+
symbol: position.symbol,
|
|
5443
|
+
direction: position.direction,
|
|
5444
|
+
qty: position.qty,
|
|
5445
|
+
entryPrice: position.price,
|
|
5446
|
+
markPrice: currentPriceBySymbol.get(position.symbol) ?? null,
|
|
5447
|
+
markedAt: markedAtBySymbol.get(position.symbol) ?? null,
|
|
5448
|
+
stopLossPrice
|
|
5449
|
+
})
|
|
5450
|
+
),
|
|
5451
|
+
orders: reservations.map(
|
|
5452
|
+
(r) => entryFacts.get(r.id) ?? {
|
|
5453
|
+
id: r.id,
|
|
5454
|
+
status: "pending",
|
|
5455
|
+
filledQty: 0
|
|
5456
|
+
}
|
|
5457
|
+
)
|
|
5458
|
+
};
|
|
5459
|
+
},
|
|
5422
5460
|
getOpenPositionPnl: async () => Array.from(positionsBySymbol.values()).map(
|
|
5423
|
-
({ position }) =>
|
|
5424
|
-
|
|
5425
|
-
|
|
5461
|
+
({ position, stopLossPrice }) => ({
|
|
5462
|
+
...getPositionPnlSnapshot({
|
|
5463
|
+
position,
|
|
5464
|
+
currentPrice: currentPriceBySymbol.get(position.symbol) ?? position.price
|
|
5465
|
+
}),
|
|
5466
|
+
observedAt: markedAtBySymbol.get(position.symbol),
|
|
5467
|
+
stopLossPrice
|
|
5426
5468
|
})
|
|
5427
5469
|
),
|
|
5428
5470
|
placeOrder: async (order) => {
|
|
5429
|
-
const {
|
|
5471
|
+
const {
|
|
5472
|
+
beforeSubmit,
|
|
5473
|
+
onRejected: _onRejected,
|
|
5474
|
+
...submittedOrder
|
|
5475
|
+
} = order;
|
|
5430
5476
|
if (positionsBySymbol.has(order.symbol)) {
|
|
5431
5477
|
return false;
|
|
5432
5478
|
}
|
|
@@ -5470,6 +5516,14 @@ var createPortfolioReplayConnector = (connector) => {
|
|
|
5470
5516
|
stopLossPrice: null
|
|
5471
5517
|
};
|
|
5472
5518
|
positionsBySymbol.set(order.symbol, positionState);
|
|
5519
|
+
markedAtBySymbol.set(order.symbol, executionTimestamp ?? order.timestamp);
|
|
5520
|
+
if (portfolioObserved && order.orderId)
|
|
5521
|
+
entryFacts.set(order.orderId, {
|
|
5522
|
+
id: order.orderId,
|
|
5523
|
+
status: "filled",
|
|
5524
|
+
filledQty: order.qty,
|
|
5525
|
+
positionId: order.orderId
|
|
5526
|
+
});
|
|
5473
5527
|
logOrder({
|
|
5474
5528
|
positionState,
|
|
5475
5529
|
data: {
|
|
@@ -5553,6 +5607,7 @@ var createPortfolioReplayConnector = (connector) => {
|
|
|
5553
5607
|
getTickers: connector.getTickers,
|
|
5554
5608
|
advanceMarket: async ({ symbol, candle }) => {
|
|
5555
5609
|
executionTimestamp = candle.timestamp;
|
|
5610
|
+
markedAtBySymbol.set(symbol, candle.timestamp);
|
|
5556
5611
|
currentPriceBySymbol.set(symbol, candle.close);
|
|
5557
5612
|
await checkSl({ symbol, candle });
|
|
5558
5613
|
await checkTp({ symbol, candle });
|
|
@@ -5593,6 +5648,73 @@ var invokeAfterSignalsHooks = async (hooks, params) => {
|
|
|
5593
5648
|
}
|
|
5594
5649
|
};
|
|
5595
5650
|
|
|
5651
|
+
// src/lib/replay/historicalSignalsReplayDecisionTrace.ts
|
|
5652
|
+
var replayDecisionTraceKey = ({
|
|
5653
|
+
strategy,
|
|
5654
|
+
symbol,
|
|
5655
|
+
signalTimestamp
|
|
5656
|
+
}) => JSON.stringify([strategy, symbol, signalTimestamp]);
|
|
5657
|
+
var describeOutcome = (result) => {
|
|
5658
|
+
if (typeof result === "string") {
|
|
5659
|
+
return {
|
|
5660
|
+
stage: result.startsWith("BACKTEST_ENTRY_DELAY_") ? "entry_delay" : /(?:NO_PATTERN|POSITION_EXISTS)$/.test(result) ? "core" : "evaluation",
|
|
5661
|
+
reason: result
|
|
5662
|
+
};
|
|
5663
|
+
}
|
|
5664
|
+
if (!result) return { stage: "evaluation", reason: "NO_SIGNAL" };
|
|
5665
|
+
const reason = result.orderSkipReason || result.orderFailureReason || result.orderStatus || "SIGNAL";
|
|
5666
|
+
const patternContext = result.additionalIndicators?.tradingPatternsContext;
|
|
5667
|
+
return {
|
|
5668
|
+
stage: result.riskDecision?.status === "rejected" || /^ENTRY_(?:RR_BELOW|RISK_EXCEEDS)/.test(reason) ? "risk" : /^(?:AI_|ML_|HOOK_BEFORE_ENTRY_GATE|RESEARCH_ENTRY_POLICY)/.test(
|
|
5669
|
+
reason
|
|
5670
|
+
) ? "gate" : result.orderStatus ? "execution" : "evaluation",
|
|
5671
|
+
reason,
|
|
5672
|
+
direction: result.direction,
|
|
5673
|
+
orderStatus: result.orderStatus,
|
|
5674
|
+
...typeof patternContext?.selectedPattern === "string" ? { pattern: patternContext.selectedPattern } : {},
|
|
5675
|
+
...typeof patternContext?.sourceCode === "string" ? { patternCode: patternContext.sourceCode } : {}
|
|
5676
|
+
};
|
|
5677
|
+
};
|
|
5678
|
+
var createReplayDecisionTrace = (targets = []) => {
|
|
5679
|
+
const traces = /* @__PURE__ */ new Map();
|
|
5680
|
+
const symbols = new Set(targets.map(({ symbol }) => symbol));
|
|
5681
|
+
for (const target of targets) {
|
|
5682
|
+
traces.set(replayDecisionTraceKey(target), {
|
|
5683
|
+
...target,
|
|
5684
|
+
decision: {
|
|
5685
|
+
stage: "not_evaluated",
|
|
5686
|
+
reason: "REPLAY_CANDLE_NOT_EVALUATED"
|
|
5687
|
+
}
|
|
5688
|
+
});
|
|
5689
|
+
}
|
|
5690
|
+
const record = (strategy, symbol, candle, result, notEvaluatedReason) => {
|
|
5691
|
+
if (!symbols.has(symbol)) return;
|
|
5692
|
+
const { timestamp, open, high, low, close, volume } = candle;
|
|
5693
|
+
const trace = traces.get(
|
|
5694
|
+
replayDecisionTraceKey({ strategy, symbol, signalTimestamp: timestamp })
|
|
5695
|
+
);
|
|
5696
|
+
if (result && typeof result !== "string" && result.timestamp !== timestamp) {
|
|
5697
|
+
const original = traces.get(
|
|
5698
|
+
replayDecisionTraceKey({
|
|
5699
|
+
strategy,
|
|
5700
|
+
symbol,
|
|
5701
|
+
signalTimestamp: result.timestamp
|
|
5702
|
+
})
|
|
5703
|
+
);
|
|
5704
|
+
if (original)
|
|
5705
|
+
original.followUp = {
|
|
5706
|
+
...describeOutcome(result),
|
|
5707
|
+
candleTimestamp: timestamp
|
|
5708
|
+
};
|
|
5709
|
+
notEvaluatedReason = "REPLAY_PROCESSED_DELAYED_ENTRY";
|
|
5710
|
+
}
|
|
5711
|
+
if (!trace) return;
|
|
5712
|
+
trace.candle = { timestamp, open, high, low, close, volume };
|
|
5713
|
+
trace.decision = notEvaluatedReason ? { stage: "not_evaluated", reason: notEvaluatedReason } : describeOutcome(result);
|
|
5714
|
+
};
|
|
5715
|
+
return { record, values: () => [...traces.values()] };
|
|
5716
|
+
};
|
|
5717
|
+
|
|
5596
5718
|
// src/lib/replay/historicalSignalsReplaySkipEvidence.ts
|
|
5597
5719
|
var import_data2 = require("@tradejs/core/data");
|
|
5598
5720
|
var replayScopeKey = (strategy, symbol) => `${strategy}::${symbol}`;
|
|
@@ -5703,6 +5825,7 @@ var executeHistoricalReplay = async (context, adapters) => {
|
|
|
5703
5825
|
context.plan.runtimeLineages,
|
|
5704
5826
|
context.collectSkipEvidence
|
|
5705
5827
|
);
|
|
5828
|
+
const decisionTrace = createReplayDecisionTrace(context.decisionTraceTargets);
|
|
5706
5829
|
const reportProgress = (timestamp) => {
|
|
5707
5830
|
if (adapters.progress.enabled === false) return;
|
|
5708
5831
|
adapters.progress.tick({
|
|
@@ -5731,6 +5854,20 @@ var executeHistoricalReplay = async (context, adapters) => {
|
|
|
5731
5854
|
status: "completed",
|
|
5732
5855
|
durationMs: adapters.clock.now() - cycleStartedAt
|
|
5733
5856
|
});
|
|
5857
|
+
if (context.decisionTraceTargets?.length) {
|
|
5858
|
+
for (const symbolRuntime of cycleSymbols) {
|
|
5859
|
+
const candle = symbolRuntime.replayData[symbolRuntime.currentIndex];
|
|
5860
|
+
for (const strategy of symbolRuntime.strategies) {
|
|
5861
|
+
decisionTrace.record(
|
|
5862
|
+
strategy.strategyName,
|
|
5863
|
+
symbolRuntime.symbol,
|
|
5864
|
+
candle,
|
|
5865
|
+
void 0,
|
|
5866
|
+
"REPLAY_CYCLE_ABORTED"
|
|
5867
|
+
);
|
|
5868
|
+
}
|
|
5869
|
+
}
|
|
5870
|
+
}
|
|
5734
5871
|
advanceAbortedCycle(cycleSymbols, timestamp);
|
|
5735
5872
|
reportProgress(timestamp);
|
|
5736
5873
|
continue;
|
|
@@ -5741,6 +5878,17 @@ var executeHistoricalReplay = async (context, adapters) => {
|
|
|
5741
5878
|
const btcCandle = symbolRuntime.btcReplayData[symbolRuntime.currentIndex];
|
|
5742
5879
|
const ethCandle = symbolRuntime.ethReplayData[symbolRuntime.currentIndex];
|
|
5743
5880
|
if (!candle || !btcCandle || candle.timestamp + (symbolRuntime.timestampOffsetMs ?? 0) !== timestamp || btcCandle.timestamp !== candle.timestamp) {
|
|
5881
|
+
if (candle && context.decisionTraceTargets?.length) {
|
|
5882
|
+
for (const strategy of symbolRuntime.strategies) {
|
|
5883
|
+
decisionTrace.record(
|
|
5884
|
+
strategy.strategyName,
|
|
5885
|
+
symbolRuntime.symbol,
|
|
5886
|
+
candle,
|
|
5887
|
+
void 0,
|
|
5888
|
+
"REPLAY_CANDLE_OR_REFERENCE_NOT_ALIGNED"
|
|
5889
|
+
);
|
|
5890
|
+
}
|
|
5891
|
+
}
|
|
5744
5892
|
continue;
|
|
5745
5893
|
}
|
|
5746
5894
|
for (const strategyRuntime of symbolRuntime.strategies) {
|
|
@@ -5749,6 +5897,12 @@ var executeHistoricalReplay = async (context, adapters) => {
|
|
|
5749
5897
|
btcCandle,
|
|
5750
5898
|
ethCandle?.timestamp === candle.timestamp ? ethCandle : void 0
|
|
5751
5899
|
);
|
|
5900
|
+
decisionTrace.record(
|
|
5901
|
+
strategyRuntime.strategyName,
|
|
5902
|
+
symbolRuntime.symbol,
|
|
5903
|
+
candle,
|
|
5904
|
+
result
|
|
5905
|
+
);
|
|
5752
5906
|
skipEvidence.record({
|
|
5753
5907
|
strategyName: strategyRuntime.strategyName,
|
|
5754
5908
|
symbol: symbolRuntime.symbol,
|
|
@@ -5780,7 +5934,8 @@ var executeHistoricalReplay = async (context, adapters) => {
|
|
|
5780
5934
|
return {
|
|
5781
5935
|
signals,
|
|
5782
5936
|
abortedCycles,
|
|
5783
|
-
replayLineageScopes: skipEvidence.values()
|
|
5937
|
+
replayLineageScopes: skipEvidence.values(),
|
|
5938
|
+
replayDecisionTraces: decisionTrace.values()
|
|
5784
5939
|
};
|
|
5785
5940
|
};
|
|
5786
5941
|
|
|
@@ -5831,7 +5986,8 @@ var collectHistoricalReplayResult = (context) => ({
|
|
|
5831
5986
|
cycleCount: context.cycleCount,
|
|
5832
5987
|
abortedCycles: context.abortedCycles,
|
|
5833
5988
|
runtimeLineages: context.runtimeLineages,
|
|
5834
|
-
replayLineageScopes: context.replayLineageScopes
|
|
5989
|
+
replayLineageScopes: context.replayLineageScopes,
|
|
5990
|
+
...context.replayDecisionTraces?.length ? { replayDecisionTraces: context.replayDecisionTraces } : {}
|
|
5835
5991
|
});
|
|
5836
5992
|
var compactHistoricalReplayResultForPortfolio = (result) => ({
|
|
5837
5993
|
...result,
|
|
@@ -5877,7 +6033,10 @@ var mergeHistoricalReplayResults = (parts) => {
|
|
|
5877
6033
|
cycleCount: parts.reduce((sum, part) => sum + part.cycleCount, 0),
|
|
5878
6034
|
abortedCycles: parts.reduce((sum, part) => sum + part.abortedCycles, 0),
|
|
5879
6035
|
runtimeLineages: parts.flatMap((part) => part.runtimeLineages),
|
|
5880
|
-
replayLineageScopes: parts.flatMap((part) => part.replayLineageScopes)
|
|
6036
|
+
replayLineageScopes: parts.flatMap((part) => part.replayLineageScopes),
|
|
6037
|
+
replayDecisionTraces: parts.flatMap(
|
|
6038
|
+
(part) => part.replayDecisionTraces ?? []
|
|
6039
|
+
)
|
|
5881
6040
|
};
|
|
5882
6041
|
};
|
|
5883
6042
|
|
|
@@ -6028,7 +6187,8 @@ var runHistoricalSignalsReplay = async ({
|
|
|
6028
6187
|
references,
|
|
6029
6188
|
showProgress = true,
|
|
6030
6189
|
collectSkipEvidence = true,
|
|
6031
|
-
collectSignals = true
|
|
6190
|
+
collectSignals = true,
|
|
6191
|
+
decisionTraceTargets
|
|
6032
6192
|
}) => {
|
|
6033
6193
|
const memory = createReplayMemoryProfile();
|
|
6034
6194
|
memory?.("prepare:start");
|
|
@@ -6115,6 +6275,7 @@ var runHistoricalSignalsReplay = async ({
|
|
|
6115
6275
|
connector: replayConnector,
|
|
6116
6276
|
collectSkipEvidence,
|
|
6117
6277
|
collectSignals,
|
|
6278
|
+
decisionTraceTargets,
|
|
6118
6279
|
hooks: projectHooks,
|
|
6119
6280
|
hookContext
|
|
6120
6281
|
},
|
|
@@ -6151,7 +6312,8 @@ var runHistoricalSignalsReplay = async ({
|
|
|
6151
6312
|
cycleCount: plan.orderedTimestamps.length,
|
|
6152
6313
|
abortedCycles: execution.abortedCycles,
|
|
6153
6314
|
runtimeLineages: plan.runtimeLineages,
|
|
6154
|
-
replayLineageScopes: execution.replayLineageScopes
|
|
6315
|
+
replayLineageScopes: execution.replayLineageScopes,
|
|
6316
|
+
replayDecisionTraces: execution.replayDecisionTraces
|
|
6155
6317
|
});
|
|
6156
6318
|
memory?.("result:collected");
|
|
6157
6319
|
return result;
|
|
@@ -6379,6 +6541,7 @@ var extractRuntimeParityEntries = (trades) => trades.filter(
|
|
|
6379
6541
|
direction: trade.direction,
|
|
6380
6542
|
qty: toFiniteNumberOrNull2(trade.qty),
|
|
6381
6543
|
timestamp: trade.entryTimestamp,
|
|
6544
|
+
...Number.isFinite(trade.signalTimestamp) ? { signalTimestamp: trade.signalTimestamp } : {},
|
|
6382
6545
|
price: toFiniteNumberOrNull2(trade.entryPrice),
|
|
6383
6546
|
orderId: trade.orderId,
|
|
6384
6547
|
signalId: typeof trade.signalId === "string" ? trade.signalId : void 0,
|
|
@@ -7124,6 +7287,7 @@ var toRuntimeParityDetail = (entry) => ({
|
|
|
7124
7287
|
direction: entry.direction,
|
|
7125
7288
|
qty: entry.qty ?? null,
|
|
7126
7289
|
timestamp: entry.timestamp,
|
|
7290
|
+
...entry.signalTimestamp != null ? { signalTimestamp: entry.signalTimestamp } : {},
|
|
7127
7291
|
price: entry.price,
|
|
7128
7292
|
exitType: entry.exitType ?? null,
|
|
7129
7293
|
exitTimestamp: entry.exitTimestamp ?? null,
|
|
@@ -7197,7 +7361,8 @@ var findNearestEvaluation = ({
|
|
|
7197
7361
|
evaluations,
|
|
7198
7362
|
toleranceMs,
|
|
7199
7363
|
timestampOffsetMs,
|
|
7200
|
-
entryTimestampOffsetMs = 0
|
|
7364
|
+
entryTimestampOffsetMs = 0,
|
|
7365
|
+
matchDirection = false
|
|
7201
7366
|
}) => {
|
|
7202
7367
|
const entryStrategy2 = getEntryStrategy(entry);
|
|
7203
7368
|
if (!entryStrategy2) {
|
|
@@ -7207,7 +7372,7 @@ var findNearestEvaluation = ({
|
|
|
7207
7372
|
let bestDiff = Number.POSITIVE_INFINITY;
|
|
7208
7373
|
const entryTimestamp = entry.timestamp + entryTimestampOffsetMs;
|
|
7209
7374
|
for (const evaluation of evaluations) {
|
|
7210
|
-
if (evaluation.strategy !== entryStrategy2 || evaluation.symbol !== entry.symbol) {
|
|
7375
|
+
if (evaluation.strategy !== entryStrategy2 || evaluation.symbol !== entry.symbol || matchDirection && evaluation.direction != null && evaluation.direction !== entry.direction) {
|
|
7211
7376
|
continue;
|
|
7212
7377
|
}
|
|
7213
7378
|
const diff = Math.abs(
|
|
@@ -7553,6 +7718,7 @@ var buildReplayMismatchDrilldown = ({
|
|
|
7553
7718
|
replaySignals = [],
|
|
7554
7719
|
replaySignalEvaluations = [],
|
|
7555
7720
|
replayLineageScopes = [],
|
|
7721
|
+
replayDecisionTraces = [],
|
|
7556
7722
|
runtimeLineageScopes = [],
|
|
7557
7723
|
toleranceMs,
|
|
7558
7724
|
backtestTimestampOffsetMs,
|
|
@@ -7561,48 +7727,66 @@ var buildReplayMismatchDrilldown = ({
|
|
|
7561
7727
|
const nearestByEntry = new Map(
|
|
7562
7728
|
nearestCandidates.map((candidate) => [candidate.entry, candidate])
|
|
7563
7729
|
);
|
|
7730
|
+
const traceByCandle = new Map(
|
|
7731
|
+
replayDecisionTraces.map((trace) => [replayDecisionTraceKey(trace), trace])
|
|
7732
|
+
);
|
|
7564
7733
|
const artifactLimit = Math.min(limit, resolveReplayParityArtifactLimit());
|
|
7565
7734
|
let runtimeOnlyArtifactsIncluded = 0;
|
|
7566
7735
|
let backtestOnlyArtifactsIncluded = 0;
|
|
7567
7736
|
const runtimeOnlyDiagnostics = runtimeOnly.map((entry, index) => {
|
|
7568
|
-
const
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
|
|
7737
|
+
const isRuntimeTrade = entry.source === "runtime";
|
|
7738
|
+
const decisionEntry = isRuntimeTrade ? entry.signalTimestamp == null ? null : { ...entry, timestamp: entry.signalTimestamp } : entry;
|
|
7739
|
+
const decisionClock = {
|
|
7740
|
+
toleranceMs: isRuntimeTrade ? 0 : toleranceMs,
|
|
7741
|
+
timestampOffsetMs: isRuntimeTrade ? 0 : resolveTradeParityTimestampOffset(
|
|
7742
|
+
getEntryStrategy(entry) ?? "",
|
|
7743
|
+
backtestTimestampOffsetMs
|
|
7744
|
+
)
|
|
7745
|
+
};
|
|
7572
7746
|
const includeArtifacts = index < artifactLimit;
|
|
7573
7747
|
const nearestCandidate = nearestByEntry.get(entry);
|
|
7574
|
-
const nearestReplaySignal = findNearestSignal({
|
|
7575
|
-
entry,
|
|
7748
|
+
const nearestReplaySignal = decisionEntry && findNearestSignal({
|
|
7749
|
+
entry: decisionEntry,
|
|
7576
7750
|
signals: replaySignals,
|
|
7577
|
-
|
|
7578
|
-
timestampOffsetMs: entryOffsetMs
|
|
7751
|
+
...decisionClock
|
|
7579
7752
|
});
|
|
7580
|
-
const nearestReplayEvaluation = findNearestEvaluation({
|
|
7581
|
-
entry,
|
|
7753
|
+
const nearestReplayEvaluation = decisionEntry && findNearestEvaluation({
|
|
7754
|
+
entry: decisionEntry,
|
|
7582
7755
|
evaluations: replaySignalEvaluations,
|
|
7583
|
-
|
|
7584
|
-
|
|
7756
|
+
matchDirection: isRuntimeTrade,
|
|
7757
|
+
...decisionClock
|
|
7585
7758
|
});
|
|
7586
|
-
const nearestReplayEvaluationOutcome = findNearestReplayEvaluationOutcome({
|
|
7587
|
-
entry,
|
|
7759
|
+
const nearestReplayEvaluationOutcome = decisionEntry && findNearestReplayEvaluationOutcome({
|
|
7760
|
+
entry: decisionEntry,
|
|
7588
7761
|
lineageScopes: replayLineageScopes,
|
|
7589
|
-
|
|
7590
|
-
timestampOffsetMs: entryOffsetMs
|
|
7762
|
+
...decisionClock
|
|
7591
7763
|
});
|
|
7764
|
+
const strategy = getEntryStrategy(entry);
|
|
7765
|
+
const replayDecisionTrace = isRuntimeTrade && strategy && decisionEntry ? traceByCandle.get(
|
|
7766
|
+
replayDecisionTraceKey({
|
|
7767
|
+
strategy,
|
|
7768
|
+
symbol: entry.symbol,
|
|
7769
|
+
signalTimestamp: decisionEntry.timestamp
|
|
7770
|
+
})
|
|
7771
|
+
) : void 0;
|
|
7772
|
+
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
7773
|
const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestReplayEvaluationOutcome ? {
|
|
7593
|
-
classification:
|
|
7774
|
+
classification: nearestReplayEvaluationOutcome.reason.startsWith(
|
|
7775
|
+
"BACKTEST_ENTRY_DELAY_"
|
|
7776
|
+
) ? "entry_delayed" : "core_skipped",
|
|
7594
7777
|
reason: nearestReplayEvaluationOutcome.reason
|
|
7595
|
-
} : nearestCandidate?.nearest ? {
|
|
7778
|
+
} : !isRuntimeTrade && nearestCandidate?.nearest ? {
|
|
7596
7779
|
classification: "timing_or_price_drift",
|
|
7597
7780
|
reason: nearestCandidate.reason
|
|
7598
7781
|
} : {
|
|
7599
7782
|
classification: "no_replay_candidate",
|
|
7600
|
-
reason:
|
|
7783
|
+
reason: unavailableReason
|
|
7601
7784
|
};
|
|
7602
7785
|
return {
|
|
7603
7786
|
entry,
|
|
7604
7787
|
...classification,
|
|
7605
7788
|
...nearestCandidate ? { nearestCandidate } : {},
|
|
7789
|
+
...replayDecisionTrace ? { replayDecisionTrace } : {},
|
|
7606
7790
|
...nearestReplaySignal ? {
|
|
7607
7791
|
replaySignal: toSignalDiagnostic({
|
|
7608
7792
|
signal: nearestReplaySignal.signal,
|
|
@@ -7728,6 +7912,7 @@ var buildReplayRuntimeComparisonDetails = ({
|
|
|
7728
7912
|
replaySignals,
|
|
7729
7913
|
replaySignalEvaluations,
|
|
7730
7914
|
replayLineageScopes,
|
|
7915
|
+
replayDecisionTraces,
|
|
7731
7916
|
runtimeLineageScopes
|
|
7732
7917
|
}) => {
|
|
7733
7918
|
const runtimeDetails = runtimeEntries.map(toRuntimeParityDetail);
|
|
@@ -7788,6 +7973,7 @@ var buildReplayRuntimeComparisonDetails = ({
|
|
|
7788
7973
|
replaySignals,
|
|
7789
7974
|
replaySignalEvaluations,
|
|
7790
7975
|
replayLineageScopes,
|
|
7976
|
+
replayDecisionTraces,
|
|
7791
7977
|
runtimeLineageScopes,
|
|
7792
7978
|
toleranceMs,
|
|
7793
7979
|
backtestTimestampOffsetMs,
|
|
@@ -8813,6 +8999,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8813
8999
|
replaySignals,
|
|
8814
9000
|
replayLineages,
|
|
8815
9001
|
replayLineageScopes,
|
|
9002
|
+
replayDecisionTraces,
|
|
8816
9003
|
runtimeEvidencePath
|
|
8817
9004
|
}) => {
|
|
8818
9005
|
const { connector, connectorName, window } = getRuntimeCompareContext();
|
|
@@ -8964,7 +9151,8 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8964
9151
|
replaySignals,
|
|
8965
9152
|
replaySignalEvaluations,
|
|
8966
9153
|
runtimeLineageScopes: comparable.runtimeLineageScopes,
|
|
8967
|
-
replayLineageScopes
|
|
9154
|
+
replayLineageScopes,
|
|
9155
|
+
replayDecisionTraces
|
|
8968
9156
|
});
|
|
8969
9157
|
const parityRows = summarizeTradeParityByStrategy({
|
|
8970
9158
|
runtimeEntries: runtimeDedupe.entries,
|
|
@@ -9891,6 +10079,7 @@ var finishReplay = async ({
|
|
|
9891
10079
|
replaySignals: replayResult.signals,
|
|
9892
10080
|
replayLineages: replayResult.runtimeLineages,
|
|
9893
10081
|
replayLineageScopes: replayResult.replayLineageScopes,
|
|
10082
|
+
replayDecisionTraces: replayResult.replayDecisionTraces,
|
|
9894
10083
|
runtimeEvidencePath: replayRuntimeEvidencePath
|
|
9895
10084
|
});
|
|
9896
10085
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
@@ -10240,7 +10429,8 @@ var replayBacktest = async () => {
|
|
|
10240
10429
|
references: historicalReplayReferences,
|
|
10241
10430
|
showProgress: !replayFlags.portfolioReport,
|
|
10242
10431
|
collectSkipEvidence: !replayFlags.portfolioReport,
|
|
10243
|
-
collectSignals: !replayFlags.portfolioReport
|
|
10432
|
+
collectSignals: !replayFlags.portfolioReport,
|
|
10433
|
+
decisionTraceTargets: replayFlags.portfolioReport ? void 0 : evidenceMetadata?.decisionTraceTargets
|
|
10244
10434
|
});
|
|
10245
10435
|
return replayFlags.portfolioReport ? compactHistoricalReplayResultForPortfolio(result) : result;
|
|
10246
10436
|
};
|