@tradejs/cli 3.1.29-beta.265 → 3.1.29-beta.266
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 +96 -50
- package/dist/lib/runtimeFeedbackReplay.js +49 -40
- package/dist/lib/runtimeParity.js +31 -2
- package/dist/lib/runtimeParityDetails.js +23 -7
- package/dist/scripts/replay.js +45 -9
- package/dist/scripts/replayRunner.js +45 -9
- package/dist/scripts/runtimeFeedbackReplay.js +49 -40
- package/dist/scripts/runtimeParity.js +10 -2
- package/package.json +7 -7
package/dist/cli.js
CHANGED
|
@@ -16947,18 +16947,43 @@ var init_chartSnapshot = __esm({
|
|
|
16947
16947
|
});
|
|
16948
16948
|
|
|
16949
16949
|
// src/lib/runtimeParity.ts
|
|
16950
|
-
var toFiniteNumberOrNull2, toGroupKey, toRuntimeDuplicateKey, getBacktestParityComparisonTimestamp, sortTradeParityEntries, buildPriceDeltaPct, toExitType, resolveBacktestExitType, extractBacktestEntryParityEntries, extractRuntimeParityEntries, findRuntimeDuplicateGroups, dedupeRuntimeParityEntries, compareTradeParityEntries, summarizeMatchedParity;
|
|
16950
|
+
var resolveTradeParityTimestampOffset, buildTradeParityTimestampOffset, toFiniteNumberOrNull2, toGroupKey, toRuntimeDuplicateKey, getBacktestParityComparisonTimestamp, sortTradeParityEntries, buildPriceDeltaPct, toExitType, resolveBacktestExitType, extractBacktestEntryParityEntries, extractRuntimeParityEntries, findRuntimeDuplicateGroups, dedupeRuntimeParityEntries, compareTradeParityEntries, summarizeMatchedParity;
|
|
16951
16951
|
var init_runtimeParity = __esm({
|
|
16952
16952
|
"src/lib/runtimeParity.ts"() {
|
|
16953
16953
|
"use strict";
|
|
16954
|
+
resolveTradeParityTimestampOffset = (strategy, offset) => typeof offset === "function" ? offset(strategy) : offset;
|
|
16955
|
+
buildTradeParityTimestampOffset = (strategies) => {
|
|
16956
|
+
const intervals = /* @__PURE__ */ new Map();
|
|
16957
|
+
for (const { strategyName, interval: interval4 } of strategies) {
|
|
16958
|
+
const minutes = Number(interval4);
|
|
16959
|
+
if (!Number.isFinite(minutes) || minutes <= 0) {
|
|
16960
|
+
throw new Error(`Invalid embedded replay interval for ${strategyName}`);
|
|
16961
|
+
}
|
|
16962
|
+
intervals.set(strategyName, minutes * 60 * 1e3);
|
|
16963
|
+
}
|
|
16964
|
+
return (strategy) => {
|
|
16965
|
+
const offsetMs = intervals.get(strategy);
|
|
16966
|
+
if (offsetMs == null) {
|
|
16967
|
+
throw new Error(`Missing embedded replay interval for ${strategy}`);
|
|
16968
|
+
}
|
|
16969
|
+
return offsetMs;
|
|
16970
|
+
};
|
|
16971
|
+
};
|
|
16954
16972
|
toFiniteNumberOrNull2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
16955
16973
|
toGroupKey = (entry) => `${entry.strategy}::${entry.symbol}::${entry.direction}`;
|
|
16956
16974
|
toRuntimeDuplicateKey = (entry) => `${toGroupKey(entry)}::${entry.timestamp}`;
|
|
16957
16975
|
getBacktestParityComparisonTimestamp = (entry, timestampOffsetMs = 0) => {
|
|
16958
|
-
|
|
16976
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
16977
|
+
entry.strategy,
|
|
16978
|
+
timestampOffsetMs
|
|
16979
|
+
);
|
|
16980
|
+
if (!Number.isFinite(offsetMs) || offsetMs < 0) {
|
|
16981
|
+
throw new Error(`Invalid backtest timestamp offset for ${entry.strategy}`);
|
|
16982
|
+
}
|
|
16983
|
+
if (offsetMs > 0 && entry.signalTimestamp != null && entry.timestamp >= entry.signalTimestamp + offsetMs) {
|
|
16959
16984
|
return entry.timestamp;
|
|
16960
16985
|
}
|
|
16961
|
-
return (entry.signalTimestamp ?? entry.timestamp) +
|
|
16986
|
+
return (entry.signalTimestamp ?? entry.timestamp) + offsetMs;
|
|
16962
16987
|
};
|
|
16963
16988
|
sortTradeParityEntries = (left, right) => left.timestamp - right.timestamp || left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.direction.localeCompare(right.direction) || left.id.localeCompare(right.id);
|
|
16964
16989
|
buildPriceDeltaPct = (leftPrice, rightPrice) => {
|
|
@@ -18014,9 +18039,13 @@ var init_runtimeParityDetails = __esm({
|
|
|
18014
18039
|
totalFee: entry.totalFee ?? null
|
|
18015
18040
|
});
|
|
18016
18041
|
toBacktestParityDetail = (entry, timestampOffsetMs = 0) => {
|
|
18042
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
18043
|
+
entry.strategy,
|
|
18044
|
+
timestampOffsetMs
|
|
18045
|
+
);
|
|
18017
18046
|
const comparisonTimestamp = getBacktestParityComparisonTimestamp(
|
|
18018
18047
|
entry,
|
|
18019
|
-
|
|
18048
|
+
offsetMs
|
|
18020
18049
|
);
|
|
18021
18050
|
return {
|
|
18022
18051
|
source: "backtest",
|
|
@@ -18026,7 +18055,7 @@ var init_runtimeParityDetails = __esm({
|
|
|
18026
18055
|
qty: entry.qty ?? null,
|
|
18027
18056
|
timestamp: entry.timestamp,
|
|
18028
18057
|
signalTimestamp: entry.signalTimestamp ?? null,
|
|
18029
|
-
comparisonTimestamp:
|
|
18058
|
+
comparisonTimestamp: offsetMs === 0 ? null : comparisonTimestamp,
|
|
18030
18059
|
price: entry.price,
|
|
18031
18060
|
exitType: entry.exitType ?? null,
|
|
18032
18061
|
exitTimestamp: entry.exitTimestamp ?? null,
|
|
@@ -18485,25 +18514,29 @@ var init_runtimeParityDetails = __esm({
|
|
|
18485
18514
|
let runtimeOnlyArtifactsIncluded = 0;
|
|
18486
18515
|
let backtestOnlyArtifactsIncluded = 0;
|
|
18487
18516
|
const runtimeOnlyDiagnostics = runtimeOnly.map((entry, index) => {
|
|
18517
|
+
const entryOffsetMs = resolveTradeParityTimestampOffset(
|
|
18518
|
+
getEntryStrategy(entry) ?? "",
|
|
18519
|
+
backtestTimestampOffsetMs
|
|
18520
|
+
);
|
|
18488
18521
|
const includeArtifacts = index < artifactLimit;
|
|
18489
18522
|
const nearestCandidate = nearestByEntry.get(entry);
|
|
18490
18523
|
const nearestReplaySignal = findNearestSignal({
|
|
18491
18524
|
entry,
|
|
18492
18525
|
signals: replaySignals,
|
|
18493
18526
|
toleranceMs,
|
|
18494
|
-
timestampOffsetMs:
|
|
18527
|
+
timestampOffsetMs: entryOffsetMs
|
|
18495
18528
|
});
|
|
18496
18529
|
const nearestReplayEvaluation = findNearestEvaluation({
|
|
18497
18530
|
entry,
|
|
18498
18531
|
evaluations: replaySignalEvaluations,
|
|
18499
18532
|
toleranceMs,
|
|
18500
|
-
timestampOffsetMs:
|
|
18533
|
+
timestampOffsetMs: entryOffsetMs
|
|
18501
18534
|
});
|
|
18502
18535
|
const nearestReplayEvaluationOutcome = findNearestReplayEvaluationOutcome({
|
|
18503
18536
|
entry,
|
|
18504
18537
|
lineageScopes: replayLineageScopes,
|
|
18505
18538
|
toleranceMs,
|
|
18506
|
-
timestampOffsetMs:
|
|
18539
|
+
timestampOffsetMs: entryOffsetMs
|
|
18507
18540
|
});
|
|
18508
18541
|
const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestReplayEvaluationOutcome ? {
|
|
18509
18542
|
classification: "core_skipped",
|
|
@@ -19386,6 +19419,7 @@ var init_runtimeComparison = __esm({
|
|
|
19386
19419
|
init_cliConfig2();
|
|
19387
19420
|
init_runtimeEvidenceSource();
|
|
19388
19421
|
init_support();
|
|
19422
|
+
init_runtimeEvidenceDeployment();
|
|
19389
19423
|
init_runtimeComparisonCalculations();
|
|
19390
19424
|
init_runtimeComparisonReporting();
|
|
19391
19425
|
init_runtimeComparisonCalculations();
|
|
@@ -19775,6 +19809,9 @@ var init_runtimeComparison = __esm({
|
|
|
19775
19809
|
expectedUserName: replayUserName,
|
|
19776
19810
|
expectedWindow: window
|
|
19777
19811
|
}) : null;
|
|
19812
|
+
const replayTimestampOffset = runtimeEvidence2 ? buildTradeParityTimestampOffset(
|
|
19813
|
+
(0, runtimeEvidenceDeployment_exports.activeRuntimeEvidenceStrategies)(runtimeEvidence2.deployment)
|
|
19814
|
+
) : getReplayEntryTimestampCompareOffsetMs();
|
|
19778
19815
|
const [
|
|
19779
19816
|
rawRuntimeTrades,
|
|
19780
19817
|
runtimeSignals,
|
|
@@ -19884,7 +19921,7 @@ var init_runtimeComparison = __esm({
|
|
|
19884
19921
|
runtimeEntries: runtimeDedupe.entries,
|
|
19885
19922
|
backtestEntries: comparableBacktestEntries,
|
|
19886
19923
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
19887
|
-
backtestTimestampOffsetMs:
|
|
19924
|
+
backtestTimestampOffsetMs: replayTimestampOffset
|
|
19888
19925
|
});
|
|
19889
19926
|
const details = buildReplayRuntimeComparisonDetails({
|
|
19890
19927
|
matched: comparison.matched,
|
|
@@ -19893,7 +19930,7 @@ var init_runtimeComparison = __esm({
|
|
|
19893
19930
|
runtimeEntries: runtimeDedupe.entries,
|
|
19894
19931
|
backtestEntries: comparableBacktestEntries,
|
|
19895
19932
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
19896
|
-
backtestTimestampOffsetMs:
|
|
19933
|
+
backtestTimestampOffsetMs: replayTimestampOffset,
|
|
19897
19934
|
runtimeSignals: comparable.runtimeSignals,
|
|
19898
19935
|
runtimeSignalEvaluations: comparable.runtimeSignalEvaluations,
|
|
19899
19936
|
replaySignals,
|
|
@@ -31772,41 +31809,52 @@ var init_runtimeFeedbackReplay = __esm({
|
|
|
31772
31809
|
interval: interval4,
|
|
31773
31810
|
startTime,
|
|
31774
31811
|
endTime
|
|
31775
|
-
}) =>
|
|
31776
|
-
|
|
31777
|
-
|
|
31778
|
-
|
|
31779
|
-
|
|
31780
|
-
|
|
31781
|
-
|
|
31782
|
-
|
|
31783
|
-
|
|
31784
|
-
|
|
31785
|
-
|
|
31786
|
-
|
|
31787
|
-
|
|
31788
|
-
|
|
31789
|
-
|
|
31790
|
-
|
|
31791
|
-
|
|
31792
|
-
|
|
31793
|
-
|
|
31794
|
-
|
|
31795
|
-
|
|
31796
|
-
|
|
31797
|
-
|
|
31798
|
-
|
|
31799
|
-
|
|
31800
|
-
|
|
31801
|
-
|
|
31802
|
-
|
|
31803
|
-
|
|
31804
|
-
|
|
31805
|
-
|
|
31806
|
-
|
|
31807
|
-
|
|
31808
|
-
|
|
31809
|
-
|
|
31812
|
+
}) => {
|
|
31813
|
+
const intervals = interval4.split(",").map((value) => value.trim());
|
|
31814
|
+
if (!intervals.length || intervals.some(
|
|
31815
|
+
(value) => !value || !Number.isFinite(Number(value)) || Number(value) <= 0
|
|
31816
|
+
)) {
|
|
31817
|
+
throw new Error(`Invalid runtime feedback replay intervals: ${interval4}`);
|
|
31818
|
+
}
|
|
31819
|
+
const replayTimeframe = intervals.sort(
|
|
31820
|
+
(left, right) => Number(left) - Number(right)
|
|
31821
|
+
)[0];
|
|
31822
|
+
return [
|
|
31823
|
+
[
|
|
31824
|
+
cliPath,
|
|
31825
|
+
"replay",
|
|
31826
|
+
"--user",
|
|
31827
|
+
userName3,
|
|
31828
|
+
"--connector",
|
|
31829
|
+
connectorName,
|
|
31830
|
+
"--deployment",
|
|
31831
|
+
deploymentId,
|
|
31832
|
+
"--timeframe",
|
|
31833
|
+
replayTimeframe,
|
|
31834
|
+
"--startTime",
|
|
31835
|
+
String(startTime),
|
|
31836
|
+
"--endTime",
|
|
31837
|
+
String(endTime),
|
|
31838
|
+
"--runtimeEvidence",
|
|
31839
|
+
runtimeEvidencePath,
|
|
31840
|
+
"--cacheOnly"
|
|
31841
|
+
],
|
|
31842
|
+
[
|
|
31843
|
+
cliPath,
|
|
31844
|
+
"replay-runtime-evidence",
|
|
31845
|
+
"--user",
|
|
31846
|
+
userName3,
|
|
31847
|
+
"--runtimeEvidence",
|
|
31848
|
+
runtimeEvidencePath,
|
|
31849
|
+
"--startTime",
|
|
31850
|
+
String(startTime),
|
|
31851
|
+
"--endTime",
|
|
31852
|
+
String(endTime),
|
|
31853
|
+
"--out",
|
|
31854
|
+
replayEvidencePath
|
|
31855
|
+
]
|
|
31856
|
+
];
|
|
31857
|
+
};
|
|
31810
31858
|
runLoggedCommand = async ({
|
|
31811
31859
|
executable,
|
|
31812
31860
|
command,
|
|
@@ -31893,10 +31941,8 @@ $ ${[executable, ...command].join(" ")}
|
|
|
31893
31941
|
const intervals = new Set(
|
|
31894
31942
|
activeStrategies.map(({ interval: interval4 }) => String(interval4))
|
|
31895
31943
|
);
|
|
31896
|
-
if (intervals.size
|
|
31897
|
-
throw new Error(
|
|
31898
|
-
`Runtime feedback replay requires one interval, received ${[...intervals].join(",")}`
|
|
31899
|
-
);
|
|
31944
|
+
if (!intervals.size) {
|
|
31945
|
+
throw new Error("Runtime feedback replay requires an active interval");
|
|
31900
31946
|
}
|
|
31901
31947
|
await import_promises28.default.mkdir(outDir, { recursive: true });
|
|
31902
31948
|
const existing = await import_promises28.default.readdir(outDir);
|
|
@@ -31917,7 +31963,7 @@ $ ${[executable, ...command].join(" ")}
|
|
|
31917
31963
|
userName: runtimeEvidenceBundle.manifest.userName,
|
|
31918
31964
|
connectorName: deployment.connectorName,
|
|
31919
31965
|
deploymentId: deployment.id,
|
|
31920
|
-
interval: [...intervals]
|
|
31966
|
+
interval: [...intervals].join(","),
|
|
31921
31967
|
startTime: runtimeEvidenceBundle.manifest.window.startTime,
|
|
31922
31968
|
endTime: runtimeEvidenceBundle.manifest.window.endTime
|
|
31923
31969
|
});
|
|
@@ -133,41 +133,52 @@ var buildRuntimeFeedbackReplayCommands = ({
|
|
|
133
133
|
interval,
|
|
134
134
|
startTime,
|
|
135
135
|
endTime
|
|
136
|
-
}) =>
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
136
|
+
}) => {
|
|
137
|
+
const intervals = interval.split(",").map((value) => value.trim());
|
|
138
|
+
if (!intervals.length || intervals.some(
|
|
139
|
+
(value) => !value || !Number.isFinite(Number(value)) || Number(value) <= 0
|
|
140
|
+
)) {
|
|
141
|
+
throw new Error(`Invalid runtime feedback replay intervals: ${interval}`);
|
|
142
|
+
}
|
|
143
|
+
const replayTimeframe = intervals.sort(
|
|
144
|
+
(left, right) => Number(left) - Number(right)
|
|
145
|
+
)[0];
|
|
146
|
+
return [
|
|
147
|
+
[
|
|
148
|
+
cliPath,
|
|
149
|
+
"replay",
|
|
150
|
+
"--user",
|
|
151
|
+
userName,
|
|
152
|
+
"--connector",
|
|
153
|
+
connectorName,
|
|
154
|
+
"--deployment",
|
|
155
|
+
deploymentId,
|
|
156
|
+
"--timeframe",
|
|
157
|
+
replayTimeframe,
|
|
158
|
+
"--startTime",
|
|
159
|
+
String(startTime),
|
|
160
|
+
"--endTime",
|
|
161
|
+
String(endTime),
|
|
162
|
+
"--runtimeEvidence",
|
|
163
|
+
runtimeEvidencePath,
|
|
164
|
+
"--cacheOnly"
|
|
165
|
+
],
|
|
166
|
+
[
|
|
167
|
+
cliPath,
|
|
168
|
+
"replay-runtime-evidence",
|
|
169
|
+
"--user",
|
|
170
|
+
userName,
|
|
171
|
+
"--runtimeEvidence",
|
|
172
|
+
runtimeEvidencePath,
|
|
173
|
+
"--startTime",
|
|
174
|
+
String(startTime),
|
|
175
|
+
"--endTime",
|
|
176
|
+
String(endTime),
|
|
177
|
+
"--out",
|
|
178
|
+
replayEvidencePath
|
|
179
|
+
]
|
|
180
|
+
];
|
|
181
|
+
};
|
|
171
182
|
var runLoggedCommand = async ({
|
|
172
183
|
executable,
|
|
173
184
|
command,
|
|
@@ -254,10 +265,8 @@ var runRuntimeFeedbackReplay = async ({
|
|
|
254
265
|
const intervals = new Set(
|
|
255
266
|
activeStrategies.map(({ interval }) => String(interval))
|
|
256
267
|
);
|
|
257
|
-
if (intervals.size
|
|
258
|
-
throw new Error(
|
|
259
|
-
`Runtime feedback replay requires one interval, received ${[...intervals].join(",")}`
|
|
260
|
-
);
|
|
268
|
+
if (!intervals.size) {
|
|
269
|
+
throw new Error("Runtime feedback replay requires an active interval");
|
|
261
270
|
}
|
|
262
271
|
await import_promises.default.mkdir(outDir, { recursive: true });
|
|
263
272
|
const existing = await import_promises.default.readdir(outDir);
|
|
@@ -278,7 +287,7 @@ var runRuntimeFeedbackReplay = async ({
|
|
|
278
287
|
userName: runtimeEvidenceBundle.manifest.userName,
|
|
279
288
|
connectorName: deployment.connectorName,
|
|
280
289
|
deploymentId: deployment.id,
|
|
281
|
-
interval: [...intervals]
|
|
290
|
+
interval: [...intervals].join(","),
|
|
282
291
|
startTime: runtimeEvidenceBundle.manifest.window.startTime,
|
|
283
292
|
endTime: runtimeEvidenceBundle.manifest.window.endTime
|
|
284
293
|
});
|
|
@@ -20,23 +20,50 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/lib/runtimeParity.ts
|
|
21
21
|
var runtimeParity_exports = {};
|
|
22
22
|
__export(runtimeParity_exports, {
|
|
23
|
+
buildTradeParityTimestampOffset: () => buildTradeParityTimestampOffset,
|
|
23
24
|
compareTradeParityEntries: () => compareTradeParityEntries,
|
|
24
25
|
dedupeRuntimeParityEntries: () => dedupeRuntimeParityEntries,
|
|
25
26
|
extractBacktestEntryParityEntries: () => extractBacktestEntryParityEntries,
|
|
26
27
|
extractRuntimeParityEntries: () => extractRuntimeParityEntries,
|
|
27
28
|
findRuntimeDuplicateGroups: () => findRuntimeDuplicateGroups,
|
|
28
29
|
getBacktestParityComparisonTimestamp: () => getBacktestParityComparisonTimestamp,
|
|
30
|
+
resolveTradeParityTimestampOffset: () => resolveTradeParityTimestampOffset,
|
|
29
31
|
summarizeMatchedParity: () => summarizeMatchedParity
|
|
30
32
|
});
|
|
31
33
|
module.exports = __toCommonJS(runtimeParity_exports);
|
|
34
|
+
var resolveTradeParityTimestampOffset = (strategy, offset) => typeof offset === "function" ? offset(strategy) : offset;
|
|
35
|
+
var buildTradeParityTimestampOffset = (strategies) => {
|
|
36
|
+
const intervals = /* @__PURE__ */ new Map();
|
|
37
|
+
for (const { strategyName, interval } of strategies) {
|
|
38
|
+
const minutes = Number(interval);
|
|
39
|
+
if (!Number.isFinite(minutes) || minutes <= 0) {
|
|
40
|
+
throw new Error(`Invalid embedded replay interval for ${strategyName}`);
|
|
41
|
+
}
|
|
42
|
+
intervals.set(strategyName, minutes * 60 * 1e3);
|
|
43
|
+
}
|
|
44
|
+
return (strategy) => {
|
|
45
|
+
const offsetMs = intervals.get(strategy);
|
|
46
|
+
if (offsetMs == null) {
|
|
47
|
+
throw new Error(`Missing embedded replay interval for ${strategy}`);
|
|
48
|
+
}
|
|
49
|
+
return offsetMs;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
32
52
|
var toFiniteNumberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
33
53
|
var toGroupKey = (entry) => `${entry.strategy}::${entry.symbol}::${entry.direction}`;
|
|
34
54
|
var toRuntimeDuplicateKey = (entry) => `${toGroupKey(entry)}::${entry.timestamp}`;
|
|
35
55
|
var getBacktestParityComparisonTimestamp = (entry, timestampOffsetMs = 0) => {
|
|
36
|
-
|
|
56
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
57
|
+
entry.strategy,
|
|
58
|
+
timestampOffsetMs
|
|
59
|
+
);
|
|
60
|
+
if (!Number.isFinite(offsetMs) || offsetMs < 0) {
|
|
61
|
+
throw new Error(`Invalid backtest timestamp offset for ${entry.strategy}`);
|
|
62
|
+
}
|
|
63
|
+
if (offsetMs > 0 && entry.signalTimestamp != null && entry.timestamp >= entry.signalTimestamp + offsetMs) {
|
|
37
64
|
return entry.timestamp;
|
|
38
65
|
}
|
|
39
|
-
return (entry.signalTimestamp ?? entry.timestamp) +
|
|
66
|
+
return (entry.signalTimestamp ?? entry.timestamp) + offsetMs;
|
|
40
67
|
};
|
|
41
68
|
var sortTradeParityEntries = (left, right) => left.timestamp - right.timestamp || left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.direction.localeCompare(right.direction) || left.id.localeCompare(right.id);
|
|
42
69
|
var buildPriceDeltaPct = (leftPrice, rightPrice) => {
|
|
@@ -301,11 +328,13 @@ var summarizeMatchedParity = (matched) => {
|
|
|
301
328
|
};
|
|
302
329
|
// Annotate the CommonJS export names for ESM import in node:
|
|
303
330
|
0 && (module.exports = {
|
|
331
|
+
buildTradeParityTimestampOffset,
|
|
304
332
|
compareTradeParityEntries,
|
|
305
333
|
dedupeRuntimeParityEntries,
|
|
306
334
|
extractBacktestEntryParityEntries,
|
|
307
335
|
extractRuntimeParityEntries,
|
|
308
336
|
findRuntimeDuplicateGroups,
|
|
309
337
|
getBacktestParityComparisonTimestamp,
|
|
338
|
+
resolveTradeParityTimestampOffset,
|
|
310
339
|
summarizeMatchedParity
|
|
311
340
|
});
|
|
@@ -30,11 +30,19 @@ module.exports = __toCommonJS(runtimeParityDetails_exports);
|
|
|
30
30
|
var import_trade = require("@tradejs/core/trade");
|
|
31
31
|
|
|
32
32
|
// src/lib/runtimeParity.ts
|
|
33
|
+
var resolveTradeParityTimestampOffset = (strategy, offset) => typeof offset === "function" ? offset(strategy) : offset;
|
|
33
34
|
var getBacktestParityComparisonTimestamp = (entry, timestampOffsetMs = 0) => {
|
|
34
|
-
|
|
35
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
36
|
+
entry.strategy,
|
|
37
|
+
timestampOffsetMs
|
|
38
|
+
);
|
|
39
|
+
if (!Number.isFinite(offsetMs) || offsetMs < 0) {
|
|
40
|
+
throw new Error(`Invalid backtest timestamp offset for ${entry.strategy}`);
|
|
41
|
+
}
|
|
42
|
+
if (offsetMs > 0 && entry.signalTimestamp != null && entry.timestamp >= entry.signalTimestamp + offsetMs) {
|
|
35
43
|
return entry.timestamp;
|
|
36
44
|
}
|
|
37
|
-
return (entry.signalTimestamp ?? entry.timestamp) +
|
|
45
|
+
return (entry.signalTimestamp ?? entry.timestamp) + offsetMs;
|
|
38
46
|
};
|
|
39
47
|
|
|
40
48
|
// src/lib/runtimeParityDetails.ts
|
|
@@ -78,9 +86,13 @@ var buildCostDetail = (entry) => ({
|
|
|
78
86
|
totalFee: entry.totalFee ?? null
|
|
79
87
|
});
|
|
80
88
|
var toBacktestParityDetail = (entry, timestampOffsetMs = 0) => {
|
|
89
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
90
|
+
entry.strategy,
|
|
91
|
+
timestampOffsetMs
|
|
92
|
+
);
|
|
81
93
|
const comparisonTimestamp = getBacktestParityComparisonTimestamp(
|
|
82
94
|
entry,
|
|
83
|
-
|
|
95
|
+
offsetMs
|
|
84
96
|
);
|
|
85
97
|
return {
|
|
86
98
|
source: "backtest",
|
|
@@ -90,7 +102,7 @@ var toBacktestParityDetail = (entry, timestampOffsetMs = 0) => {
|
|
|
90
102
|
qty: entry.qty ?? null,
|
|
91
103
|
timestamp: entry.timestamp,
|
|
92
104
|
signalTimestamp: entry.signalTimestamp ?? null,
|
|
93
|
-
comparisonTimestamp:
|
|
105
|
+
comparisonTimestamp: offsetMs === 0 ? null : comparisonTimestamp,
|
|
94
106
|
price: entry.price,
|
|
95
107
|
exitType: entry.exitType ?? null,
|
|
96
108
|
exitTimestamp: entry.exitTimestamp ?? null,
|
|
@@ -549,25 +561,29 @@ var buildReplayMismatchDrilldown = ({
|
|
|
549
561
|
let runtimeOnlyArtifactsIncluded = 0;
|
|
550
562
|
let backtestOnlyArtifactsIncluded = 0;
|
|
551
563
|
const runtimeOnlyDiagnostics = runtimeOnly.map((entry, index) => {
|
|
564
|
+
const entryOffsetMs = resolveTradeParityTimestampOffset(
|
|
565
|
+
getEntryStrategy(entry) ?? "",
|
|
566
|
+
backtestTimestampOffsetMs
|
|
567
|
+
);
|
|
552
568
|
const includeArtifacts = index < artifactLimit;
|
|
553
569
|
const nearestCandidate = nearestByEntry.get(entry);
|
|
554
570
|
const nearestReplaySignal = findNearestSignal({
|
|
555
571
|
entry,
|
|
556
572
|
signals: replaySignals,
|
|
557
573
|
toleranceMs,
|
|
558
|
-
timestampOffsetMs:
|
|
574
|
+
timestampOffsetMs: entryOffsetMs
|
|
559
575
|
});
|
|
560
576
|
const nearestReplayEvaluation = findNearestEvaluation({
|
|
561
577
|
entry,
|
|
562
578
|
evaluations: replaySignalEvaluations,
|
|
563
579
|
toleranceMs,
|
|
564
|
-
timestampOffsetMs:
|
|
580
|
+
timestampOffsetMs: entryOffsetMs
|
|
565
581
|
});
|
|
566
582
|
const nearestReplayEvaluationOutcome = findNearestReplayEvaluationOutcome({
|
|
567
583
|
entry,
|
|
568
584
|
lineageScopes: replayLineageScopes,
|
|
569
585
|
toleranceMs,
|
|
570
|
-
timestampOffsetMs:
|
|
586
|
+
timestampOffsetMs: entryOffsetMs
|
|
571
587
|
});
|
|
572
588
|
const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestReplayEvaluationOutcome ? {
|
|
573
589
|
classification: "core_skipped",
|
package/dist/scripts/replay.js
CHANGED
|
@@ -6056,14 +6056,39 @@ var buildReplayChartSnapshot = (params) => {
|
|
|
6056
6056
|
var import_chalk10 = __toESM(require("chalk"));
|
|
6057
6057
|
|
|
6058
6058
|
// src/lib/runtimeParity.ts
|
|
6059
|
+
var resolveTradeParityTimestampOffset = (strategy, offset) => typeof offset === "function" ? offset(strategy) : offset;
|
|
6060
|
+
var buildTradeParityTimestampOffset = (strategies) => {
|
|
6061
|
+
const intervals = /* @__PURE__ */ new Map();
|
|
6062
|
+
for (const { strategyName, interval } of strategies) {
|
|
6063
|
+
const minutes = Number(interval);
|
|
6064
|
+
if (!Number.isFinite(minutes) || minutes <= 0) {
|
|
6065
|
+
throw new Error(`Invalid embedded replay interval for ${strategyName}`);
|
|
6066
|
+
}
|
|
6067
|
+
intervals.set(strategyName, minutes * 60 * 1e3);
|
|
6068
|
+
}
|
|
6069
|
+
return (strategy) => {
|
|
6070
|
+
const offsetMs = intervals.get(strategy);
|
|
6071
|
+
if (offsetMs == null) {
|
|
6072
|
+
throw new Error(`Missing embedded replay interval for ${strategy}`);
|
|
6073
|
+
}
|
|
6074
|
+
return offsetMs;
|
|
6075
|
+
};
|
|
6076
|
+
};
|
|
6059
6077
|
var toFiniteNumberOrNull2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
6060
6078
|
var toGroupKey = (entry) => `${entry.strategy}::${entry.symbol}::${entry.direction}`;
|
|
6061
6079
|
var toRuntimeDuplicateKey = (entry) => `${toGroupKey(entry)}::${entry.timestamp}`;
|
|
6062
6080
|
var getBacktestParityComparisonTimestamp = (entry, timestampOffsetMs = 0) => {
|
|
6063
|
-
|
|
6081
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
6082
|
+
entry.strategy,
|
|
6083
|
+
timestampOffsetMs
|
|
6084
|
+
);
|
|
6085
|
+
if (!Number.isFinite(offsetMs) || offsetMs < 0) {
|
|
6086
|
+
throw new Error(`Invalid backtest timestamp offset for ${entry.strategy}`);
|
|
6087
|
+
}
|
|
6088
|
+
if (offsetMs > 0 && entry.signalTimestamp != null && entry.timestamp >= entry.signalTimestamp + offsetMs) {
|
|
6064
6089
|
return entry.timestamp;
|
|
6065
6090
|
}
|
|
6066
|
-
return (entry.signalTimestamp ?? entry.timestamp) +
|
|
6091
|
+
return (entry.signalTimestamp ?? entry.timestamp) + offsetMs;
|
|
6067
6092
|
};
|
|
6068
6093
|
var sortTradeParityEntries = (left, right) => left.timestamp - right.timestamp || left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.direction.localeCompare(right.direction) || left.id.localeCompare(right.id);
|
|
6069
6094
|
var buildPriceDeltaPct = (leftPrice, rightPrice) => {
|
|
@@ -6869,9 +6894,13 @@ var buildCostDetail = (entry) => ({
|
|
|
6869
6894
|
totalFee: entry.totalFee ?? null
|
|
6870
6895
|
});
|
|
6871
6896
|
var toBacktestParityDetail = (entry, timestampOffsetMs = 0) => {
|
|
6897
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
6898
|
+
entry.strategy,
|
|
6899
|
+
timestampOffsetMs
|
|
6900
|
+
);
|
|
6872
6901
|
const comparisonTimestamp = getBacktestParityComparisonTimestamp(
|
|
6873
6902
|
entry,
|
|
6874
|
-
|
|
6903
|
+
offsetMs
|
|
6875
6904
|
);
|
|
6876
6905
|
return {
|
|
6877
6906
|
source: "backtest",
|
|
@@ -6881,7 +6910,7 @@ var toBacktestParityDetail = (entry, timestampOffsetMs = 0) => {
|
|
|
6881
6910
|
qty: entry.qty ?? null,
|
|
6882
6911
|
timestamp: entry.timestamp,
|
|
6883
6912
|
signalTimestamp: entry.signalTimestamp ?? null,
|
|
6884
|
-
comparisonTimestamp:
|
|
6913
|
+
comparisonTimestamp: offsetMs === 0 ? null : comparisonTimestamp,
|
|
6885
6914
|
price: entry.price,
|
|
6886
6915
|
exitType: entry.exitType ?? null,
|
|
6887
6916
|
exitTimestamp: entry.exitTimestamp ?? null,
|
|
@@ -7340,25 +7369,29 @@ var buildReplayMismatchDrilldown = ({
|
|
|
7340
7369
|
let runtimeOnlyArtifactsIncluded = 0;
|
|
7341
7370
|
let backtestOnlyArtifactsIncluded = 0;
|
|
7342
7371
|
const runtimeOnlyDiagnostics = runtimeOnly.map((entry, index) => {
|
|
7372
|
+
const entryOffsetMs = resolveTradeParityTimestampOffset(
|
|
7373
|
+
getEntryStrategy(entry) ?? "",
|
|
7374
|
+
backtestTimestampOffsetMs
|
|
7375
|
+
);
|
|
7343
7376
|
const includeArtifacts = index < artifactLimit;
|
|
7344
7377
|
const nearestCandidate = nearestByEntry.get(entry);
|
|
7345
7378
|
const nearestReplaySignal = findNearestSignal({
|
|
7346
7379
|
entry,
|
|
7347
7380
|
signals: replaySignals,
|
|
7348
7381
|
toleranceMs,
|
|
7349
|
-
timestampOffsetMs:
|
|
7382
|
+
timestampOffsetMs: entryOffsetMs
|
|
7350
7383
|
});
|
|
7351
7384
|
const nearestReplayEvaluation = findNearestEvaluation({
|
|
7352
7385
|
entry,
|
|
7353
7386
|
evaluations: replaySignalEvaluations,
|
|
7354
7387
|
toleranceMs,
|
|
7355
|
-
timestampOffsetMs:
|
|
7388
|
+
timestampOffsetMs: entryOffsetMs
|
|
7356
7389
|
});
|
|
7357
7390
|
const nearestReplayEvaluationOutcome = findNearestReplayEvaluationOutcome({
|
|
7358
7391
|
entry,
|
|
7359
7392
|
lineageScopes: replayLineageScopes,
|
|
7360
7393
|
toleranceMs,
|
|
7361
|
-
timestampOffsetMs:
|
|
7394
|
+
timestampOffsetMs: entryOffsetMs
|
|
7362
7395
|
});
|
|
7363
7396
|
const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestReplayEvaluationOutcome ? {
|
|
7364
7397
|
classification: "core_skipped",
|
|
@@ -8593,6 +8626,9 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8593
8626
|
expectedUserName: replayUserName,
|
|
8594
8627
|
expectedWindow: window
|
|
8595
8628
|
}) : null;
|
|
8629
|
+
const replayTimestampOffset = runtimeEvidence ? buildTradeParityTimestampOffset(
|
|
8630
|
+
(0, runtimeEvidenceDeployment_exports.activeRuntimeEvidenceStrategies)(runtimeEvidence.deployment)
|
|
8631
|
+
) : getReplayEntryTimestampCompareOffsetMs();
|
|
8596
8632
|
const [
|
|
8597
8633
|
rawRuntimeTrades,
|
|
8598
8634
|
runtimeSignals,
|
|
@@ -8702,7 +8738,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8702
8738
|
runtimeEntries: runtimeDedupe.entries,
|
|
8703
8739
|
backtestEntries: comparableBacktestEntries,
|
|
8704
8740
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8705
|
-
backtestTimestampOffsetMs:
|
|
8741
|
+
backtestTimestampOffsetMs: replayTimestampOffset
|
|
8706
8742
|
});
|
|
8707
8743
|
const details = buildReplayRuntimeComparisonDetails({
|
|
8708
8744
|
matched: comparison.matched,
|
|
@@ -8711,7 +8747,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8711
8747
|
runtimeEntries: runtimeDedupe.entries,
|
|
8712
8748
|
backtestEntries: comparableBacktestEntries,
|
|
8713
8749
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8714
|
-
backtestTimestampOffsetMs:
|
|
8750
|
+
backtestTimestampOffsetMs: replayTimestampOffset,
|
|
8715
8751
|
runtimeSignals: comparable.runtimeSignals,
|
|
8716
8752
|
runtimeSignalEvaluations: comparable.runtimeSignalEvaluations,
|
|
8717
8753
|
replaySignals,
|
|
@@ -6059,14 +6059,39 @@ var buildReplayChartSnapshot = (params) => {
|
|
|
6059
6059
|
var import_chalk10 = __toESM(require("chalk"));
|
|
6060
6060
|
|
|
6061
6061
|
// src/lib/runtimeParity.ts
|
|
6062
|
+
var resolveTradeParityTimestampOffset = (strategy, offset) => typeof offset === "function" ? offset(strategy) : offset;
|
|
6063
|
+
var buildTradeParityTimestampOffset = (strategies) => {
|
|
6064
|
+
const intervals = /* @__PURE__ */ new Map();
|
|
6065
|
+
for (const { strategyName, interval } of strategies) {
|
|
6066
|
+
const minutes = Number(interval);
|
|
6067
|
+
if (!Number.isFinite(minutes) || minutes <= 0) {
|
|
6068
|
+
throw new Error(`Invalid embedded replay interval for ${strategyName}`);
|
|
6069
|
+
}
|
|
6070
|
+
intervals.set(strategyName, minutes * 60 * 1e3);
|
|
6071
|
+
}
|
|
6072
|
+
return (strategy) => {
|
|
6073
|
+
const offsetMs = intervals.get(strategy);
|
|
6074
|
+
if (offsetMs == null) {
|
|
6075
|
+
throw new Error(`Missing embedded replay interval for ${strategy}`);
|
|
6076
|
+
}
|
|
6077
|
+
return offsetMs;
|
|
6078
|
+
};
|
|
6079
|
+
};
|
|
6062
6080
|
var toFiniteNumberOrNull2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
6063
6081
|
var toGroupKey = (entry) => `${entry.strategy}::${entry.symbol}::${entry.direction}`;
|
|
6064
6082
|
var toRuntimeDuplicateKey = (entry) => `${toGroupKey(entry)}::${entry.timestamp}`;
|
|
6065
6083
|
var getBacktestParityComparisonTimestamp = (entry, timestampOffsetMs = 0) => {
|
|
6066
|
-
|
|
6084
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
6085
|
+
entry.strategy,
|
|
6086
|
+
timestampOffsetMs
|
|
6087
|
+
);
|
|
6088
|
+
if (!Number.isFinite(offsetMs) || offsetMs < 0) {
|
|
6089
|
+
throw new Error(`Invalid backtest timestamp offset for ${entry.strategy}`);
|
|
6090
|
+
}
|
|
6091
|
+
if (offsetMs > 0 && entry.signalTimestamp != null && entry.timestamp >= entry.signalTimestamp + offsetMs) {
|
|
6067
6092
|
return entry.timestamp;
|
|
6068
6093
|
}
|
|
6069
|
-
return (entry.signalTimestamp ?? entry.timestamp) +
|
|
6094
|
+
return (entry.signalTimestamp ?? entry.timestamp) + offsetMs;
|
|
6070
6095
|
};
|
|
6071
6096
|
var sortTradeParityEntries = (left, right) => left.timestamp - right.timestamp || left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.direction.localeCompare(right.direction) || left.id.localeCompare(right.id);
|
|
6072
6097
|
var buildPriceDeltaPct = (leftPrice, rightPrice) => {
|
|
@@ -6872,9 +6897,13 @@ var buildCostDetail = (entry) => ({
|
|
|
6872
6897
|
totalFee: entry.totalFee ?? null
|
|
6873
6898
|
});
|
|
6874
6899
|
var toBacktestParityDetail = (entry, timestampOffsetMs = 0) => {
|
|
6900
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
6901
|
+
entry.strategy,
|
|
6902
|
+
timestampOffsetMs
|
|
6903
|
+
);
|
|
6875
6904
|
const comparisonTimestamp = getBacktestParityComparisonTimestamp(
|
|
6876
6905
|
entry,
|
|
6877
|
-
|
|
6906
|
+
offsetMs
|
|
6878
6907
|
);
|
|
6879
6908
|
return {
|
|
6880
6909
|
source: "backtest",
|
|
@@ -6884,7 +6913,7 @@ var toBacktestParityDetail = (entry, timestampOffsetMs = 0) => {
|
|
|
6884
6913
|
qty: entry.qty ?? null,
|
|
6885
6914
|
timestamp: entry.timestamp,
|
|
6886
6915
|
signalTimestamp: entry.signalTimestamp ?? null,
|
|
6887
|
-
comparisonTimestamp:
|
|
6916
|
+
comparisonTimestamp: offsetMs === 0 ? null : comparisonTimestamp,
|
|
6888
6917
|
price: entry.price,
|
|
6889
6918
|
exitType: entry.exitType ?? null,
|
|
6890
6919
|
exitTimestamp: entry.exitTimestamp ?? null,
|
|
@@ -7343,25 +7372,29 @@ var buildReplayMismatchDrilldown = ({
|
|
|
7343
7372
|
let runtimeOnlyArtifactsIncluded = 0;
|
|
7344
7373
|
let backtestOnlyArtifactsIncluded = 0;
|
|
7345
7374
|
const runtimeOnlyDiagnostics = runtimeOnly.map((entry, index) => {
|
|
7375
|
+
const entryOffsetMs = resolveTradeParityTimestampOffset(
|
|
7376
|
+
getEntryStrategy(entry) ?? "",
|
|
7377
|
+
backtestTimestampOffsetMs
|
|
7378
|
+
);
|
|
7346
7379
|
const includeArtifacts = index < artifactLimit;
|
|
7347
7380
|
const nearestCandidate = nearestByEntry.get(entry);
|
|
7348
7381
|
const nearestReplaySignal = findNearestSignal({
|
|
7349
7382
|
entry,
|
|
7350
7383
|
signals: replaySignals,
|
|
7351
7384
|
toleranceMs,
|
|
7352
|
-
timestampOffsetMs:
|
|
7385
|
+
timestampOffsetMs: entryOffsetMs
|
|
7353
7386
|
});
|
|
7354
7387
|
const nearestReplayEvaluation = findNearestEvaluation({
|
|
7355
7388
|
entry,
|
|
7356
7389
|
evaluations: replaySignalEvaluations,
|
|
7357
7390
|
toleranceMs,
|
|
7358
|
-
timestampOffsetMs:
|
|
7391
|
+
timestampOffsetMs: entryOffsetMs
|
|
7359
7392
|
});
|
|
7360
7393
|
const nearestReplayEvaluationOutcome = findNearestReplayEvaluationOutcome({
|
|
7361
7394
|
entry,
|
|
7362
7395
|
lineageScopes: replayLineageScopes,
|
|
7363
7396
|
toleranceMs,
|
|
7364
|
-
timestampOffsetMs:
|
|
7397
|
+
timestampOffsetMs: entryOffsetMs
|
|
7365
7398
|
});
|
|
7366
7399
|
const classification = nearestReplaySignal ? classifySignalDiagnostic(nearestReplaySignal.signal) : nearestReplayEvaluation ? classifyEvaluationDiagnostic(nearestReplayEvaluation.evaluation) : nearestReplayEvaluationOutcome ? {
|
|
7367
7400
|
classification: "core_skipped",
|
|
@@ -8596,6 +8629,9 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8596
8629
|
expectedUserName: replayUserName,
|
|
8597
8630
|
expectedWindow: window
|
|
8598
8631
|
}) : null;
|
|
8632
|
+
const replayTimestampOffset = runtimeEvidence ? buildTradeParityTimestampOffset(
|
|
8633
|
+
(0, runtimeEvidenceDeployment_exports.activeRuntimeEvidenceStrategies)(runtimeEvidence.deployment)
|
|
8634
|
+
) : getReplayEntryTimestampCompareOffsetMs();
|
|
8599
8635
|
const [
|
|
8600
8636
|
rawRuntimeTrades,
|
|
8601
8637
|
runtimeSignals,
|
|
@@ -8705,7 +8741,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8705
8741
|
runtimeEntries: runtimeDedupe.entries,
|
|
8706
8742
|
backtestEntries: comparableBacktestEntries,
|
|
8707
8743
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8708
|
-
backtestTimestampOffsetMs:
|
|
8744
|
+
backtestTimestampOffsetMs: replayTimestampOffset
|
|
8709
8745
|
});
|
|
8710
8746
|
const details = buildReplayRuntimeComparisonDetails({
|
|
8711
8747
|
matched: comparison.matched,
|
|
@@ -8714,7 +8750,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8714
8750
|
runtimeEntries: runtimeDedupe.entries,
|
|
8715
8751
|
backtestEntries: comparableBacktestEntries,
|
|
8716
8752
|
toleranceMs: REPLAY_RUNTIME_COMPARE_TOLERANCE_MS,
|
|
8717
|
-
backtestTimestampOffsetMs:
|
|
8753
|
+
backtestTimestampOffsetMs: replayTimestampOffset,
|
|
8718
8754
|
runtimeSignals: comparable.runtimeSignals,
|
|
8719
8755
|
runtimeSignalEvaluations: comparable.runtimeSignalEvaluations,
|
|
8720
8756
|
replaySignals,
|
|
@@ -137,41 +137,52 @@ var buildRuntimeFeedbackReplayCommands = ({
|
|
|
137
137
|
interval,
|
|
138
138
|
startTime,
|
|
139
139
|
endTime
|
|
140
|
-
}) =>
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
140
|
+
}) => {
|
|
141
|
+
const intervals = interval.split(",").map((value) => value.trim());
|
|
142
|
+
if (!intervals.length || intervals.some(
|
|
143
|
+
(value) => !value || !Number.isFinite(Number(value)) || Number(value) <= 0
|
|
144
|
+
)) {
|
|
145
|
+
throw new Error(`Invalid runtime feedback replay intervals: ${interval}`);
|
|
146
|
+
}
|
|
147
|
+
const replayTimeframe = intervals.sort(
|
|
148
|
+
(left, right) => Number(left) - Number(right)
|
|
149
|
+
)[0];
|
|
150
|
+
return [
|
|
151
|
+
[
|
|
152
|
+
cliPath,
|
|
153
|
+
"replay",
|
|
154
|
+
"--user",
|
|
155
|
+
userName,
|
|
156
|
+
"--connector",
|
|
157
|
+
connectorName,
|
|
158
|
+
"--deployment",
|
|
159
|
+
deploymentId,
|
|
160
|
+
"--timeframe",
|
|
161
|
+
replayTimeframe,
|
|
162
|
+
"--startTime",
|
|
163
|
+
String(startTime),
|
|
164
|
+
"--endTime",
|
|
165
|
+
String(endTime),
|
|
166
|
+
"--runtimeEvidence",
|
|
167
|
+
runtimeEvidencePath,
|
|
168
|
+
"--cacheOnly"
|
|
169
|
+
],
|
|
170
|
+
[
|
|
171
|
+
cliPath,
|
|
172
|
+
"replay-runtime-evidence",
|
|
173
|
+
"--user",
|
|
174
|
+
userName,
|
|
175
|
+
"--runtimeEvidence",
|
|
176
|
+
runtimeEvidencePath,
|
|
177
|
+
"--startTime",
|
|
178
|
+
String(startTime),
|
|
179
|
+
"--endTime",
|
|
180
|
+
String(endTime),
|
|
181
|
+
"--out",
|
|
182
|
+
replayEvidencePath
|
|
183
|
+
]
|
|
184
|
+
];
|
|
185
|
+
};
|
|
175
186
|
var runLoggedCommand = async ({
|
|
176
187
|
executable,
|
|
177
188
|
command,
|
|
@@ -258,10 +269,8 @@ var runRuntimeFeedbackReplay = async ({
|
|
|
258
269
|
const intervals = new Set(
|
|
259
270
|
activeStrategies.map(({ interval }) => String(interval))
|
|
260
271
|
);
|
|
261
|
-
if (intervals.size
|
|
262
|
-
throw new Error(
|
|
263
|
-
`Runtime feedback replay requires one interval, received ${[...intervals].join(",")}`
|
|
264
|
-
);
|
|
272
|
+
if (!intervals.size) {
|
|
273
|
+
throw new Error("Runtime feedback replay requires an active interval");
|
|
265
274
|
}
|
|
266
275
|
await import_promises.default.mkdir(outDir, { recursive: true });
|
|
267
276
|
const existing = await import_promises.default.readdir(outDir);
|
|
@@ -282,7 +291,7 @@ var runRuntimeFeedbackReplay = async ({
|
|
|
282
291
|
userName: runtimeEvidenceBundle.manifest.userName,
|
|
283
292
|
connectorName: deployment.connectorName,
|
|
284
293
|
deploymentId: deployment.id,
|
|
285
|
-
interval: [...intervals]
|
|
294
|
+
interval: [...intervals].join(","),
|
|
286
295
|
startTime: runtimeEvidenceBundle.manifest.window.startTime,
|
|
287
296
|
endTime: runtimeEvidenceBundle.manifest.window.endTime
|
|
288
297
|
});
|
|
@@ -48,14 +48,22 @@ var import_backtest = require("@tradejs/node/backtest");
|
|
|
48
48
|
var import_connectors5 = require("@tradejs/node/connectors");
|
|
49
49
|
|
|
50
50
|
// src/lib/runtimeParity.ts
|
|
51
|
+
var resolveTradeParityTimestampOffset = (strategy, offset) => typeof offset === "function" ? offset(strategy) : offset;
|
|
51
52
|
var toFiniteNumberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
52
53
|
var toGroupKey = (entry) => `${entry.strategy}::${entry.symbol}::${entry.direction}`;
|
|
53
54
|
var toRuntimeDuplicateKey = (entry) => `${toGroupKey(entry)}::${entry.timestamp}`;
|
|
54
55
|
var getBacktestParityComparisonTimestamp = (entry, timestampOffsetMs = 0) => {
|
|
55
|
-
|
|
56
|
+
const offsetMs = resolveTradeParityTimestampOffset(
|
|
57
|
+
entry.strategy,
|
|
58
|
+
timestampOffsetMs
|
|
59
|
+
);
|
|
60
|
+
if (!Number.isFinite(offsetMs) || offsetMs < 0) {
|
|
61
|
+
throw new Error(`Invalid backtest timestamp offset for ${entry.strategy}`);
|
|
62
|
+
}
|
|
63
|
+
if (offsetMs > 0 && entry.signalTimestamp != null && entry.timestamp >= entry.signalTimestamp + offsetMs) {
|
|
56
64
|
return entry.timestamp;
|
|
57
65
|
}
|
|
58
|
-
return (entry.signalTimestamp ?? entry.timestamp) +
|
|
66
|
+
return (entry.signalTimestamp ?? entry.timestamp) + offsetMs;
|
|
59
67
|
};
|
|
60
68
|
var sortTradeParityEntries = (left, right) => left.timestamp - right.timestamp || left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.direction.localeCompare(right.direction) || left.id.localeCompare(right.id);
|
|
61
69
|
var buildPriceDeltaPct = (leftPrice, rightPrice) => {
|
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.266",
|
|
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.266",
|
|
41
|
+
"@tradejs/core": "^3.1.29-beta.266",
|
|
42
|
+
"@tradejs/indicators": "^3.1.29-beta.266",
|
|
43
|
+
"@tradejs/infra": "^3.1.29-beta.266",
|
|
44
|
+
"@tradejs/node": "^3.1.29-beta.266",
|
|
45
45
|
"@tradejs/strategy-kit": "3.0.6",
|
|
46
|
-
"@tradejs/types": "^3.1.29-beta.
|
|
46
|
+
"@tradejs/types": "^3.1.29-beta.266",
|
|
47
47
|
"args": "^5.0.3",
|
|
48
48
|
"bcryptjs": "^2.4.3",
|
|
49
49
|
"chalk": "4.1.2",
|