@tradejs/cli 1.0.9 → 1.0.10
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 +29497 -13537
- package/dist/lib/aiPocketSearch.js +1316 -0
- package/dist/lib/aiTrainCharts.js +711 -0
- package/dist/lib/aiTrainDataset.js +40 -0
- package/dist/lib/aiTrainEvaluationDump.js +76 -0
- package/dist/lib/aiTrainMetrics.js +148 -8
- package/dist/lib/aiTrainOptions.js +130 -0
- package/dist/lib/aiTrainQuarantine.js +199 -0
- package/dist/lib/aiTrainResearch.js +467 -0
- package/dist/lib/binanceMarketContextBackfill.js +841 -0
- package/dist/lib/binanceMarketData.js +392 -0
- package/dist/lib/coinMarketCapContextBackfill.js +1088 -0
- package/dist/lib/continuity.js +60 -0
- package/dist/lib/derivativesContextBackfill.js +490 -77
- package/dist/lib/executionCalibration.js +705 -0
- package/dist/lib/marketContextPrepare.js +4403 -0
- package/dist/lib/paritySummary.js +101 -0
- package/dist/lib/runBot.js +1 -0
- package/dist/lib/runEnvironment.js +2254 -0
- package/dist/lib/runFormatting.js +1657 -0
- package/dist/lib/runtimeDebugEvidence.js +526 -0
- package/dist/lib/runtimeModeConfig.js +53 -0
- package/dist/lib/runtimeParity.js +88 -11
- package/dist/lib/runtimeParityDetails.js +751 -0
- package/dist/lib/runtimeRedis.js +157 -0
- package/dist/lib/runtimeSignalsLoader.js +40 -6
- package/dist/lib/runtimeSignalsStorage.js +45 -22
- package/dist/lib/runtimeStrategyBacktest.js +134 -0
- package/dist/lib/runtimeTradeSync.js +263 -0
- package/dist/lib/telegramReports.js +9 -0
- package/dist/lib/tickerUniverseCache.js +116 -0
- package/dist/lib/timeWindow.js +3 -3
- package/dist/scripts/agentRun.js +14 -8
- package/dist/scripts/aiExport.js +1721 -7
- package/dist/scripts/aiExportSelect.js +16 -4
- package/dist/scripts/aiPocketSearch.js +4190 -0
- package/dist/scripts/aiTrain.js +1866 -87
- package/dist/scripts/backtest.js +4509 -818
- package/dist/scripts/binanceMarketIngest.js +565 -0
- package/dist/scripts/bot.js +16 -2
- package/dist/scripts/candlesMigrateProvider.js +32 -22
- package/dist/scripts/cleanDir.js +15 -2
- package/dist/scripts/cleanRedis.js +15 -2
- package/dist/scripts/cleanTests.js +38 -2
- package/dist/scripts/cleanupMarketContext.js +69 -0
- package/dist/scripts/continuity.js +148 -67
- package/dist/scripts/derivativesIngest.js +14 -4
- package/dist/scripts/derivativesIngestCoinalyzeAll.js +14 -4
- package/dist/scripts/doctor.js +14 -4
- package/dist/scripts/executionCalibration.js +792 -0
- package/dist/scripts/infraCommon.js +10 -0
- package/dist/scripts/infraDown.js +14 -4
- package/dist/scripts/infraInit.js +24 -4
- package/dist/scripts/infraUp.js +14 -4
- package/dist/scripts/marketWs.js +222 -0
- package/dist/scripts/migration.js +15 -2
- package/dist/scripts/mlExport.js +1698 -10
- package/dist/scripts/mlExportSelect.js +16 -4
- package/dist/scripts/mlInspect.js +13 -3
- package/dist/scripts/mlTrainLatestSelect.js +14 -4
- package/dist/scripts/replay.js +8824 -0
- package/dist/scripts/replayRunner.js +8827 -0
- package/dist/scripts/replayRuntimeEvidence.js +868 -0
- package/dist/scripts/researchAuto.js +286 -59
- package/dist/scripts/results.js +3 -3
- package/dist/scripts/runtimeEvidence.js +605 -0
- package/dist/scripts/runtimeParity.js +6362 -1043
- package/dist/scripts/serverHealth.js +588 -0
- package/dist/scripts/signals.js +5590 -511
- package/dist/scripts/signalsDaemon.js +6167 -0
- package/dist/scripts/signalsSummary.js +833 -248
- package/dist/scripts/test-ml.js +25 -3
- package/dist/scripts/test.js +26 -1
- package/dist/scripts/user-add.js +15 -2
- package/dist/workers/testerWorker.js +130 -21
- package/package.json +18 -12
|
@@ -0,0 +1,751 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/lib/runtimeParityDetails.ts
|
|
21
|
+
var runtimeParityDetails_exports = {};
|
|
22
|
+
__export(runtimeParityDetails_exports, {
|
|
23
|
+
buildReplayExchangeComparisonDetails: () => buildReplayExchangeComparisonDetails,
|
|
24
|
+
buildReplayMismatchDrilldown: () => buildReplayMismatchDrilldown,
|
|
25
|
+
buildReplayRuntimeComparisonDetails: () => buildReplayRuntimeComparisonDetails,
|
|
26
|
+
buildStrategyNameByOrderLinkKey: () => buildStrategyNameByOrderLinkKey,
|
|
27
|
+
resolveReplayStrategyNameFromExchangeEntry: () => resolveReplayStrategyNameFromExchangeEntry
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(runtimeParityDetails_exports);
|
|
30
|
+
var import_trade = require("@tradejs/core/trade");
|
|
31
|
+
|
|
32
|
+
// src/lib/runtimeParity.ts
|
|
33
|
+
var getBacktestParityComparisonTimestamp = (entry, timestampOffsetMs = 0) => {
|
|
34
|
+
if (timestampOffsetMs > 0 && entry.signalTimestamp != null && entry.timestamp >= entry.signalTimestamp + timestampOffsetMs) {
|
|
35
|
+
return entry.timestamp;
|
|
36
|
+
}
|
|
37
|
+
return (entry.signalTimestamp ?? entry.timestamp) + timestampOffsetMs;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/lib/runtimeParityDetails.ts
|
|
41
|
+
var DEFAULT_REPLAY_PARITY_DETAILS_LIMIT = 500;
|
|
42
|
+
var DEFAULT_REPLAY_PARITY_ARTIFACT_LIMIT = 100;
|
|
43
|
+
var buildStrategyNameByOrderLinkKey = (strategyNames) => new Map(
|
|
44
|
+
strategyNames.flatMap((strategyName) => {
|
|
45
|
+
const strategyKey = (0, import_trade.normalizeStrategyOrderLinkKey)(strategyName);
|
|
46
|
+
return strategyKey ? [[strategyKey, strategyName]] : [];
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
var resolveReplayStrategyNameFromExchangeEntry = ({
|
|
50
|
+
exchangeEntry,
|
|
51
|
+
strategyNameByOrderLinkKey
|
|
52
|
+
}) => {
|
|
53
|
+
const strategyKey = (0, import_trade.parseStrategyOrderLinkKey)(exchangeEntry.orderLinkId);
|
|
54
|
+
if (!strategyKey) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
return strategyNameByOrderLinkKey.get(strategyKey) ?? null;
|
|
58
|
+
};
|
|
59
|
+
var resolveReplayParityDetailsLimit = () => {
|
|
60
|
+
const parsed = Number.parseInt(
|
|
61
|
+
String(process.env.REPLAY_PARITY_DETAILS_LIMIT ?? ""),
|
|
62
|
+
10
|
|
63
|
+
);
|
|
64
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_REPLAY_PARITY_DETAILS_LIMIT;
|
|
65
|
+
};
|
|
66
|
+
var resolveReplayParityArtifactLimit = () => {
|
|
67
|
+
const parsed = Number.parseInt(
|
|
68
|
+
String(process.env.REPLAY_PARITY_ARTIFACT_LIMIT ?? ""),
|
|
69
|
+
10
|
|
70
|
+
);
|
|
71
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_REPLAY_PARITY_ARTIFACT_LIMIT;
|
|
72
|
+
};
|
|
73
|
+
var capReplayDetails = (items, limit) => items.slice(0, Math.max(0, limit));
|
|
74
|
+
var buildCostDetail = (entry) => ({
|
|
75
|
+
entryFee: entry.entryFee ?? null,
|
|
76
|
+
exitFee: entry.exitFee ?? null,
|
|
77
|
+
fundingFee: entry.fundingFee ?? null,
|
|
78
|
+
totalFee: entry.totalFee ?? null
|
|
79
|
+
});
|
|
80
|
+
var toBacktestParityDetail = (entry, timestampOffsetMs = 0) => {
|
|
81
|
+
const comparisonTimestamp = getBacktestParityComparisonTimestamp(
|
|
82
|
+
entry,
|
|
83
|
+
timestampOffsetMs
|
|
84
|
+
);
|
|
85
|
+
return {
|
|
86
|
+
source: "backtest",
|
|
87
|
+
strategy: entry.strategy,
|
|
88
|
+
symbol: entry.symbol,
|
|
89
|
+
direction: entry.direction,
|
|
90
|
+
qty: entry.qty ?? null,
|
|
91
|
+
timestamp: entry.timestamp,
|
|
92
|
+
signalTimestamp: entry.signalTimestamp ?? null,
|
|
93
|
+
comparisonTimestamp: timestampOffsetMs === 0 ? null : comparisonTimestamp,
|
|
94
|
+
price: entry.price,
|
|
95
|
+
exitType: entry.exitType ?? null,
|
|
96
|
+
exitTimestamp: entry.exitTimestamp ?? null,
|
|
97
|
+
exitPrice: entry.exitPrice ?? null,
|
|
98
|
+
pnl: entry.expectedPnl ?? null,
|
|
99
|
+
costs: buildCostDetail(entry),
|
|
100
|
+
orderId: entry.orderId,
|
|
101
|
+
signalId: entry.signalId
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
var toRuntimeParityDetail = (entry) => ({
|
|
105
|
+
source: "runtime",
|
|
106
|
+
strategy: entry.strategy,
|
|
107
|
+
symbol: entry.symbol,
|
|
108
|
+
direction: entry.direction,
|
|
109
|
+
qty: entry.qty ?? null,
|
|
110
|
+
timestamp: entry.timestamp,
|
|
111
|
+
price: entry.price,
|
|
112
|
+
exitType: entry.exitType ?? null,
|
|
113
|
+
exitTimestamp: entry.exitTimestamp ?? null,
|
|
114
|
+
exitPrice: entry.exitPrice ?? null,
|
|
115
|
+
pnl: entry.realizedPnl ?? null,
|
|
116
|
+
costs: buildCostDetail(entry),
|
|
117
|
+
orderId: entry.orderId,
|
|
118
|
+
signalId: entry.signalId
|
|
119
|
+
});
|
|
120
|
+
var toExchangeParityDetail = ({
|
|
121
|
+
entry,
|
|
122
|
+
inferredStrategy = null
|
|
123
|
+
}) => ({
|
|
124
|
+
source: "exchange",
|
|
125
|
+
inferredStrategy,
|
|
126
|
+
symbol: entry.symbol,
|
|
127
|
+
direction: entry.direction,
|
|
128
|
+
qty: entry.qty,
|
|
129
|
+
timestamp: entry.entryTimestamp,
|
|
130
|
+
price: entry.entryPrice,
|
|
131
|
+
exitType: null,
|
|
132
|
+
exitTimestamp: entry.exitTimestamp ?? null,
|
|
133
|
+
exitPrice: entry.exitPrice ?? null,
|
|
134
|
+
pnl: entry.closedPnl ?? null,
|
|
135
|
+
costs: buildCostDetail({
|
|
136
|
+
entryFee: entry.openFee,
|
|
137
|
+
exitFee: entry.closeFee,
|
|
138
|
+
fundingFee: entry.fundingFee,
|
|
139
|
+
totalFee: entry.totalFee
|
|
140
|
+
}),
|
|
141
|
+
orderId: entry.orderId,
|
|
142
|
+
orderLinkId: entry.orderLinkId
|
|
143
|
+
});
|
|
144
|
+
var buildTimestampDelta = (left, right) => typeof left === "number" && Number.isFinite(left) && typeof right === "number" && Number.isFinite(right) ? Math.abs(left - right) : null;
|
|
145
|
+
var getComparisonTimestamp = (entry) => typeof entry.comparisonTimestamp === "number" && Number.isFinite(entry.comparisonTimestamp) ? entry.comparisonTimestamp : entry.timestamp;
|
|
146
|
+
var getEntryStrategy = (entry) => entry.strategy ?? entry.inferredStrategy ?? null;
|
|
147
|
+
var findNearestSignal = ({
|
|
148
|
+
entry,
|
|
149
|
+
signals,
|
|
150
|
+
toleranceMs,
|
|
151
|
+
timestampOffsetMs,
|
|
152
|
+
entryTimestampOffsetMs = 0
|
|
153
|
+
}) => {
|
|
154
|
+
const entryStrategy = getEntryStrategy(entry);
|
|
155
|
+
if (!entryStrategy) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
let bestSignal = null;
|
|
159
|
+
let bestDiff = Number.POSITIVE_INFINITY;
|
|
160
|
+
const entryTimestamp = entry.timestamp + entryTimestampOffsetMs;
|
|
161
|
+
for (const signal of signals) {
|
|
162
|
+
if (signal.strategy !== entryStrategy || signal.symbol !== entry.symbol || signal.direction !== entry.direction) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const diff = Math.abs(
|
|
166
|
+
signal.timestamp + timestampOffsetMs - entryTimestamp
|
|
167
|
+
);
|
|
168
|
+
if (diff > toleranceMs || diff >= bestDiff) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
bestSignal = signal;
|
|
172
|
+
bestDiff = diff;
|
|
173
|
+
}
|
|
174
|
+
return bestSignal ? {
|
|
175
|
+
signal: bestSignal,
|
|
176
|
+
timestampDiffMs: bestDiff
|
|
177
|
+
} : null;
|
|
178
|
+
};
|
|
179
|
+
var findNearestEvaluation = ({
|
|
180
|
+
entry,
|
|
181
|
+
evaluations,
|
|
182
|
+
toleranceMs,
|
|
183
|
+
timestampOffsetMs,
|
|
184
|
+
entryTimestampOffsetMs = 0
|
|
185
|
+
}) => {
|
|
186
|
+
const entryStrategy = getEntryStrategy(entry);
|
|
187
|
+
if (!entryStrategy) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
let bestEvaluation = null;
|
|
191
|
+
let bestDiff = Number.POSITIVE_INFINITY;
|
|
192
|
+
const entryTimestamp = entry.timestamp + entryTimestampOffsetMs;
|
|
193
|
+
for (const evaluation of evaluations) {
|
|
194
|
+
if (evaluation.strategy !== entryStrategy || evaluation.symbol !== entry.symbol) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const diff = Math.abs(
|
|
198
|
+
evaluation.timestamp + timestampOffsetMs - entryTimestamp
|
|
199
|
+
);
|
|
200
|
+
if (diff > toleranceMs || diff >= bestDiff) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
bestEvaluation = evaluation;
|
|
204
|
+
bestDiff = diff;
|
|
205
|
+
}
|
|
206
|
+
return bestEvaluation ? {
|
|
207
|
+
evaluation: bestEvaluation,
|
|
208
|
+
timestampDiffMs: bestDiff
|
|
209
|
+
} : null;
|
|
210
|
+
};
|
|
211
|
+
var classifySignalDiagnostic = (signal) => {
|
|
212
|
+
if (signal.orderStatus === "failed") {
|
|
213
|
+
return {
|
|
214
|
+
classification: "order_failed",
|
|
215
|
+
reason: signal.orderFailureReason || signal.orderSkipReason || "failed"
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
if (signal.orderStatus === "skipped" || signal.orderStatus === "canceled" || typeof signal.orderSkipReason === "string" && signal.orderSkipReason.trim() || signal.ml?.passed === false) {
|
|
219
|
+
return {
|
|
220
|
+
classification: "gated_or_policy_blocked",
|
|
221
|
+
reason: signal.orderSkipReason || (signal.ml?.passed === false ? `ml_probability=${signal.ml.probability} threshold=${signal.ml.threshold}` : `orderStatus=${signal.orderStatus ?? "skipped"}`)
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
if (signal.orderStatus === "completed") {
|
|
225
|
+
return {
|
|
226
|
+
classification: "completed_signal_without_match",
|
|
227
|
+
reason: "completed_signal_without_matching_trade"
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
classification: "signal_without_order_status",
|
|
232
|
+
reason: "signal_found_without_order_status"
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
var classifyEvaluationDiagnostic = (evaluation) => {
|
|
236
|
+
if (evaluation.status === "skip") {
|
|
237
|
+
return {
|
|
238
|
+
classification: "core_skipped",
|
|
239
|
+
reason: evaluation.reason || "NO_SIGNAL"
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
if (evaluation.status === "error") {
|
|
243
|
+
return {
|
|
244
|
+
classification: "evaluation_error",
|
|
245
|
+
reason: evaluation.reason || "runtime_evaluation_error"
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
if (evaluation.orderStatus === "failed") {
|
|
249
|
+
return {
|
|
250
|
+
classification: "order_failed",
|
|
251
|
+
reason: evaluation.reason || "orderStatus=failed"
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
if (evaluation.orderStatus === "skipped" || evaluation.orderStatus === "canceled" || typeof evaluation.orderSkipReason === "string" && evaluation.orderSkipReason.trim()) {
|
|
255
|
+
return {
|
|
256
|
+
classification: "gated_or_policy_blocked",
|
|
257
|
+
reason: evaluation.orderSkipReason || evaluation.reason || `orderStatus=${evaluation.orderStatus}`
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
if (evaluation.orderStatus === "completed") {
|
|
261
|
+
return {
|
|
262
|
+
classification: "completed_signal_without_match",
|
|
263
|
+
reason: "completed_signal_without_matching_trade"
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
classification: "signal_evaluation_without_order_status",
|
|
268
|
+
reason: evaluation.reason || "signal_evaluation_without_order_status"
|
|
269
|
+
};
|
|
270
|
+
};
|
|
271
|
+
var summarizeMismatchDiagnostics = (items) => Object.fromEntries(
|
|
272
|
+
[
|
|
273
|
+
...items.reduce((counts, item) => {
|
|
274
|
+
counts.set(
|
|
275
|
+
item.classification,
|
|
276
|
+
(counts.get(item.classification) ?? 0) + 1
|
|
277
|
+
);
|
|
278
|
+
return counts;
|
|
279
|
+
}, /* @__PURE__ */ new Map()).entries()
|
|
280
|
+
].sort(([left], [right]) => left.localeCompare(right))
|
|
281
|
+
);
|
|
282
|
+
var buildPnlDelta = (expectedPnl, realizedPnl) => typeof expectedPnl === "number" && Number.isFinite(expectedPnl) && typeof realizedPnl === "number" && Number.isFinite(realizedPnl) ? realizedPnl - expectedPnl : null;
|
|
283
|
+
var buildPriceDeltaPct = (leftPrice, rightPrice) => {
|
|
284
|
+
if (leftPrice == null || rightPrice == null || !Number.isFinite(leftPrice) || !Number.isFinite(rightPrice) || leftPrice === 0) {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
return Math.abs((rightPrice - leftPrice) / leftPrice * 100);
|
|
288
|
+
};
|
|
289
|
+
var toFiniteNumberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
290
|
+
var toAiDiagnostic = (analysis) => {
|
|
291
|
+
if (!analysis || typeof analysis !== "object") {
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
direction: typeof analysis.direction === "string" ? analysis.direction : null,
|
|
296
|
+
quality: toFiniteNumberOrNull(analysis.quality),
|
|
297
|
+
needRetest: typeof analysis.needRetest === "boolean" ? analysis.needRetest : null,
|
|
298
|
+
gateDecision: typeof analysis.gateDecision === "string" ? analysis.gateDecision : null,
|
|
299
|
+
llmDecision: typeof analysis.llmDecision === "string" ? analysis.llmDecision : null,
|
|
300
|
+
qualityReason: typeof analysis.qualityReason === "string" ? analysis.qualityReason : null
|
|
301
|
+
};
|
|
302
|
+
};
|
|
303
|
+
var toSignalDiagnostic = ({
|
|
304
|
+
signal,
|
|
305
|
+
timestampDiffMs
|
|
306
|
+
}) => ({
|
|
307
|
+
signalId: signal.signalId,
|
|
308
|
+
timestamp: signal.timestamp,
|
|
309
|
+
timestampDiffMs,
|
|
310
|
+
direction: signal.direction,
|
|
311
|
+
orderStatus: signal.orderStatus,
|
|
312
|
+
orderSkipReason: signal.orderSkipReason,
|
|
313
|
+
ai: toAiDiagnostic(signal.aiAnalysis),
|
|
314
|
+
ml: signal.ml ?? null
|
|
315
|
+
});
|
|
316
|
+
var toEvaluationDiagnostic = ({
|
|
317
|
+
evaluation,
|
|
318
|
+
timestampDiffMs
|
|
319
|
+
}) => ({
|
|
320
|
+
evaluationId: evaluation.evaluationId,
|
|
321
|
+
timestamp: evaluation.timestamp,
|
|
322
|
+
timestampDiffMs,
|
|
323
|
+
status: evaluation.status,
|
|
324
|
+
reason: evaluation.reason,
|
|
325
|
+
signalId: evaluation.signalId,
|
|
326
|
+
direction: evaluation.direction,
|
|
327
|
+
orderStatus: evaluation.orderStatus,
|
|
328
|
+
orderSkipReason: evaluation.orderSkipReason,
|
|
329
|
+
ai: toAiDiagnostic(evaluation.aiAnalysis),
|
|
330
|
+
ml: evaluation.ml ?? null
|
|
331
|
+
});
|
|
332
|
+
var buildSlippageCost = ({
|
|
333
|
+
direction,
|
|
334
|
+
expectedPrice,
|
|
335
|
+
actualPrice,
|
|
336
|
+
qty,
|
|
337
|
+
stage
|
|
338
|
+
}) => {
|
|
339
|
+
if (typeof expectedPrice !== "number" || !Number.isFinite(expectedPrice) || typeof actualPrice !== "number" || !Number.isFinite(actualPrice) || typeof qty !== "number" || !Number.isFinite(qty)) {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
const sideMultiplier = direction === "SHORT" ? -1 : 1;
|
|
343
|
+
const priceDelta = stage === "entry" ? (actualPrice - expectedPrice) * sideMultiplier : (expectedPrice - actualPrice) * sideMultiplier;
|
|
344
|
+
return Number((priceDelta * qty).toFixed(12));
|
|
345
|
+
};
|
|
346
|
+
var buildMatchedReplayDetail = ({
|
|
347
|
+
runtime,
|
|
348
|
+
backtest,
|
|
349
|
+
timestampDiffMs,
|
|
350
|
+
priceDeltaPct
|
|
351
|
+
}) => {
|
|
352
|
+
const exitPriceDeltaPct = buildPriceDeltaPct(
|
|
353
|
+
runtime.exitPrice ?? null,
|
|
354
|
+
backtest.exitPrice ?? null
|
|
355
|
+
);
|
|
356
|
+
const qty = runtime.qty ?? backtest.qty ?? null;
|
|
357
|
+
const entrySlippageCost = buildSlippageCost({
|
|
358
|
+
direction: runtime.direction,
|
|
359
|
+
expectedPrice: backtest.price,
|
|
360
|
+
actualPrice: runtime.price,
|
|
361
|
+
qty,
|
|
362
|
+
stage: "entry"
|
|
363
|
+
});
|
|
364
|
+
const exitSlippageCost = buildSlippageCost({
|
|
365
|
+
direction: runtime.direction,
|
|
366
|
+
expectedPrice: backtest.exitPrice,
|
|
367
|
+
actualPrice: runtime.exitPrice,
|
|
368
|
+
qty,
|
|
369
|
+
stage: "exit"
|
|
370
|
+
});
|
|
371
|
+
const totalSlippageCost = entrySlippageCost == null && exitSlippageCost == null ? null : Number(
|
|
372
|
+
((entrySlippageCost ?? 0) + (exitSlippageCost ?? 0)).toFixed(12)
|
|
373
|
+
);
|
|
374
|
+
return {
|
|
375
|
+
runtime,
|
|
376
|
+
backtest,
|
|
377
|
+
timestampDiffMs,
|
|
378
|
+
priceDeltaPct,
|
|
379
|
+
exitTimestampDiffMs: buildTimestampDelta(
|
|
380
|
+
runtime.exitTimestamp,
|
|
381
|
+
backtest.exitTimestamp
|
|
382
|
+
),
|
|
383
|
+
exitPriceDeltaPct,
|
|
384
|
+
exitType: {
|
|
385
|
+
expected: backtest.exitType ?? null,
|
|
386
|
+
actual: runtime.exitType ?? null,
|
|
387
|
+
matches: backtest.exitType && runtime.exitType ? backtest.exitType === runtime.exitType : null
|
|
388
|
+
},
|
|
389
|
+
pnl: {
|
|
390
|
+
expectedPnl: backtest.pnl ?? null,
|
|
391
|
+
realizedPnl: runtime.pnl ?? null,
|
|
392
|
+
delta: buildPnlDelta(backtest.pnl, runtime.pnl)
|
|
393
|
+
},
|
|
394
|
+
slippage: {
|
|
395
|
+
entryPriceDeltaPct: priceDeltaPct,
|
|
396
|
+
exitPriceDeltaPct,
|
|
397
|
+
entryCost: entrySlippageCost,
|
|
398
|
+
exitCost: exitSlippageCost,
|
|
399
|
+
totalCost: totalSlippageCost
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
};
|
|
403
|
+
var buildNearestCandidates = ({
|
|
404
|
+
entries,
|
|
405
|
+
candidates,
|
|
406
|
+
toleranceMs,
|
|
407
|
+
requireSameStrategy
|
|
408
|
+
}) => entries.map((entry) => {
|
|
409
|
+
const comparable = candidates.filter((candidate) => {
|
|
410
|
+
if (candidate.symbol !== entry.symbol || candidate.direction !== entry.direction) {
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
if (!requireSameStrategy) {
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
416
|
+
const entryStrategy = entry.strategy ?? entry.inferredStrategy ?? null;
|
|
417
|
+
const candidateStrategy = candidate.strategy ?? candidate.inferredStrategy ?? null;
|
|
418
|
+
return entryStrategy != null && entryStrategy === candidateStrategy;
|
|
419
|
+
});
|
|
420
|
+
const nearest = comparable.map((candidate) => ({
|
|
421
|
+
candidate,
|
|
422
|
+
timestampDiffMs: Math.abs(
|
|
423
|
+
getComparisonTimestamp(candidate) - getComparisonTimestamp(entry)
|
|
424
|
+
)
|
|
425
|
+
})).sort((left, right) => left.timestampDiffMs - right.timestampDiffMs)[0];
|
|
426
|
+
if (!nearest) {
|
|
427
|
+
return {
|
|
428
|
+
entry,
|
|
429
|
+
nearest: null,
|
|
430
|
+
timestampDiffMs: null,
|
|
431
|
+
priceDeltaPct: null,
|
|
432
|
+
reason: "no_candidate_same_symbol_direction"
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
const priceDeltaPct = buildPriceDeltaPct(
|
|
436
|
+
entry.price,
|
|
437
|
+
nearest.candidate.price
|
|
438
|
+
);
|
|
439
|
+
return {
|
|
440
|
+
entry,
|
|
441
|
+
nearest: nearest.candidate,
|
|
442
|
+
timestampDiffMs: nearest.timestampDiffMs,
|
|
443
|
+
priceDeltaPct,
|
|
444
|
+
reason: nearest.timestampDiffMs > toleranceMs ? "outside_tolerance" : "candidate_already_matched"
|
|
445
|
+
};
|
|
446
|
+
});
|
|
447
|
+
var buildReplayMismatchDrilldown = ({
|
|
448
|
+
runtimeOnly,
|
|
449
|
+
backtestOnly,
|
|
450
|
+
nearestCandidates,
|
|
451
|
+
runtimeSignals = [],
|
|
452
|
+
runtimeSignalEvaluations = [],
|
|
453
|
+
replaySignals = [],
|
|
454
|
+
replaySignalEvaluations = [],
|
|
455
|
+
toleranceMs,
|
|
456
|
+
backtestTimestampOffsetMs,
|
|
457
|
+
limit
|
|
458
|
+
}) => {
|
|
459
|
+
const nearestByEntry = new Map(
|
|
460
|
+
nearestCandidates.map((candidate) => [candidate.entry, candidate])
|
|
461
|
+
);
|
|
462
|
+
const artifactLimit = Math.min(limit, resolveReplayParityArtifactLimit());
|
|
463
|
+
let runtimeOnlyArtifactsIncluded = 0;
|
|
464
|
+
let backtestOnlyArtifactsIncluded = 0;
|
|
465
|
+
const runtimeOnlyDiagnostics = runtimeOnly.map((entry, index) => {
|
|
466
|
+
const includeArtifacts = index < artifactLimit;
|
|
467
|
+
const nearestCandidate = nearestByEntry.get(entry);
|
|
468
|
+
const nearestReplaySignal = findNearestSignal({
|
|
469
|
+
entry,
|
|
470
|
+
signals: replaySignals,
|
|
471
|
+
toleranceMs,
|
|
472
|
+
timestampOffsetMs: backtestTimestampOffsetMs
|
|
473
|
+
});
|
|
474
|
+
const nearestReplayEvaluation = findNearestEvaluation({
|
|
475
|
+
entry,
|
|
476
|
+
evaluations: replaySignalEvaluations,
|
|
477
|
+
toleranceMs,
|
|
478
|
+
timestampOffsetMs: backtestTimestampOffsetMs
|
|
479
|
+
});
|
|
480
|
+
const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestCandidate?.nearest ? {
|
|
481
|
+
classification: "timing_or_price_drift",
|
|
482
|
+
reason: nearestCandidate.reason
|
|
483
|
+
} : {
|
|
484
|
+
classification: "no_replay_candidate",
|
|
485
|
+
reason: "no_replay_signal_or_backtest_candidate"
|
|
486
|
+
};
|
|
487
|
+
return {
|
|
488
|
+
entry,
|
|
489
|
+
...classification,
|
|
490
|
+
...nearestCandidate ? { nearestCandidate } : {},
|
|
491
|
+
...nearestReplaySignal ? {
|
|
492
|
+
replaySignal: toSignalDiagnostic({
|
|
493
|
+
signal: nearestReplaySignal.signal,
|
|
494
|
+
timestampDiffMs: nearestReplaySignal.timestampDiffMs
|
|
495
|
+
}),
|
|
496
|
+
...includeArtifacts ? { replaySignalArtifact: nearestReplaySignal.signal } : {}
|
|
497
|
+
} : {},
|
|
498
|
+
...nearestReplayEvaluation ? {
|
|
499
|
+
replayEvaluation: toEvaluationDiagnostic({
|
|
500
|
+
evaluation: nearestReplayEvaluation.evaluation,
|
|
501
|
+
timestampDiffMs: nearestReplayEvaluation.timestampDiffMs
|
|
502
|
+
}),
|
|
503
|
+
...includeArtifacts ? { replayEvaluationArtifact: nearestReplayEvaluation.evaluation } : {}
|
|
504
|
+
} : {}
|
|
505
|
+
};
|
|
506
|
+
});
|
|
507
|
+
const backtestOnlyDiagnostics = backtestOnly.map((entry, index) => {
|
|
508
|
+
const includeArtifacts = index < artifactLimit;
|
|
509
|
+
const nearestCandidate = nearestByEntry.get(entry);
|
|
510
|
+
const nearestRuntimeSignal = findNearestSignal({
|
|
511
|
+
entry,
|
|
512
|
+
signals: runtimeSignals,
|
|
513
|
+
toleranceMs,
|
|
514
|
+
timestampOffsetMs: 0
|
|
515
|
+
});
|
|
516
|
+
const nearestRuntimeEvaluation = findNearestEvaluation({
|
|
517
|
+
entry,
|
|
518
|
+
evaluations: runtimeSignalEvaluations,
|
|
519
|
+
toleranceMs,
|
|
520
|
+
timestampOffsetMs: 0
|
|
521
|
+
});
|
|
522
|
+
const classification = nearestRuntimeSignal ? classifySignalDiagnostic(nearestRuntimeSignal.signal) : nearestRuntimeEvaluation ? classifyEvaluationDiagnostic(nearestRuntimeEvaluation.evaluation) : nearestCandidate?.nearest ? {
|
|
523
|
+
classification: "timing_or_price_drift",
|
|
524
|
+
reason: nearestCandidate.reason
|
|
525
|
+
} : {
|
|
526
|
+
classification: "no_runtime_evaluation",
|
|
527
|
+
reason: "no_runtime_signal_or_evaluation"
|
|
528
|
+
};
|
|
529
|
+
return {
|
|
530
|
+
entry,
|
|
531
|
+
...classification,
|
|
532
|
+
...nearestCandidate ? { nearestCandidate } : {},
|
|
533
|
+
...nearestRuntimeSignal ? {
|
|
534
|
+
runtimeSignal: toSignalDiagnostic({
|
|
535
|
+
signal: nearestRuntimeSignal.signal,
|
|
536
|
+
timestampDiffMs: nearestRuntimeSignal.timestampDiffMs
|
|
537
|
+
}),
|
|
538
|
+
...includeArtifacts ? { runtimeSignalArtifact: nearestRuntimeSignal.signal } : {}
|
|
539
|
+
} : {},
|
|
540
|
+
...nearestRuntimeEvaluation ? {
|
|
541
|
+
runtimeEvaluation: toEvaluationDiagnostic({
|
|
542
|
+
evaluation: nearestRuntimeEvaluation.evaluation,
|
|
543
|
+
timestampDiffMs: nearestRuntimeEvaluation.timestampDiffMs
|
|
544
|
+
}),
|
|
545
|
+
...includeArtifacts ? {
|
|
546
|
+
runtimeEvaluationArtifact: nearestRuntimeEvaluation.evaluation
|
|
547
|
+
} : {}
|
|
548
|
+
} : {}
|
|
549
|
+
};
|
|
550
|
+
});
|
|
551
|
+
for (const diagnostic of runtimeOnlyDiagnostics) {
|
|
552
|
+
if (diagnostic.replaySignalArtifact || diagnostic.replayEvaluationArtifact) {
|
|
553
|
+
runtimeOnlyArtifactsIncluded += 1;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
for (const diagnostic of backtestOnlyDiagnostics) {
|
|
557
|
+
if (diagnostic.runtimeSignalArtifact || diagnostic.runtimeEvaluationArtifact) {
|
|
558
|
+
backtestOnlyArtifactsIncluded += 1;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
return {
|
|
562
|
+
runtimeOnly: capReplayDetails(runtimeOnlyDiagnostics, limit),
|
|
563
|
+
backtestOnly: capReplayDetails(backtestOnlyDiagnostics, limit),
|
|
564
|
+
summary: {
|
|
565
|
+
runtimeOnly: summarizeMismatchDiagnostics(runtimeOnlyDiagnostics),
|
|
566
|
+
backtestOnly: summarizeMismatchDiagnostics(backtestOnlyDiagnostics),
|
|
567
|
+
artifacts: {
|
|
568
|
+
limit: artifactLimit,
|
|
569
|
+
runtimeOnlyIncluded: runtimeOnlyArtifactsIncluded,
|
|
570
|
+
runtimeOnlyOmitted: Math.max(
|
|
571
|
+
0,
|
|
572
|
+
runtimeOnlyDiagnostics.length - runtimeOnlyArtifactsIncluded
|
|
573
|
+
),
|
|
574
|
+
backtestOnlyIncluded: backtestOnlyArtifactsIncluded,
|
|
575
|
+
backtestOnlyOmitted: Math.max(
|
|
576
|
+
0,
|
|
577
|
+
backtestOnlyDiagnostics.length - backtestOnlyArtifactsIncluded
|
|
578
|
+
)
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
};
|
|
583
|
+
var buildReplayRuntimeComparisonDetails = ({
|
|
584
|
+
matched,
|
|
585
|
+
runtimeOnly,
|
|
586
|
+
backtestOnly,
|
|
587
|
+
runtimeEntries,
|
|
588
|
+
backtestEntries,
|
|
589
|
+
toleranceMs,
|
|
590
|
+
limit = resolveReplayParityDetailsLimit(),
|
|
591
|
+
backtestTimestampOffsetMs = 0,
|
|
592
|
+
runtimeSignals,
|
|
593
|
+
runtimeSignalEvaluations,
|
|
594
|
+
replaySignals,
|
|
595
|
+
replaySignalEvaluations
|
|
596
|
+
}) => {
|
|
597
|
+
const runtimeDetails = runtimeEntries.map(toRuntimeParityDetail);
|
|
598
|
+
const backtestDetails = backtestEntries.map(
|
|
599
|
+
(entry) => toBacktestParityDetail(entry, backtestTimestampOffsetMs)
|
|
600
|
+
);
|
|
601
|
+
const runtimeOnlyDetails = runtimeOnly.map(toRuntimeParityDetail);
|
|
602
|
+
const backtestOnlyDetails = backtestOnly.map(
|
|
603
|
+
(entry) => toBacktestParityDetail(entry, backtestTimestampOffsetMs)
|
|
604
|
+
);
|
|
605
|
+
const nearestCandidates = [
|
|
606
|
+
...buildNearestCandidates({
|
|
607
|
+
entries: runtimeOnlyDetails,
|
|
608
|
+
candidates: backtestDetails,
|
|
609
|
+
toleranceMs,
|
|
610
|
+
requireSameStrategy: true
|
|
611
|
+
}),
|
|
612
|
+
...buildNearestCandidates({
|
|
613
|
+
entries: backtestOnlyDetails,
|
|
614
|
+
candidates: runtimeDetails,
|
|
615
|
+
toleranceMs,
|
|
616
|
+
requireSameStrategy: true
|
|
617
|
+
})
|
|
618
|
+
];
|
|
619
|
+
const cappedNearestCandidates = capReplayDetails(nearestCandidates, limit);
|
|
620
|
+
const cappedRuntimeOnlyDetails = capReplayDetails(runtimeOnlyDetails, limit);
|
|
621
|
+
const cappedBacktestOnlyDetails = capReplayDetails(
|
|
622
|
+
backtestOnlyDetails,
|
|
623
|
+
limit
|
|
624
|
+
);
|
|
625
|
+
return {
|
|
626
|
+
capped: matched.length > limit || runtimeOnlyDetails.length > limit || backtestOnlyDetails.length > limit || nearestCandidates.length > limit,
|
|
627
|
+
limit,
|
|
628
|
+
matched: capReplayDetails(
|
|
629
|
+
matched.map(
|
|
630
|
+
(item) => buildMatchedReplayDetail({
|
|
631
|
+
runtime: toRuntimeParityDetail(item.runtime),
|
|
632
|
+
backtest: toBacktestParityDetail(
|
|
633
|
+
item.backtest,
|
|
634
|
+
backtestTimestampOffsetMs
|
|
635
|
+
),
|
|
636
|
+
timestampDiffMs: item.timestampDiffMs,
|
|
637
|
+
priceDeltaPct: item.priceDeltaPct
|
|
638
|
+
})
|
|
639
|
+
),
|
|
640
|
+
limit
|
|
641
|
+
),
|
|
642
|
+
runtimeOnly: cappedRuntimeOnlyDetails,
|
|
643
|
+
backtestOnly: cappedBacktestOnlyDetails,
|
|
644
|
+
nearestCandidates: cappedNearestCandidates,
|
|
645
|
+
mismatchDrilldown: buildReplayMismatchDrilldown({
|
|
646
|
+
runtimeOnly: runtimeOnlyDetails,
|
|
647
|
+
backtestOnly: backtestOnlyDetails,
|
|
648
|
+
nearestCandidates,
|
|
649
|
+
runtimeSignals,
|
|
650
|
+
runtimeSignalEvaluations,
|
|
651
|
+
replaySignals,
|
|
652
|
+
replaySignalEvaluations,
|
|
653
|
+
toleranceMs,
|
|
654
|
+
backtestTimestampOffsetMs,
|
|
655
|
+
limit
|
|
656
|
+
})
|
|
657
|
+
};
|
|
658
|
+
};
|
|
659
|
+
var buildReplayExchangeComparisonDetails = ({
|
|
660
|
+
matched,
|
|
661
|
+
exchangeOnly,
|
|
662
|
+
backtestOnly,
|
|
663
|
+
exchangeEntries,
|
|
664
|
+
backtestEntries,
|
|
665
|
+
strategyNameByOrderLinkKey,
|
|
666
|
+
toleranceMs,
|
|
667
|
+
limit = resolveReplayParityDetailsLimit(),
|
|
668
|
+
backtestTimestampOffsetMs = 0,
|
|
669
|
+
runtimeSignals,
|
|
670
|
+
runtimeSignalEvaluations,
|
|
671
|
+
replaySignals,
|
|
672
|
+
replaySignalEvaluations
|
|
673
|
+
}) => {
|
|
674
|
+
const toExchangeDetail = (entry) => toExchangeParityDetail({
|
|
675
|
+
entry,
|
|
676
|
+
inferredStrategy: resolveReplayStrategyNameFromExchangeEntry({
|
|
677
|
+
exchangeEntry: entry,
|
|
678
|
+
strategyNameByOrderLinkKey
|
|
679
|
+
})
|
|
680
|
+
});
|
|
681
|
+
const exchangeDetails = exchangeEntries.map(toExchangeDetail);
|
|
682
|
+
const backtestDetails = backtestEntries.map(
|
|
683
|
+
(entry) => toBacktestParityDetail(entry, backtestTimestampOffsetMs)
|
|
684
|
+
);
|
|
685
|
+
const exchangeOnlyDetails = exchangeOnly.map(toExchangeDetail);
|
|
686
|
+
const backtestOnlyDetails = backtestOnly.map(
|
|
687
|
+
(entry) => toBacktestParityDetail(entry, backtestTimestampOffsetMs)
|
|
688
|
+
);
|
|
689
|
+
const nearestCandidates = [
|
|
690
|
+
...buildNearestCandidates({
|
|
691
|
+
entries: exchangeOnlyDetails,
|
|
692
|
+
candidates: backtestDetails,
|
|
693
|
+
toleranceMs
|
|
694
|
+
}),
|
|
695
|
+
...buildNearestCandidates({
|
|
696
|
+
entries: backtestOnlyDetails,
|
|
697
|
+
candidates: exchangeDetails,
|
|
698
|
+
toleranceMs
|
|
699
|
+
})
|
|
700
|
+
];
|
|
701
|
+
const cappedNearestCandidates = capReplayDetails(nearestCandidates, limit);
|
|
702
|
+
const cappedExchangeOnlyDetails = capReplayDetails(
|
|
703
|
+
exchangeOnlyDetails,
|
|
704
|
+
limit
|
|
705
|
+
);
|
|
706
|
+
const cappedBacktestOnlyDetails = capReplayDetails(
|
|
707
|
+
backtestOnlyDetails,
|
|
708
|
+
limit
|
|
709
|
+
);
|
|
710
|
+
return {
|
|
711
|
+
capped: matched.length > limit || exchangeOnlyDetails.length > limit || backtestOnlyDetails.length > limit || nearestCandidates.length > limit,
|
|
712
|
+
limit,
|
|
713
|
+
matched: capReplayDetails(
|
|
714
|
+
matched.map(
|
|
715
|
+
(item) => buildMatchedReplayDetail({
|
|
716
|
+
runtime: toExchangeDetail(item.exchange),
|
|
717
|
+
backtest: toBacktestParityDetail(
|
|
718
|
+
item.backtest,
|
|
719
|
+
backtestTimestampOffsetMs
|
|
720
|
+
),
|
|
721
|
+
timestampDiffMs: item.timestampDiffMs,
|
|
722
|
+
priceDeltaPct: item.priceDeltaPct
|
|
723
|
+
})
|
|
724
|
+
),
|
|
725
|
+
limit
|
|
726
|
+
),
|
|
727
|
+
runtimeOnly: cappedExchangeOnlyDetails,
|
|
728
|
+
backtestOnly: cappedBacktestOnlyDetails,
|
|
729
|
+
nearestCandidates: cappedNearestCandidates,
|
|
730
|
+
mismatchDrilldown: buildReplayMismatchDrilldown({
|
|
731
|
+
runtimeOnly: exchangeOnlyDetails,
|
|
732
|
+
backtestOnly: backtestOnlyDetails,
|
|
733
|
+
nearestCandidates,
|
|
734
|
+
runtimeSignals,
|
|
735
|
+
runtimeSignalEvaluations,
|
|
736
|
+
replaySignals,
|
|
737
|
+
replaySignalEvaluations,
|
|
738
|
+
toleranceMs,
|
|
739
|
+
backtestTimestampOffsetMs,
|
|
740
|
+
limit
|
|
741
|
+
})
|
|
742
|
+
};
|
|
743
|
+
};
|
|
744
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
745
|
+
0 && (module.exports = {
|
|
746
|
+
buildReplayExchangeComparisonDetails,
|
|
747
|
+
buildReplayMismatchDrilldown,
|
|
748
|
+
buildReplayRuntimeComparisonDetails,
|
|
749
|
+
buildStrategyNameByOrderLinkKey,
|
|
750
|
+
resolveReplayStrategyNameFromExchangeEntry
|
|
751
|
+
});
|