@tradejs/cli 2.0.17 → 2.0.18
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 +200 -47
- package/dist/lib/runtimeEvidenceArtifacts.js +13 -1
- package/dist/scripts/replay.js +159 -26
- package/dist/scripts/replayRunner.js +159 -26
- package/dist/scripts/runtimeEvidence.js +13 -1
- package/package.json +9 -9
package/dist/cli.js
CHANGED
|
@@ -3678,8 +3678,8 @@ var init_aiPocketSearch = __esm({
|
|
|
3678
3678
|
"volumeusd",
|
|
3679
3679
|
"vwap"
|
|
3680
3680
|
]);
|
|
3681
|
-
classifyAiPocketFeaturePath = (
|
|
3682
|
-
const segments = (Array.isArray(
|
|
3681
|
+
classifyAiPocketFeaturePath = (path25) => {
|
|
3682
|
+
const segments = (Array.isArray(path25) ? path25 : path25.split(".")).filter(
|
|
3683
3683
|
Boolean
|
|
3684
3684
|
);
|
|
3685
3685
|
const normalized = segments.map((segment) => segment.toLowerCase());
|
|
@@ -10868,27 +10868,27 @@ var init_coinMarketCapContextBackfill = __esm({
|
|
|
10868
10868
|
totalCredits: 0,
|
|
10869
10869
|
byPath: /* @__PURE__ */ new Map()
|
|
10870
10870
|
});
|
|
10871
|
-
getCoinMarketCapPathLabel = (
|
|
10872
|
-
if (
|
|
10873
|
-
if (
|
|
10874
|
-
if (
|
|
10875
|
-
if (
|
|
10876
|
-
if (
|
|
10877
|
-
if (
|
|
10878
|
-
return
|
|
10879
|
-
};
|
|
10880
|
-
recordCoinMarketCapFetchStats = (stats,
|
|
10871
|
+
getCoinMarketCapPathLabel = (path25) => {
|
|
10872
|
+
if (path25.includes("global-metrics")) return "global";
|
|
10873
|
+
if (path25.includes("cryptocurrency/quotes")) return "reference";
|
|
10874
|
+
if (path25.includes("exchange/quotes")) return "exchangeLiquidity";
|
|
10875
|
+
if (path25.includes("fear-and-greed")) return "fearGreed";
|
|
10876
|
+
if (path25.includes("cmc100")) return "cmc100";
|
|
10877
|
+
if (path25.includes("cmc20")) return "cmc20";
|
|
10878
|
+
return path25;
|
|
10879
|
+
};
|
|
10880
|
+
recordCoinMarketCapFetchStats = (stats, path25, creditCount) => {
|
|
10881
10881
|
if (!stats) return;
|
|
10882
|
-
const existing = stats.byPath.get(
|
|
10882
|
+
const existing = stats.byPath.get(path25) ?? { requests: 0, credits: 0 };
|
|
10883
10883
|
existing.requests += 1;
|
|
10884
10884
|
existing.credits += creditCount ?? 0;
|
|
10885
10885
|
stats.requests += 1;
|
|
10886
10886
|
stats.totalCredits += creditCount ?? 0;
|
|
10887
|
-
stats.byPath.set(
|
|
10887
|
+
stats.byPath.set(path25, existing);
|
|
10888
10888
|
};
|
|
10889
10889
|
formatCoinMarketCapFetchStats = (stats) => {
|
|
10890
10890
|
const byPath = [...stats.byPath.entries()].map(
|
|
10891
|
-
([
|
|
10891
|
+
([path25, item]) => `${getCoinMarketCapPathLabel(path25)}=${item.requests}req/${item.credits}cr`
|
|
10892
10892
|
).join(" ");
|
|
10893
10893
|
return `apiRequests=${stats.requests} apiCredits=${stats.totalCredits}${byPath ? ` ${byPath}` : ""}`;
|
|
10894
10894
|
};
|
|
@@ -16778,15 +16778,15 @@ var require_pg_connection_string = __commonJS({
|
|
|
16778
16778
|
if (config.sslnegotiation === "direct" && config.ssl === void 0) {
|
|
16779
16779
|
config.ssl = true;
|
|
16780
16780
|
}
|
|
16781
|
-
const
|
|
16781
|
+
const fs24 = config.sslcert || config.sslkey || config.sslrootcert ? require("fs") : null;
|
|
16782
16782
|
if (config.sslcert) {
|
|
16783
|
-
config.ssl.cert =
|
|
16783
|
+
config.ssl.cert = fs24.readFileSync(config.sslcert).toString();
|
|
16784
16784
|
}
|
|
16785
16785
|
if (config.sslkey) {
|
|
16786
|
-
config.ssl.key =
|
|
16786
|
+
config.ssl.key = fs24.readFileSync(config.sslkey).toString();
|
|
16787
16787
|
}
|
|
16788
16788
|
if (config.sslrootcert) {
|
|
16789
|
-
config.ssl.ca =
|
|
16789
|
+
config.ssl.ca = fs24.readFileSync(config.sslrootcert).toString();
|
|
16790
16790
|
}
|
|
16791
16791
|
if (options.useLibpqCompat && config.uselibpqcompat) {
|
|
16792
16792
|
throw new Error("Both useLibpqCompat and uselibpqcompat are set. Please use only one of them.");
|
|
@@ -18606,7 +18606,7 @@ var require_split2 = __commonJS({
|
|
|
18606
18606
|
var require_helper = __commonJS({
|
|
18607
18607
|
"../../node_modules/pgpass/lib/helper.js"(exports2, module2) {
|
|
18608
18608
|
"use strict";
|
|
18609
|
-
var
|
|
18609
|
+
var path25 = require("path");
|
|
18610
18610
|
var Stream = require("stream").Stream;
|
|
18611
18611
|
var split = require_split2();
|
|
18612
18612
|
var util = require("util");
|
|
@@ -18645,7 +18645,7 @@ var require_helper = __commonJS({
|
|
|
18645
18645
|
};
|
|
18646
18646
|
module2.exports.getFileName = function(rawEnv) {
|
|
18647
18647
|
var env = rawEnv || process.env;
|
|
18648
|
-
var file = env.PGPASSFILE || (isWin ?
|
|
18648
|
+
var file = env.PGPASSFILE || (isWin ? path25.join(env.APPDATA || "./", "postgresql", "pgpass.conf") : path25.join(env.HOME || "./", ".pgpass"));
|
|
18649
18649
|
return file;
|
|
18650
18650
|
};
|
|
18651
18651
|
module2.exports.usePgPass = function(stats, fname) {
|
|
@@ -18777,16 +18777,16 @@ var require_helper = __commonJS({
|
|
|
18777
18777
|
var require_lib = __commonJS({
|
|
18778
18778
|
"../../node_modules/pgpass/lib/index.js"(exports2, module2) {
|
|
18779
18779
|
"use strict";
|
|
18780
|
-
var
|
|
18781
|
-
var
|
|
18780
|
+
var path25 = require("path");
|
|
18781
|
+
var fs24 = require("fs");
|
|
18782
18782
|
var helper = require_helper();
|
|
18783
18783
|
module2.exports = function(connInfo, cb) {
|
|
18784
18784
|
var file = helper.getFileName();
|
|
18785
|
-
|
|
18785
|
+
fs24.stat(file, function(err, stat) {
|
|
18786
18786
|
if (err || !helper.usePgPass(stat, file)) {
|
|
18787
18787
|
return cb(void 0);
|
|
18788
18788
|
}
|
|
18789
|
-
var st =
|
|
18789
|
+
var st = fs24.createReadStream(file);
|
|
18790
18790
|
helper.getPassword(connInfo, st, cb);
|
|
18791
18791
|
});
|
|
18792
18792
|
};
|
|
@@ -27415,7 +27415,7 @@ var init_replayRuntimeEvidence = __esm({
|
|
|
27415
27415
|
});
|
|
27416
27416
|
|
|
27417
27417
|
// src/lib/runtimeEvidenceArtifacts.ts
|
|
27418
|
-
var import_node_crypto5, import_promises15, import_node_path8, RUNTIME_EVIDENCE_MANIFEST_FILE, RUNTIME_EVIDENCE_PAYLOAD_FILE, RUNTIME_EVIDENCE_COMPLETE_FILE, asRecord4, sha256, isExistingDestinationError, safeSegment, compactTimestamp, dateParts, assertFiniteTimestamp, parseManifest, publishRuntimeEvidenceBundle, verifyRuntimeEvidenceBundle, discoverRuntimeEvidenceBundles;
|
|
27418
|
+
var import_node_crypto5, import_promises15, import_node_path8, RUNTIME_EVIDENCE_MANIFEST_FILE, RUNTIME_EVIDENCE_PAYLOAD_FILE, RUNTIME_EVIDENCE_COMPLETE_FILE, asRecord4, sha256, runtimeEvidenceIdentitySha256, isExistingDestinationError, safeSegment, compactTimestamp, dateParts, assertFiniteTimestamp, parseManifest, publishRuntimeEvidenceBundle, verifyRuntimeEvidenceBundle, discoverRuntimeEvidenceBundles;
|
|
27419
27419
|
var init_runtimeEvidenceArtifacts = __esm({
|
|
27420
27420
|
"src/lib/runtimeEvidenceArtifacts.ts"() {
|
|
27421
27421
|
"use strict";
|
|
@@ -27427,6 +27427,17 @@ var init_runtimeEvidenceArtifacts = __esm({
|
|
|
27427
27427
|
RUNTIME_EVIDENCE_COMPLETE_FILE = ".complete";
|
|
27428
27428
|
asRecord4 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
27429
27429
|
sha256 = (value) => (0, import_node_crypto5.createHash)("sha256").update(value).digest("hex");
|
|
27430
|
+
runtimeEvidenceIdentitySha256 = (artifact) => {
|
|
27431
|
+
const identityArtifact = { ...artifact };
|
|
27432
|
+
delete identityArtifact.generatedAt;
|
|
27433
|
+
const runtime = asRecord4(identityArtifact.runtime);
|
|
27434
|
+
if (runtime) {
|
|
27435
|
+
const identityRuntime = { ...runtime };
|
|
27436
|
+
delete identityRuntime.generatedAt;
|
|
27437
|
+
identityArtifact.runtime = identityRuntime;
|
|
27438
|
+
}
|
|
27439
|
+
return sha256(JSON.stringify(identityArtifact));
|
|
27440
|
+
};
|
|
27430
27441
|
isExistingDestinationError = (error) => {
|
|
27431
27442
|
const code = error.code;
|
|
27432
27443
|
return code === "EEXIST" || code === "ENOTEMPTY";
|
|
@@ -27497,11 +27508,12 @@ var init_runtimeEvidenceArtifacts = __esm({
|
|
|
27497
27508
|
const payload = `${JSON.stringify(artifact, null, 2)}
|
|
27498
27509
|
`;
|
|
27499
27510
|
const payloadSha256 = sha256(payload);
|
|
27511
|
+
const identitySha256 = runtimeEvidenceIdentitySha256(artifact);
|
|
27500
27512
|
const safeDeploymentId = safeSegment(deploymentId, "default");
|
|
27501
27513
|
const artifactId = [
|
|
27502
27514
|
compactTimestamp(startTime),
|
|
27503
27515
|
compactTimestamp(endTime),
|
|
27504
|
-
|
|
27516
|
+
identitySha256.slice(0, 16)
|
|
27505
27517
|
].join("_");
|
|
27506
27518
|
const createdAt = Date.now();
|
|
27507
27519
|
const manifest = {
|
|
@@ -28908,21 +28920,21 @@ var init_serverHealth = __esm({
|
|
|
28908
28920
|
}
|
|
28909
28921
|
return `${minutes}m`;
|
|
28910
28922
|
};
|
|
28911
|
-
readDiskUsage = (
|
|
28923
|
+
readDiskUsage = (path25) => {
|
|
28912
28924
|
try {
|
|
28913
|
-
const stats = (0, import_node_fs2.statfsSync)(
|
|
28925
|
+
const stats = (0, import_node_fs2.statfsSync)(path25);
|
|
28914
28926
|
const totalBlocks = Number(stats.blocks ?? 0);
|
|
28915
28927
|
const availableBlocks = Number(stats.bavail ?? 0);
|
|
28916
28928
|
if (totalBlocks <= 0) {
|
|
28917
|
-
return { path:
|
|
28929
|
+
return { path: path25, usedPct: null };
|
|
28918
28930
|
}
|
|
28919
28931
|
const usedPct = (totalBlocks - Math.max(0, availableBlocks)) / totalBlocks * 100;
|
|
28920
28932
|
return {
|
|
28921
|
-
path:
|
|
28933
|
+
path: path25,
|
|
28922
28934
|
usedPct: Number.isFinite(usedPct) ? usedPct : null
|
|
28923
28935
|
};
|
|
28924
28936
|
} catch {
|
|
28925
|
-
return { path:
|
|
28937
|
+
return { path: path25, usedPct: null };
|
|
28926
28938
|
}
|
|
28927
28939
|
};
|
|
28928
28940
|
parseMeminfoBytes = (value) => {
|
|
@@ -33273,7 +33285,7 @@ var init_signalsDaemon = __esm({
|
|
|
33273
33285
|
});
|
|
33274
33286
|
|
|
33275
33287
|
// src/lib/replay/cliConfig.ts
|
|
33276
|
-
var import_args32, REPLAY_UNSUPPORTED_FLAGS, hasCliFlag2, assertNoUnsupportedReplayFlags, replayNormalizedArgv, replayFlags, replayInterval, replayUserName, replayProjectRoot, isReplayUpdateOnlyRun;
|
|
33288
|
+
var import_args32, REPLAY_UNSUPPORTED_FLAGS, hasCliFlag2, assertNoUnsupportedReplayFlags, replayNormalizedArgv, replayFlags, replayInterval, replayUserName, replayProjectRoot, replayRuntimeEvidencePath, isReplayUpdateOnlyRun;
|
|
33277
33289
|
var init_cliConfig2 = __esm({
|
|
33278
33290
|
"src/lib/replay/cliConfig.ts"() {
|
|
33279
33291
|
"use strict";
|
|
@@ -33322,6 +33334,10 @@ var init_cliConfig2 = __esm({
|
|
|
33322
33334
|
import_args32.default.option(["d", "days"], "Run replay only for the last N days");
|
|
33323
33335
|
import_args32.default.option("startTime", "Explicit replay start timestamp (ms or seconds)");
|
|
33324
33336
|
import_args32.default.option("endTime", "Explicit replay end timestamp (ms or seconds)");
|
|
33337
|
+
import_args32.default.option(
|
|
33338
|
+
"runtimeEvidence",
|
|
33339
|
+
"Immutable runtime evidence JSON used for offline runtime comparison"
|
|
33340
|
+
);
|
|
33325
33341
|
import_args32.default.option(["u", "updateOnly"], "Only update tickers history", false);
|
|
33326
33342
|
import_args32.default.option(["C", "cacheOnly"], "Do not update tickers history", false);
|
|
33327
33343
|
import_args32.default.option(
|
|
@@ -33347,6 +33363,7 @@ var init_cliConfig2 = __esm({
|
|
|
33347
33363
|
replayInterval = String(replayFlags.timeframe ?? 15);
|
|
33348
33364
|
replayUserName = String(replayFlags.user ?? "root");
|
|
33349
33365
|
replayProjectRoot = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
|
|
33366
|
+
replayRuntimeEvidencePath = typeof replayFlags.runtimeEvidence === "string" && replayFlags.runtimeEvidence.trim() ? replayFlags.runtimeEvidence.trim() : null;
|
|
33350
33367
|
isReplayUpdateOnlyRun = Boolean(replayFlags.updateOnly);
|
|
33351
33368
|
}
|
|
33352
33369
|
});
|
|
@@ -35374,6 +35391,121 @@ var init_runtimeParityDetails = __esm({
|
|
|
35374
35391
|
}
|
|
35375
35392
|
});
|
|
35376
35393
|
|
|
35394
|
+
// src/lib/replay/runtimeEvidenceSource.ts
|
|
35395
|
+
var import_promises20, import_node_path14, asRecord6, asFiniteNumber, unwrapRows2, buildRuntimeEvidenceLineageScopes, loadReplayRuntimeEvidenceSource;
|
|
35396
|
+
var init_runtimeEvidenceSource = __esm({
|
|
35397
|
+
"src/lib/replay/runtimeEvidenceSource.ts"() {
|
|
35398
|
+
"use strict";
|
|
35399
|
+
import_promises20 = __toESM(require("fs/promises"));
|
|
35400
|
+
import_node_path14 = __toESM(require("path"));
|
|
35401
|
+
init_runtimeLineage();
|
|
35402
|
+
asRecord6 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
35403
|
+
asFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
35404
|
+
unwrapRows2 = (value, field) => (Array.isArray(value) ? value : []).map((item) => {
|
|
35405
|
+
const row = asRecord6(item);
|
|
35406
|
+
return asRecord6(row?.[field]) ?? row;
|
|
35407
|
+
}).filter((item) => item != null);
|
|
35408
|
+
buildRuntimeEvidenceLineageScopes = ({
|
|
35409
|
+
signals: signals2,
|
|
35410
|
+
evaluations
|
|
35411
|
+
}) => {
|
|
35412
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
35413
|
+
const add = ({
|
|
35414
|
+
strategy,
|
|
35415
|
+
symbol,
|
|
35416
|
+
runtimeConfigId,
|
|
35417
|
+
timestamp,
|
|
35418
|
+
lineage
|
|
35419
|
+
}) => {
|
|
35420
|
+
if (!lineage || !Number.isFinite(timestamp)) {
|
|
35421
|
+
return;
|
|
35422
|
+
}
|
|
35423
|
+
const key = `${strategy}::${symbol}::${runtimeLineageKey(lineage)}`;
|
|
35424
|
+
const existing = scopes.get(key);
|
|
35425
|
+
scopes.set(key, {
|
|
35426
|
+
strategy,
|
|
35427
|
+
symbol,
|
|
35428
|
+
...runtimeConfigId ? { runtimeConfigId } : {},
|
|
35429
|
+
lineage,
|
|
35430
|
+
firstTimestamp: existing ? Math.min(existing.firstTimestamp, timestamp) : timestamp,
|
|
35431
|
+
lastTimestamp: existing ? Math.max(existing.lastTimestamp, timestamp) : timestamp
|
|
35432
|
+
});
|
|
35433
|
+
};
|
|
35434
|
+
for (const signal of signals2) {
|
|
35435
|
+
add({
|
|
35436
|
+
strategy: signal.strategy,
|
|
35437
|
+
symbol: signal.symbol,
|
|
35438
|
+
runtimeConfigId: signal.runtimeConfigId,
|
|
35439
|
+
timestamp: signal.timestamp,
|
|
35440
|
+
lineage: signal.runtimeLineage
|
|
35441
|
+
});
|
|
35442
|
+
}
|
|
35443
|
+
for (const evaluation of evaluations) {
|
|
35444
|
+
add({
|
|
35445
|
+
strategy: evaluation.strategy,
|
|
35446
|
+
symbol: evaluation.symbol,
|
|
35447
|
+
runtimeConfigId: evaluation.runtimeConfigId,
|
|
35448
|
+
timestamp: evaluation.timestamp,
|
|
35449
|
+
lineage: evaluation.runtimeLineage
|
|
35450
|
+
});
|
|
35451
|
+
}
|
|
35452
|
+
return [...scopes.values()].sort(
|
|
35453
|
+
(left, right) => left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.firstTimestamp - right.firstTimestamp
|
|
35454
|
+
);
|
|
35455
|
+
};
|
|
35456
|
+
loadReplayRuntimeEvidenceSource = async ({
|
|
35457
|
+
filePath,
|
|
35458
|
+
projectRoot: projectRoot16,
|
|
35459
|
+
expectedUserName,
|
|
35460
|
+
expectedWindow
|
|
35461
|
+
}) => {
|
|
35462
|
+
const resolvedPath = import_node_path14.default.resolve(projectRoot16, filePath);
|
|
35463
|
+
const root = asRecord6(
|
|
35464
|
+
JSON.parse(await import_promises20.default.readFile(resolvedPath, "utf8"))
|
|
35465
|
+
);
|
|
35466
|
+
const runtime = asRecord6(root?.runtime) ?? root;
|
|
35467
|
+
if (!root || !runtime) {
|
|
35468
|
+
throw new Error(`Invalid runtime evidence JSON: ${resolvedPath}`);
|
|
35469
|
+
}
|
|
35470
|
+
const rootWindow = asRecord6(root.window);
|
|
35471
|
+
const runtimeWindow2 = asRecord6(runtime.window);
|
|
35472
|
+
const startTime = asFiniteNumber(rootWindow?.startTime) ?? asFiniteNumber(runtimeWindow2?.startTime);
|
|
35473
|
+
const endTime = asFiniteNumber(rootWindow?.endTime) ?? asFiniteNumber(runtimeWindow2?.endTime);
|
|
35474
|
+
const userName2 = String(root.userName ?? runtime.userName ?? "").trim();
|
|
35475
|
+
if (!userName2 || userName2 !== expectedUserName) {
|
|
35476
|
+
throw new Error(
|
|
35477
|
+
`Runtime evidence user mismatch: expected=${expectedUserName}, actual=${userName2 || "missing"}`
|
|
35478
|
+
);
|
|
35479
|
+
}
|
|
35480
|
+
const endTimeMatches = endTime === expectedWindow.end || endTime === expectedWindow.end + 1;
|
|
35481
|
+
if (startTime !== expectedWindow.start || !endTimeMatches) {
|
|
35482
|
+
throw new Error(
|
|
35483
|
+
`Runtime evidence window mismatch: expected=${expectedWindow.start}..${expectedWindow.end}, actual=${startTime ?? "missing"}..${endTime ?? "missing"}`
|
|
35484
|
+
);
|
|
35485
|
+
}
|
|
35486
|
+
const trades = unwrapRows2(runtime.trades, "trade");
|
|
35487
|
+
const signals2 = unwrapRows2(runtime.signals, "signal");
|
|
35488
|
+
const evaluations = unwrapRows2(
|
|
35489
|
+
runtime.evaluations,
|
|
35490
|
+
"evaluation"
|
|
35491
|
+
);
|
|
35492
|
+
return {
|
|
35493
|
+
path: resolvedPath,
|
|
35494
|
+
userName: userName2,
|
|
35495
|
+
startTime,
|
|
35496
|
+
endTime,
|
|
35497
|
+
trades,
|
|
35498
|
+
signals: signals2,
|
|
35499
|
+
evaluations,
|
|
35500
|
+
lineageScopes: buildRuntimeEvidenceLineageScopes({
|
|
35501
|
+
signals: signals2,
|
|
35502
|
+
evaluations
|
|
35503
|
+
})
|
|
35504
|
+
};
|
|
35505
|
+
};
|
|
35506
|
+
}
|
|
35507
|
+
});
|
|
35508
|
+
|
|
35377
35509
|
// src/lib/replay/runtimeComparison.ts
|
|
35378
35510
|
var import_chalk46, import_time11, getReplayEntryTimestampCompareOffsetMs, buildReplaySignalEvaluations, lineageScopeKey, buildExpectedLineageByScope, hasExpectedLineage, filterReplayComparisonByLineage, formatDrilldownSummary, toFiniteNumberOrNull4, sumOptionalNumbers, firstFiniteNumber, firstString, summarizeBacktestPnlByStrategy, getExchangeExecutionMergeKey, mergeExchangeExecutionGroup, mergeExchangeEntryExecutions, loadExchangeEntryRows, buildPriceDeltaPct3, loadExchangeEntriesForComparison, compareExchangeEntriesToBacktest, findExchangeRuntimeOutcome, splitExchangeMatchesByRuntimeOrderStatus, saveAndPrintReplayExchangeComparison, saveAndPrintReplayRuntimeComparison;
|
|
35379
35511
|
var init_runtimeComparison = __esm({
|
|
@@ -35390,6 +35522,7 @@ var init_runtimeComparison = __esm({
|
|
|
35390
35522
|
init_runtimeParityDetails();
|
|
35391
35523
|
init_runState();
|
|
35392
35524
|
init_cliConfig2();
|
|
35525
|
+
init_runtimeEvidenceSource();
|
|
35393
35526
|
init_support();
|
|
35394
35527
|
init_runtimeLineage();
|
|
35395
35528
|
getReplayEntryTimestampCompareOffsetMs = () => {
|
|
@@ -36160,7 +36293,8 @@ var init_runtimeComparison = __esm({
|
|
|
36160
36293
|
liveStrategySummaries,
|
|
36161
36294
|
backtestEntries,
|
|
36162
36295
|
replaySignals,
|
|
36163
|
-
replayLineages
|
|
36296
|
+
replayLineages,
|
|
36297
|
+
runtimeEvidencePath
|
|
36164
36298
|
}) => {
|
|
36165
36299
|
const { connector, connectorName, window: window2 } = getRuntimeCompareContext();
|
|
36166
36300
|
if (!connector || !window2) {
|
|
@@ -36170,12 +36304,23 @@ var init_runtimeComparison = __esm({
|
|
|
36170
36304
|
liveStrategySummaries.map((summary) => summary.strategyName)
|
|
36171
36305
|
);
|
|
36172
36306
|
const replaySignalEvaluations = buildReplaySignalEvaluations(replaySignals);
|
|
36307
|
+
const runtimeEvidence2 = runtimeEvidencePath ? await loadReplayRuntimeEvidenceSource({
|
|
36308
|
+
filePath: runtimeEvidencePath,
|
|
36309
|
+
projectRoot: replayProjectRoot,
|
|
36310
|
+
expectedUserName: replayUserName,
|
|
36311
|
+
expectedWindow: window2
|
|
36312
|
+
}) : null;
|
|
36173
36313
|
const [
|
|
36174
36314
|
rawRuntimeTrades,
|
|
36175
36315
|
runtimeSignals,
|
|
36176
36316
|
runtimeSignalEvaluations,
|
|
36177
36317
|
runtimeLineageScopes
|
|
36178
|
-
] =
|
|
36318
|
+
] = runtimeEvidence2 ? [
|
|
36319
|
+
runtimeEvidence2.trades,
|
|
36320
|
+
runtimeEvidence2.signals,
|
|
36321
|
+
runtimeEvidence2.evaluations,
|
|
36322
|
+
runtimeEvidence2.lineageScopes
|
|
36323
|
+
] : await Promise.all([
|
|
36179
36324
|
loadRuntimeTrades(replayUserName, {
|
|
36180
36325
|
startTime: window2.start,
|
|
36181
36326
|
endTime: window2.end
|
|
@@ -36193,7 +36338,14 @@ var init_runtimeComparison = __esm({
|
|
|
36193
36338
|
endTime: window2.end
|
|
36194
36339
|
})
|
|
36195
36340
|
]);
|
|
36196
|
-
|
|
36341
|
+
if (runtimeEvidence2) {
|
|
36342
|
+
console.log(
|
|
36343
|
+
import_chalk46.default.gray(
|
|
36344
|
+
`runtime compare: using immutable evidence ${runtimeEvidence2.path}`
|
|
36345
|
+
)
|
|
36346
|
+
);
|
|
36347
|
+
}
|
|
36348
|
+
const syncedRuntimeTrades = runtimeEvidence2 ? rawRuntimeTrades : await syncRuntimeTrades({
|
|
36197
36349
|
userName: replayUserName,
|
|
36198
36350
|
connector,
|
|
36199
36351
|
trades: rawRuntimeTrades,
|
|
@@ -36244,7 +36396,7 @@ var init_runtimeComparison = __esm({
|
|
|
36244
36396
|
const windowRuntimeTrades = comparable.runtimeTrades.filter(
|
|
36245
36397
|
(trade) => trade.entryTimestamp >= window2.start && trade.entryTimestamp < window2.end && relevantStrategies.has(trade.strategy)
|
|
36246
36398
|
);
|
|
36247
|
-
if (!windowRuntimeTrades.length) {
|
|
36399
|
+
if (!windowRuntimeTrades.length && !runtimeEvidence2) {
|
|
36248
36400
|
console.log("");
|
|
36249
36401
|
console.log(
|
|
36250
36402
|
import_chalk46.default.yellow(
|
|
@@ -36378,12 +36530,12 @@ var init_runtimeComparison = __esm({
|
|
|
36378
36530
|
});
|
|
36379
36531
|
|
|
36380
36532
|
// src/lib/replay/outputReport.ts
|
|
36381
|
-
var
|
|
36533
|
+
var import_promises21, import_node_path15, import_data4, formatMsk, formatNumber5, formatMoney2, formatPct, tableCell, createMarkdownTable2, entryStrategy, entryKey, buildDrilldownMap, buildPerTradeAnalysis, renderMatchedRows, renderRuntimeOnlyRows, renderOrderFailedRows, renderBacktestOnlyRows, renderStrategyRows, renderLineageRows, writeReplayOutputReport;
|
|
36382
36534
|
var init_outputReport = __esm({
|
|
36383
36535
|
"src/lib/replay/outputReport.ts"() {
|
|
36384
36536
|
"use strict";
|
|
36385
|
-
|
|
36386
|
-
|
|
36537
|
+
import_promises21 = __toESM(require("fs/promises"));
|
|
36538
|
+
import_node_path15 = __toESM(require("path"));
|
|
36387
36539
|
import_data4 = require("@tradejs/core/data");
|
|
36388
36540
|
init_support();
|
|
36389
36541
|
formatMsk = (timestamp) => typeof timestamp === "number" && Number.isFinite(timestamp) ? new Intl.DateTimeFormat("ru-RU", {
|
|
@@ -36688,8 +36840,8 @@ var init_outputReport = __esm({
|
|
|
36688
36840
|
strategySnapshot,
|
|
36689
36841
|
runtimeComparison
|
|
36690
36842
|
}) => {
|
|
36691
|
-
const outputDir =
|
|
36692
|
-
await
|
|
36843
|
+
const outputDir = import_node_path15.default.resolve(projectRoot16, "data/replay/output");
|
|
36844
|
+
await import_promises21.default.mkdir(outputDir, { recursive: true });
|
|
36693
36845
|
const perTradeAnalysis = buildPerTradeAnalysis(runtimeComparison);
|
|
36694
36846
|
const report = {
|
|
36695
36847
|
reportType: "replay-output",
|
|
@@ -36724,8 +36876,8 @@ var init_outputReport = __esm({
|
|
|
36724
36876
|
runtimeComparison,
|
|
36725
36877
|
perTradeAnalysis
|
|
36726
36878
|
};
|
|
36727
|
-
const markdownPath =
|
|
36728
|
-
const jsonPath =
|
|
36879
|
+
const markdownPath = import_node_path15.default.join(outputDir, `${timestamp}-replay-report.md`);
|
|
36880
|
+
const jsonPath = import_node_path15.default.join(outputDir, `${timestamp}-replay-report.json`);
|
|
36729
36881
|
const lines = [
|
|
36730
36882
|
"# Replay Report",
|
|
36731
36883
|
"",
|
|
@@ -36791,8 +36943,8 @@ var init_outputReport = __esm({
|
|
|
36791
36943
|
""
|
|
36792
36944
|
];
|
|
36793
36945
|
await Promise.all([
|
|
36794
|
-
|
|
36795
|
-
|
|
36946
|
+
import_promises21.default.writeFile(markdownPath, lines.join("\n"), "utf8"),
|
|
36947
|
+
import_promises21.default.writeFile(jsonPath, `${(0, import_data4.toJson)(report, true)}
|
|
36796
36948
|
`, "utf8")
|
|
36797
36949
|
]);
|
|
36798
36950
|
return {
|
|
@@ -36857,7 +37009,8 @@ var init_replayRunner = __esm({
|
|
|
36857
37009
|
liveStrategySummaries: replayStrategySnapshot.summaries,
|
|
36858
37010
|
backtestEntries: replayStrategySnapshot.backtestEntries,
|
|
36859
37011
|
replaySignals: replayResult.signals,
|
|
36860
|
-
replayLineages: replayResult.runtimeLineages
|
|
37012
|
+
replayLineages: replayResult.runtimeLineages,
|
|
37013
|
+
runtimeEvidencePath: replayRuntimeEvidencePath
|
|
36861
37014
|
});
|
|
36862
37015
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
36863
37016
|
const durationSeconds = Number(
|
|
@@ -46,6 +46,17 @@ var RUNTIME_EVIDENCE_PAYLOAD_FILE = "runtime-evidence.json";
|
|
|
46
46
|
var RUNTIME_EVIDENCE_COMPLETE_FILE = ".complete";
|
|
47
47
|
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
48
48
|
var sha256 = (value) => (0, import_node_crypto.createHash)("sha256").update(value).digest("hex");
|
|
49
|
+
var runtimeEvidenceIdentitySha256 = (artifact) => {
|
|
50
|
+
const identityArtifact = { ...artifact };
|
|
51
|
+
delete identityArtifact.generatedAt;
|
|
52
|
+
const runtime = asRecord(identityArtifact.runtime);
|
|
53
|
+
if (runtime) {
|
|
54
|
+
const identityRuntime = { ...runtime };
|
|
55
|
+
delete identityRuntime.generatedAt;
|
|
56
|
+
identityArtifact.runtime = identityRuntime;
|
|
57
|
+
}
|
|
58
|
+
return sha256(JSON.stringify(identityArtifact));
|
|
59
|
+
};
|
|
49
60
|
var isExistingDestinationError = (error) => {
|
|
50
61
|
const code = error.code;
|
|
51
62
|
return code === "EEXIST" || code === "ENOTEMPTY";
|
|
@@ -116,11 +127,12 @@ var publishRuntimeEvidenceBundle = async ({
|
|
|
116
127
|
const payload = `${JSON.stringify(artifact, null, 2)}
|
|
117
128
|
`;
|
|
118
129
|
const payloadSha256 = sha256(payload);
|
|
130
|
+
const identitySha256 = runtimeEvidenceIdentitySha256(artifact);
|
|
119
131
|
const safeDeploymentId = safeSegment(deploymentId, "default");
|
|
120
132
|
const artifactId = [
|
|
121
133
|
compactTimestamp(startTime),
|
|
122
134
|
compactTimestamp(endTime),
|
|
123
|
-
|
|
135
|
+
identitySha256.slice(0, 16)
|
|
124
136
|
].join("_");
|
|
125
137
|
const createdAt = Date.now();
|
|
126
138
|
const manifest = {
|
package/dist/scripts/replay.js
CHANGED
|
@@ -4349,27 +4349,27 @@ var createCoinMarketCapFetchStats = () => ({
|
|
|
4349
4349
|
totalCredits: 0,
|
|
4350
4350
|
byPath: /* @__PURE__ */ new Map()
|
|
4351
4351
|
});
|
|
4352
|
-
var getCoinMarketCapPathLabel = (
|
|
4353
|
-
if (
|
|
4354
|
-
if (
|
|
4355
|
-
if (
|
|
4356
|
-
if (
|
|
4357
|
-
if (
|
|
4358
|
-
if (
|
|
4359
|
-
return
|
|
4360
|
-
};
|
|
4361
|
-
var recordCoinMarketCapFetchStats = (stats,
|
|
4352
|
+
var getCoinMarketCapPathLabel = (path4) => {
|
|
4353
|
+
if (path4.includes("global-metrics")) return "global";
|
|
4354
|
+
if (path4.includes("cryptocurrency/quotes")) return "reference";
|
|
4355
|
+
if (path4.includes("exchange/quotes")) return "exchangeLiquidity";
|
|
4356
|
+
if (path4.includes("fear-and-greed")) return "fearGreed";
|
|
4357
|
+
if (path4.includes("cmc100")) return "cmc100";
|
|
4358
|
+
if (path4.includes("cmc20")) return "cmc20";
|
|
4359
|
+
return path4;
|
|
4360
|
+
};
|
|
4361
|
+
var recordCoinMarketCapFetchStats = (stats, path4, creditCount) => {
|
|
4362
4362
|
if (!stats) return;
|
|
4363
|
-
const existing = stats.byPath.get(
|
|
4363
|
+
const existing = stats.byPath.get(path4) ?? { requests: 0, credits: 0 };
|
|
4364
4364
|
existing.requests += 1;
|
|
4365
4365
|
existing.credits += creditCount ?? 0;
|
|
4366
4366
|
stats.requests += 1;
|
|
4367
4367
|
stats.totalCredits += creditCount ?? 0;
|
|
4368
|
-
stats.byPath.set(
|
|
4368
|
+
stats.byPath.set(path4, existing);
|
|
4369
4369
|
};
|
|
4370
4370
|
var formatCoinMarketCapFetchStats = (stats) => {
|
|
4371
4371
|
const byPath = [...stats.byPath.entries()].map(
|
|
4372
|
-
([
|
|
4372
|
+
([path4, item]) => `${getCoinMarketCapPathLabel(path4)}=${item.requests}req/${item.credits}cr`
|
|
4373
4373
|
).join(" ");
|
|
4374
4374
|
return `apiRequests=${stats.requests} apiCredits=${stats.totalCredits}${byPath ? ` ${byPath}` : ""}`;
|
|
4375
4375
|
};
|
|
@@ -5949,6 +5949,10 @@ import_args.default.option(["f", "timeframe"], "Timeframe", 15);
|
|
|
5949
5949
|
import_args.default.option(["d", "days"], "Run replay only for the last N days");
|
|
5950
5950
|
import_args.default.option("startTime", "Explicit replay start timestamp (ms or seconds)");
|
|
5951
5951
|
import_args.default.option("endTime", "Explicit replay end timestamp (ms or seconds)");
|
|
5952
|
+
import_args.default.option(
|
|
5953
|
+
"runtimeEvidence",
|
|
5954
|
+
"Immutable runtime evidence JSON used for offline runtime comparison"
|
|
5955
|
+
);
|
|
5952
5956
|
import_args.default.option(["u", "updateOnly"], "Only update tickers history", false);
|
|
5953
5957
|
import_args.default.option(["C", "cacheOnly"], "Do not update tickers history", false);
|
|
5954
5958
|
import_args.default.option(
|
|
@@ -5974,6 +5978,7 @@ var replayFlags = import_args.default.parse(replayNormalizedArgv);
|
|
|
5974
5978
|
var replayInterval = String(replayFlags.timeframe ?? 15);
|
|
5975
5979
|
var replayUserName = String(replayFlags.user ?? "root");
|
|
5976
5980
|
var replayProjectRoot = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
|
|
5981
|
+
var replayRuntimeEvidencePath = typeof replayFlags.runtimeEvidence === "string" && replayFlags.runtimeEvidence.trim() ? replayFlags.runtimeEvidence.trim() : null;
|
|
5977
5982
|
var isReplayUpdateOnlyRun = Boolean(replayFlags.updateOnly);
|
|
5978
5983
|
|
|
5979
5984
|
// src/lib/replay/support.ts
|
|
@@ -8806,6 +8811,114 @@ var buildReplayExchangeComparisonDetails = ({
|
|
|
8806
8811
|
};
|
|
8807
8812
|
};
|
|
8808
8813
|
|
|
8814
|
+
// src/lib/replay/runtimeEvidenceSource.ts
|
|
8815
|
+
var import_promises2 = __toESM(require("fs/promises"));
|
|
8816
|
+
var import_node_path2 = __toESM(require("path"));
|
|
8817
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
8818
|
+
var asFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
8819
|
+
var unwrapRows = (value, field) => (Array.isArray(value) ? value : []).map((item) => {
|
|
8820
|
+
const row = asRecord(item);
|
|
8821
|
+
return asRecord(row?.[field]) ?? row;
|
|
8822
|
+
}).filter((item) => item != null);
|
|
8823
|
+
var buildRuntimeEvidenceLineageScopes = ({
|
|
8824
|
+
signals,
|
|
8825
|
+
evaluations
|
|
8826
|
+
}) => {
|
|
8827
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
8828
|
+
const add = ({
|
|
8829
|
+
strategy,
|
|
8830
|
+
symbol,
|
|
8831
|
+
runtimeConfigId,
|
|
8832
|
+
timestamp,
|
|
8833
|
+
lineage
|
|
8834
|
+
}) => {
|
|
8835
|
+
if (!lineage || !Number.isFinite(timestamp)) {
|
|
8836
|
+
return;
|
|
8837
|
+
}
|
|
8838
|
+
const key = `${strategy}::${symbol}::${runtimeLineageKey(lineage)}`;
|
|
8839
|
+
const existing = scopes.get(key);
|
|
8840
|
+
scopes.set(key, {
|
|
8841
|
+
strategy,
|
|
8842
|
+
symbol,
|
|
8843
|
+
...runtimeConfigId ? { runtimeConfigId } : {},
|
|
8844
|
+
lineage,
|
|
8845
|
+
firstTimestamp: existing ? Math.min(existing.firstTimestamp, timestamp) : timestamp,
|
|
8846
|
+
lastTimestamp: existing ? Math.max(existing.lastTimestamp, timestamp) : timestamp
|
|
8847
|
+
});
|
|
8848
|
+
};
|
|
8849
|
+
for (const signal of signals) {
|
|
8850
|
+
add({
|
|
8851
|
+
strategy: signal.strategy,
|
|
8852
|
+
symbol: signal.symbol,
|
|
8853
|
+
runtimeConfigId: signal.runtimeConfigId,
|
|
8854
|
+
timestamp: signal.timestamp,
|
|
8855
|
+
lineage: signal.runtimeLineage
|
|
8856
|
+
});
|
|
8857
|
+
}
|
|
8858
|
+
for (const evaluation of evaluations) {
|
|
8859
|
+
add({
|
|
8860
|
+
strategy: evaluation.strategy,
|
|
8861
|
+
symbol: evaluation.symbol,
|
|
8862
|
+
runtimeConfigId: evaluation.runtimeConfigId,
|
|
8863
|
+
timestamp: evaluation.timestamp,
|
|
8864
|
+
lineage: evaluation.runtimeLineage
|
|
8865
|
+
});
|
|
8866
|
+
}
|
|
8867
|
+
return [...scopes.values()].sort(
|
|
8868
|
+
(left, right) => left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.firstTimestamp - right.firstTimestamp
|
|
8869
|
+
);
|
|
8870
|
+
};
|
|
8871
|
+
var loadReplayRuntimeEvidenceSource = async ({
|
|
8872
|
+
filePath,
|
|
8873
|
+
projectRoot,
|
|
8874
|
+
expectedUserName,
|
|
8875
|
+
expectedWindow
|
|
8876
|
+
}) => {
|
|
8877
|
+
const resolvedPath = import_node_path2.default.resolve(projectRoot, filePath);
|
|
8878
|
+
const root = asRecord(
|
|
8879
|
+
JSON.parse(await import_promises2.default.readFile(resolvedPath, "utf8"))
|
|
8880
|
+
);
|
|
8881
|
+
const runtime = asRecord(root?.runtime) ?? root;
|
|
8882
|
+
if (!root || !runtime) {
|
|
8883
|
+
throw new Error(`Invalid runtime evidence JSON: ${resolvedPath}`);
|
|
8884
|
+
}
|
|
8885
|
+
const rootWindow = asRecord(root.window);
|
|
8886
|
+
const runtimeWindow = asRecord(runtime.window);
|
|
8887
|
+
const startTime = asFiniteNumber(rootWindow?.startTime) ?? asFiniteNumber(runtimeWindow?.startTime);
|
|
8888
|
+
const endTime = asFiniteNumber(rootWindow?.endTime) ?? asFiniteNumber(runtimeWindow?.endTime);
|
|
8889
|
+
const userName = String(root.userName ?? runtime.userName ?? "").trim();
|
|
8890
|
+
if (!userName || userName !== expectedUserName) {
|
|
8891
|
+
throw new Error(
|
|
8892
|
+
`Runtime evidence user mismatch: expected=${expectedUserName}, actual=${userName || "missing"}`
|
|
8893
|
+
);
|
|
8894
|
+
}
|
|
8895
|
+
const endTimeMatches = endTime === expectedWindow.end || endTime === expectedWindow.end + 1;
|
|
8896
|
+
if (startTime !== expectedWindow.start || !endTimeMatches) {
|
|
8897
|
+
throw new Error(
|
|
8898
|
+
`Runtime evidence window mismatch: expected=${expectedWindow.start}..${expectedWindow.end}, actual=${startTime ?? "missing"}..${endTime ?? "missing"}`
|
|
8899
|
+
);
|
|
8900
|
+
}
|
|
8901
|
+
const trades = unwrapRows(runtime.trades, "trade");
|
|
8902
|
+
const signals = unwrapRows(runtime.signals, "signal");
|
|
8903
|
+
const evaluations = unwrapRows(
|
|
8904
|
+
runtime.evaluations,
|
|
8905
|
+
"evaluation"
|
|
8906
|
+
);
|
|
8907
|
+
return {
|
|
8908
|
+
path: resolvedPath,
|
|
8909
|
+
userName,
|
|
8910
|
+
startTime,
|
|
8911
|
+
endTime,
|
|
8912
|
+
trades,
|
|
8913
|
+
signals,
|
|
8914
|
+
evaluations,
|
|
8915
|
+
lineageScopes: buildRuntimeEvidenceLineageScopes({
|
|
8916
|
+
signals,
|
|
8917
|
+
evaluations
|
|
8918
|
+
})
|
|
8919
|
+
};
|
|
8920
|
+
};
|
|
8921
|
+
|
|
8809
8922
|
// src/lib/replay/runtimeComparison.ts
|
|
8810
8923
|
var getReplayEntryTimestampCompareOffsetMs = () => {
|
|
8811
8924
|
const intervalMinutes = Number(replayInterval);
|
|
@@ -9575,7 +9688,8 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
9575
9688
|
liveStrategySummaries,
|
|
9576
9689
|
backtestEntries,
|
|
9577
9690
|
replaySignals,
|
|
9578
|
-
replayLineages
|
|
9691
|
+
replayLineages,
|
|
9692
|
+
runtimeEvidencePath
|
|
9579
9693
|
}) => {
|
|
9580
9694
|
const { connector, connectorName, window: window2 } = getRuntimeCompareContext();
|
|
9581
9695
|
if (!connector || !window2) {
|
|
@@ -9585,12 +9699,23 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
9585
9699
|
liveStrategySummaries.map((summary) => summary.strategyName)
|
|
9586
9700
|
);
|
|
9587
9701
|
const replaySignalEvaluations = buildReplaySignalEvaluations(replaySignals);
|
|
9702
|
+
const runtimeEvidence = runtimeEvidencePath ? await loadReplayRuntimeEvidenceSource({
|
|
9703
|
+
filePath: runtimeEvidencePath,
|
|
9704
|
+
projectRoot: replayProjectRoot,
|
|
9705
|
+
expectedUserName: replayUserName,
|
|
9706
|
+
expectedWindow: window2
|
|
9707
|
+
}) : null;
|
|
9588
9708
|
const [
|
|
9589
9709
|
rawRuntimeTrades,
|
|
9590
9710
|
runtimeSignals,
|
|
9591
9711
|
runtimeSignalEvaluations,
|
|
9592
9712
|
runtimeLineageScopes
|
|
9593
|
-
] =
|
|
9713
|
+
] = runtimeEvidence ? [
|
|
9714
|
+
runtimeEvidence.trades,
|
|
9715
|
+
runtimeEvidence.signals,
|
|
9716
|
+
runtimeEvidence.evaluations,
|
|
9717
|
+
runtimeEvidence.lineageScopes
|
|
9718
|
+
] : await Promise.all([
|
|
9594
9719
|
loadRuntimeTrades(replayUserName, {
|
|
9595
9720
|
startTime: window2.start,
|
|
9596
9721
|
endTime: window2.end
|
|
@@ -9608,7 +9733,14 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
9608
9733
|
endTime: window2.end
|
|
9609
9734
|
})
|
|
9610
9735
|
]);
|
|
9611
|
-
|
|
9736
|
+
if (runtimeEvidence) {
|
|
9737
|
+
console.log(
|
|
9738
|
+
import_chalk11.default.gray(
|
|
9739
|
+
`runtime compare: using immutable evidence ${runtimeEvidence.path}`
|
|
9740
|
+
)
|
|
9741
|
+
);
|
|
9742
|
+
}
|
|
9743
|
+
const syncedRuntimeTrades = runtimeEvidence ? rawRuntimeTrades : await syncRuntimeTrades({
|
|
9612
9744
|
userName: replayUserName,
|
|
9613
9745
|
connector,
|
|
9614
9746
|
trades: rawRuntimeTrades,
|
|
@@ -9659,7 +9791,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
9659
9791
|
const windowRuntimeTrades = comparable.runtimeTrades.filter(
|
|
9660
9792
|
(trade) => trade.entryTimestamp >= window2.start && trade.entryTimestamp < window2.end && relevantStrategies.has(trade.strategy)
|
|
9661
9793
|
);
|
|
9662
|
-
if (!windowRuntimeTrades.length) {
|
|
9794
|
+
if (!windowRuntimeTrades.length && !runtimeEvidence) {
|
|
9663
9795
|
console.log("");
|
|
9664
9796
|
console.log(
|
|
9665
9797
|
import_chalk11.default.yellow(
|
|
@@ -9791,8 +9923,8 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
9791
9923
|
};
|
|
9792
9924
|
|
|
9793
9925
|
// src/lib/replay/outputReport.ts
|
|
9794
|
-
var
|
|
9795
|
-
var
|
|
9926
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
9927
|
+
var import_node_path3 = __toESM(require("path"));
|
|
9796
9928
|
var import_data = require("@tradejs/core/data");
|
|
9797
9929
|
var formatMsk = (timestamp) => typeof timestamp === "number" && Number.isFinite(timestamp) ? new Intl.DateTimeFormat("ru-RU", {
|
|
9798
9930
|
timeZone: "Europe/Moscow",
|
|
@@ -10096,8 +10228,8 @@ var writeReplayOutputReport = async ({
|
|
|
10096
10228
|
strategySnapshot,
|
|
10097
10229
|
runtimeComparison
|
|
10098
10230
|
}) => {
|
|
10099
|
-
const outputDir =
|
|
10100
|
-
await
|
|
10231
|
+
const outputDir = import_node_path3.default.resolve(projectRoot, "data/replay/output");
|
|
10232
|
+
await import_promises3.default.mkdir(outputDir, { recursive: true });
|
|
10101
10233
|
const perTradeAnalysis = buildPerTradeAnalysis(runtimeComparison);
|
|
10102
10234
|
const report = {
|
|
10103
10235
|
reportType: "replay-output",
|
|
@@ -10132,8 +10264,8 @@ var writeReplayOutputReport = async ({
|
|
|
10132
10264
|
runtimeComparison,
|
|
10133
10265
|
perTradeAnalysis
|
|
10134
10266
|
};
|
|
10135
|
-
const markdownPath =
|
|
10136
|
-
const jsonPath =
|
|
10267
|
+
const markdownPath = import_node_path3.default.join(outputDir, `${timestamp}-replay-report.md`);
|
|
10268
|
+
const jsonPath = import_node_path3.default.join(outputDir, `${timestamp}-replay-report.json`);
|
|
10137
10269
|
const lines = [
|
|
10138
10270
|
"# Replay Report",
|
|
10139
10271
|
"",
|
|
@@ -10199,8 +10331,8 @@ var writeReplayOutputReport = async ({
|
|
|
10199
10331
|
""
|
|
10200
10332
|
];
|
|
10201
10333
|
await Promise.all([
|
|
10202
|
-
|
|
10203
|
-
|
|
10334
|
+
import_promises3.default.writeFile(markdownPath, lines.join("\n"), "utf8"),
|
|
10335
|
+
import_promises3.default.writeFile(jsonPath, `${(0, import_data.toJson)(report, true)}
|
|
10204
10336
|
`, "utf8")
|
|
10205
10337
|
]);
|
|
10206
10338
|
return {
|
|
@@ -10241,7 +10373,8 @@ var finishReplay = async ({
|
|
|
10241
10373
|
liveStrategySummaries: replayStrategySnapshot.summaries,
|
|
10242
10374
|
backtestEntries: replayStrategySnapshot.backtestEntries,
|
|
10243
10375
|
replaySignals: replayResult.signals,
|
|
10244
|
-
replayLineages: replayResult.runtimeLineages
|
|
10376
|
+
replayLineages: replayResult.runtimeLineages,
|
|
10377
|
+
runtimeEvidencePath: replayRuntimeEvidencePath
|
|
10245
10378
|
});
|
|
10246
10379
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
10247
10380
|
const durationSeconds = Number(
|
|
@@ -4352,27 +4352,27 @@ var createCoinMarketCapFetchStats = () => ({
|
|
|
4352
4352
|
totalCredits: 0,
|
|
4353
4353
|
byPath: /* @__PURE__ */ new Map()
|
|
4354
4354
|
});
|
|
4355
|
-
var getCoinMarketCapPathLabel = (
|
|
4356
|
-
if (
|
|
4357
|
-
if (
|
|
4358
|
-
if (
|
|
4359
|
-
if (
|
|
4360
|
-
if (
|
|
4361
|
-
if (
|
|
4362
|
-
return
|
|
4363
|
-
};
|
|
4364
|
-
var recordCoinMarketCapFetchStats = (stats,
|
|
4355
|
+
var getCoinMarketCapPathLabel = (path4) => {
|
|
4356
|
+
if (path4.includes("global-metrics")) return "global";
|
|
4357
|
+
if (path4.includes("cryptocurrency/quotes")) return "reference";
|
|
4358
|
+
if (path4.includes("exchange/quotes")) return "exchangeLiquidity";
|
|
4359
|
+
if (path4.includes("fear-and-greed")) return "fearGreed";
|
|
4360
|
+
if (path4.includes("cmc100")) return "cmc100";
|
|
4361
|
+
if (path4.includes("cmc20")) return "cmc20";
|
|
4362
|
+
return path4;
|
|
4363
|
+
};
|
|
4364
|
+
var recordCoinMarketCapFetchStats = (stats, path4, creditCount) => {
|
|
4365
4365
|
if (!stats) return;
|
|
4366
|
-
const existing = stats.byPath.get(
|
|
4366
|
+
const existing = stats.byPath.get(path4) ?? { requests: 0, credits: 0 };
|
|
4367
4367
|
existing.requests += 1;
|
|
4368
4368
|
existing.credits += creditCount ?? 0;
|
|
4369
4369
|
stats.requests += 1;
|
|
4370
4370
|
stats.totalCredits += creditCount ?? 0;
|
|
4371
|
-
stats.byPath.set(
|
|
4371
|
+
stats.byPath.set(path4, existing);
|
|
4372
4372
|
};
|
|
4373
4373
|
var formatCoinMarketCapFetchStats = (stats) => {
|
|
4374
4374
|
const byPath = [...stats.byPath.entries()].map(
|
|
4375
|
-
([
|
|
4375
|
+
([path4, item]) => `${getCoinMarketCapPathLabel(path4)}=${item.requests}req/${item.credits}cr`
|
|
4376
4376
|
).join(" ");
|
|
4377
4377
|
return `apiRequests=${stats.requests} apiCredits=${stats.totalCredits}${byPath ? ` ${byPath}` : ""}`;
|
|
4378
4378
|
};
|
|
@@ -5952,6 +5952,10 @@ import_args.default.option(["f", "timeframe"], "Timeframe", 15);
|
|
|
5952
5952
|
import_args.default.option(["d", "days"], "Run replay only for the last N days");
|
|
5953
5953
|
import_args.default.option("startTime", "Explicit replay start timestamp (ms or seconds)");
|
|
5954
5954
|
import_args.default.option("endTime", "Explicit replay end timestamp (ms or seconds)");
|
|
5955
|
+
import_args.default.option(
|
|
5956
|
+
"runtimeEvidence",
|
|
5957
|
+
"Immutable runtime evidence JSON used for offline runtime comparison"
|
|
5958
|
+
);
|
|
5955
5959
|
import_args.default.option(["u", "updateOnly"], "Only update tickers history", false);
|
|
5956
5960
|
import_args.default.option(["C", "cacheOnly"], "Do not update tickers history", false);
|
|
5957
5961
|
import_args.default.option(
|
|
@@ -5977,6 +5981,7 @@ var replayFlags = import_args.default.parse(replayNormalizedArgv);
|
|
|
5977
5981
|
var replayInterval = String(replayFlags.timeframe ?? 15);
|
|
5978
5982
|
var replayUserName = String(replayFlags.user ?? "root");
|
|
5979
5983
|
var replayProjectRoot = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
|
|
5984
|
+
var replayRuntimeEvidencePath = typeof replayFlags.runtimeEvidence === "string" && replayFlags.runtimeEvidence.trim() ? replayFlags.runtimeEvidence.trim() : null;
|
|
5980
5985
|
var isReplayUpdateOnlyRun = Boolean(replayFlags.updateOnly);
|
|
5981
5986
|
|
|
5982
5987
|
// src/lib/replay/support.ts
|
|
@@ -8809,6 +8814,114 @@ var buildReplayExchangeComparisonDetails = ({
|
|
|
8809
8814
|
};
|
|
8810
8815
|
};
|
|
8811
8816
|
|
|
8817
|
+
// src/lib/replay/runtimeEvidenceSource.ts
|
|
8818
|
+
var import_promises2 = __toESM(require("fs/promises"));
|
|
8819
|
+
var import_node_path2 = __toESM(require("path"));
|
|
8820
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
8821
|
+
var asFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
8822
|
+
var unwrapRows = (value, field) => (Array.isArray(value) ? value : []).map((item) => {
|
|
8823
|
+
const row = asRecord(item);
|
|
8824
|
+
return asRecord(row?.[field]) ?? row;
|
|
8825
|
+
}).filter((item) => item != null);
|
|
8826
|
+
var buildRuntimeEvidenceLineageScopes = ({
|
|
8827
|
+
signals,
|
|
8828
|
+
evaluations
|
|
8829
|
+
}) => {
|
|
8830
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
8831
|
+
const add = ({
|
|
8832
|
+
strategy,
|
|
8833
|
+
symbol,
|
|
8834
|
+
runtimeConfigId,
|
|
8835
|
+
timestamp,
|
|
8836
|
+
lineage
|
|
8837
|
+
}) => {
|
|
8838
|
+
if (!lineage || !Number.isFinite(timestamp)) {
|
|
8839
|
+
return;
|
|
8840
|
+
}
|
|
8841
|
+
const key = `${strategy}::${symbol}::${runtimeLineageKey(lineage)}`;
|
|
8842
|
+
const existing = scopes.get(key);
|
|
8843
|
+
scopes.set(key, {
|
|
8844
|
+
strategy,
|
|
8845
|
+
symbol,
|
|
8846
|
+
...runtimeConfigId ? { runtimeConfigId } : {},
|
|
8847
|
+
lineage,
|
|
8848
|
+
firstTimestamp: existing ? Math.min(existing.firstTimestamp, timestamp) : timestamp,
|
|
8849
|
+
lastTimestamp: existing ? Math.max(existing.lastTimestamp, timestamp) : timestamp
|
|
8850
|
+
});
|
|
8851
|
+
};
|
|
8852
|
+
for (const signal of signals) {
|
|
8853
|
+
add({
|
|
8854
|
+
strategy: signal.strategy,
|
|
8855
|
+
symbol: signal.symbol,
|
|
8856
|
+
runtimeConfigId: signal.runtimeConfigId,
|
|
8857
|
+
timestamp: signal.timestamp,
|
|
8858
|
+
lineage: signal.runtimeLineage
|
|
8859
|
+
});
|
|
8860
|
+
}
|
|
8861
|
+
for (const evaluation of evaluations) {
|
|
8862
|
+
add({
|
|
8863
|
+
strategy: evaluation.strategy,
|
|
8864
|
+
symbol: evaluation.symbol,
|
|
8865
|
+
runtimeConfigId: evaluation.runtimeConfigId,
|
|
8866
|
+
timestamp: evaluation.timestamp,
|
|
8867
|
+
lineage: evaluation.runtimeLineage
|
|
8868
|
+
});
|
|
8869
|
+
}
|
|
8870
|
+
return [...scopes.values()].sort(
|
|
8871
|
+
(left, right) => left.strategy.localeCompare(right.strategy) || left.symbol.localeCompare(right.symbol) || left.firstTimestamp - right.firstTimestamp
|
|
8872
|
+
);
|
|
8873
|
+
};
|
|
8874
|
+
var loadReplayRuntimeEvidenceSource = async ({
|
|
8875
|
+
filePath,
|
|
8876
|
+
projectRoot,
|
|
8877
|
+
expectedUserName,
|
|
8878
|
+
expectedWindow
|
|
8879
|
+
}) => {
|
|
8880
|
+
const resolvedPath = import_node_path2.default.resolve(projectRoot, filePath);
|
|
8881
|
+
const root = asRecord(
|
|
8882
|
+
JSON.parse(await import_promises2.default.readFile(resolvedPath, "utf8"))
|
|
8883
|
+
);
|
|
8884
|
+
const runtime = asRecord(root?.runtime) ?? root;
|
|
8885
|
+
if (!root || !runtime) {
|
|
8886
|
+
throw new Error(`Invalid runtime evidence JSON: ${resolvedPath}`);
|
|
8887
|
+
}
|
|
8888
|
+
const rootWindow = asRecord(root.window);
|
|
8889
|
+
const runtimeWindow = asRecord(runtime.window);
|
|
8890
|
+
const startTime = asFiniteNumber(rootWindow?.startTime) ?? asFiniteNumber(runtimeWindow?.startTime);
|
|
8891
|
+
const endTime = asFiniteNumber(rootWindow?.endTime) ?? asFiniteNumber(runtimeWindow?.endTime);
|
|
8892
|
+
const userName = String(root.userName ?? runtime.userName ?? "").trim();
|
|
8893
|
+
if (!userName || userName !== expectedUserName) {
|
|
8894
|
+
throw new Error(
|
|
8895
|
+
`Runtime evidence user mismatch: expected=${expectedUserName}, actual=${userName || "missing"}`
|
|
8896
|
+
);
|
|
8897
|
+
}
|
|
8898
|
+
const endTimeMatches = endTime === expectedWindow.end || endTime === expectedWindow.end + 1;
|
|
8899
|
+
if (startTime !== expectedWindow.start || !endTimeMatches) {
|
|
8900
|
+
throw new Error(
|
|
8901
|
+
`Runtime evidence window mismatch: expected=${expectedWindow.start}..${expectedWindow.end}, actual=${startTime ?? "missing"}..${endTime ?? "missing"}`
|
|
8902
|
+
);
|
|
8903
|
+
}
|
|
8904
|
+
const trades = unwrapRows(runtime.trades, "trade");
|
|
8905
|
+
const signals = unwrapRows(runtime.signals, "signal");
|
|
8906
|
+
const evaluations = unwrapRows(
|
|
8907
|
+
runtime.evaluations,
|
|
8908
|
+
"evaluation"
|
|
8909
|
+
);
|
|
8910
|
+
return {
|
|
8911
|
+
path: resolvedPath,
|
|
8912
|
+
userName,
|
|
8913
|
+
startTime,
|
|
8914
|
+
endTime,
|
|
8915
|
+
trades,
|
|
8916
|
+
signals,
|
|
8917
|
+
evaluations,
|
|
8918
|
+
lineageScopes: buildRuntimeEvidenceLineageScopes({
|
|
8919
|
+
signals,
|
|
8920
|
+
evaluations
|
|
8921
|
+
})
|
|
8922
|
+
};
|
|
8923
|
+
};
|
|
8924
|
+
|
|
8812
8925
|
// src/lib/replay/runtimeComparison.ts
|
|
8813
8926
|
var getReplayEntryTimestampCompareOffsetMs = () => {
|
|
8814
8927
|
const intervalMinutes = Number(replayInterval);
|
|
@@ -9578,7 +9691,8 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
9578
9691
|
liveStrategySummaries,
|
|
9579
9692
|
backtestEntries,
|
|
9580
9693
|
replaySignals,
|
|
9581
|
-
replayLineages
|
|
9694
|
+
replayLineages,
|
|
9695
|
+
runtimeEvidencePath
|
|
9582
9696
|
}) => {
|
|
9583
9697
|
const { connector, connectorName, window: window2 } = getRuntimeCompareContext();
|
|
9584
9698
|
if (!connector || !window2) {
|
|
@@ -9588,12 +9702,23 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
9588
9702
|
liveStrategySummaries.map((summary) => summary.strategyName)
|
|
9589
9703
|
);
|
|
9590
9704
|
const replaySignalEvaluations = buildReplaySignalEvaluations(replaySignals);
|
|
9705
|
+
const runtimeEvidence = runtimeEvidencePath ? await loadReplayRuntimeEvidenceSource({
|
|
9706
|
+
filePath: runtimeEvidencePath,
|
|
9707
|
+
projectRoot: replayProjectRoot,
|
|
9708
|
+
expectedUserName: replayUserName,
|
|
9709
|
+
expectedWindow: window2
|
|
9710
|
+
}) : null;
|
|
9591
9711
|
const [
|
|
9592
9712
|
rawRuntimeTrades,
|
|
9593
9713
|
runtimeSignals,
|
|
9594
9714
|
runtimeSignalEvaluations,
|
|
9595
9715
|
runtimeLineageScopes
|
|
9596
|
-
] =
|
|
9716
|
+
] = runtimeEvidence ? [
|
|
9717
|
+
runtimeEvidence.trades,
|
|
9718
|
+
runtimeEvidence.signals,
|
|
9719
|
+
runtimeEvidence.evaluations,
|
|
9720
|
+
runtimeEvidence.lineageScopes
|
|
9721
|
+
] : await Promise.all([
|
|
9597
9722
|
loadRuntimeTrades(replayUserName, {
|
|
9598
9723
|
startTime: window2.start,
|
|
9599
9724
|
endTime: window2.end
|
|
@@ -9611,7 +9736,14 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
9611
9736
|
endTime: window2.end
|
|
9612
9737
|
})
|
|
9613
9738
|
]);
|
|
9614
|
-
|
|
9739
|
+
if (runtimeEvidence) {
|
|
9740
|
+
console.log(
|
|
9741
|
+
import_chalk11.default.gray(
|
|
9742
|
+
`runtime compare: using immutable evidence ${runtimeEvidence.path}`
|
|
9743
|
+
)
|
|
9744
|
+
);
|
|
9745
|
+
}
|
|
9746
|
+
const syncedRuntimeTrades = runtimeEvidence ? rawRuntimeTrades : await syncRuntimeTrades({
|
|
9615
9747
|
userName: replayUserName,
|
|
9616
9748
|
connector,
|
|
9617
9749
|
trades: rawRuntimeTrades,
|
|
@@ -9662,7 +9794,7 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
9662
9794
|
const windowRuntimeTrades = comparable.runtimeTrades.filter(
|
|
9663
9795
|
(trade) => trade.entryTimestamp >= window2.start && trade.entryTimestamp < window2.end && relevantStrategies.has(trade.strategy)
|
|
9664
9796
|
);
|
|
9665
|
-
if (!windowRuntimeTrades.length) {
|
|
9797
|
+
if (!windowRuntimeTrades.length && !runtimeEvidence) {
|
|
9666
9798
|
console.log("");
|
|
9667
9799
|
console.log(
|
|
9668
9800
|
import_chalk11.default.yellow(
|
|
@@ -9794,8 +9926,8 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
9794
9926
|
};
|
|
9795
9927
|
|
|
9796
9928
|
// src/lib/replay/outputReport.ts
|
|
9797
|
-
var
|
|
9798
|
-
var
|
|
9929
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
9930
|
+
var import_node_path3 = __toESM(require("path"));
|
|
9799
9931
|
var import_data = require("@tradejs/core/data");
|
|
9800
9932
|
var formatMsk = (timestamp) => typeof timestamp === "number" && Number.isFinite(timestamp) ? new Intl.DateTimeFormat("ru-RU", {
|
|
9801
9933
|
timeZone: "Europe/Moscow",
|
|
@@ -10099,8 +10231,8 @@ var writeReplayOutputReport = async ({
|
|
|
10099
10231
|
strategySnapshot,
|
|
10100
10232
|
runtimeComparison
|
|
10101
10233
|
}) => {
|
|
10102
|
-
const outputDir =
|
|
10103
|
-
await
|
|
10234
|
+
const outputDir = import_node_path3.default.resolve(projectRoot, "data/replay/output");
|
|
10235
|
+
await import_promises3.default.mkdir(outputDir, { recursive: true });
|
|
10104
10236
|
const perTradeAnalysis = buildPerTradeAnalysis(runtimeComparison);
|
|
10105
10237
|
const report = {
|
|
10106
10238
|
reportType: "replay-output",
|
|
@@ -10135,8 +10267,8 @@ var writeReplayOutputReport = async ({
|
|
|
10135
10267
|
runtimeComparison,
|
|
10136
10268
|
perTradeAnalysis
|
|
10137
10269
|
};
|
|
10138
|
-
const markdownPath =
|
|
10139
|
-
const jsonPath =
|
|
10270
|
+
const markdownPath = import_node_path3.default.join(outputDir, `${timestamp}-replay-report.md`);
|
|
10271
|
+
const jsonPath = import_node_path3.default.join(outputDir, `${timestamp}-replay-report.json`);
|
|
10140
10272
|
const lines = [
|
|
10141
10273
|
"# Replay Report",
|
|
10142
10274
|
"",
|
|
@@ -10202,8 +10334,8 @@ var writeReplayOutputReport = async ({
|
|
|
10202
10334
|
""
|
|
10203
10335
|
];
|
|
10204
10336
|
await Promise.all([
|
|
10205
|
-
|
|
10206
|
-
|
|
10337
|
+
import_promises3.default.writeFile(markdownPath, lines.join("\n"), "utf8"),
|
|
10338
|
+
import_promises3.default.writeFile(jsonPath, `${(0, import_data.toJson)(report, true)}
|
|
10207
10339
|
`, "utf8")
|
|
10208
10340
|
]);
|
|
10209
10341
|
return {
|
|
@@ -10244,7 +10376,8 @@ var finishReplay = async ({
|
|
|
10244
10376
|
liveStrategySummaries: replayStrategySnapshot.summaries,
|
|
10245
10377
|
backtestEntries: replayStrategySnapshot.backtestEntries,
|
|
10246
10378
|
replaySignals: replayResult.signals,
|
|
10247
|
-
replayLineages: replayResult.runtimeLineages
|
|
10379
|
+
replayLineages: replayResult.runtimeLineages,
|
|
10380
|
+
runtimeEvidencePath: replayRuntimeEvidencePath
|
|
10248
10381
|
});
|
|
10249
10382
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
10250
10383
|
const durationSeconds = Number(
|
|
@@ -417,6 +417,17 @@ var RUNTIME_EVIDENCE_PAYLOAD_FILE = "runtime-evidence.json";
|
|
|
417
417
|
var RUNTIME_EVIDENCE_COMPLETE_FILE = ".complete";
|
|
418
418
|
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
419
419
|
var sha256 = (value) => (0, import_node_crypto.createHash)("sha256").update(value).digest("hex");
|
|
420
|
+
var runtimeEvidenceIdentitySha256 = (artifact) => {
|
|
421
|
+
const identityArtifact = { ...artifact };
|
|
422
|
+
delete identityArtifact.generatedAt;
|
|
423
|
+
const runtime = asRecord(identityArtifact.runtime);
|
|
424
|
+
if (runtime) {
|
|
425
|
+
const identityRuntime = { ...runtime };
|
|
426
|
+
delete identityRuntime.generatedAt;
|
|
427
|
+
identityArtifact.runtime = identityRuntime;
|
|
428
|
+
}
|
|
429
|
+
return sha256(JSON.stringify(identityArtifact));
|
|
430
|
+
};
|
|
420
431
|
var isExistingDestinationError = (error) => {
|
|
421
432
|
const code = error.code;
|
|
422
433
|
return code === "EEXIST" || code === "ENOTEMPTY";
|
|
@@ -487,11 +498,12 @@ var publishRuntimeEvidenceBundle = async ({
|
|
|
487
498
|
const payload = `${JSON.stringify(artifact, null, 2)}
|
|
488
499
|
`;
|
|
489
500
|
const payloadSha256 = sha256(payload);
|
|
501
|
+
const identitySha256 = runtimeEvidenceIdentitySha256(artifact);
|
|
490
502
|
const safeDeploymentId = safeSegment(deploymentId, "default");
|
|
491
503
|
const artifactId = [
|
|
492
504
|
compactTimestamp(startTime),
|
|
493
505
|
compactTimestamp(endTime),
|
|
494
|
-
|
|
506
|
+
identitySha256.slice(0, 16)
|
|
495
507
|
].join("_");
|
|
496
508
|
const createdAt = Date.now();
|
|
497
509
|
const manifest = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tradejs/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.18",
|
|
4
4
|
"description": "Official CLI for the TradeJS TypeScript framework: infra setup, backtests, signals, bots, and ML workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tradejs",
|
|
@@ -29,14 +29,14 @@
|
|
|
29
29
|
"tradejs": "dist/cli.js"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@tradejs/base": "^2.0.
|
|
33
|
-
"@tradejs/connectors": "^2.0.
|
|
34
|
-
"@tradejs/core": "^2.0.
|
|
35
|
-
"@tradejs/indicators": "^2.0.
|
|
36
|
-
"@tradejs/infra": "^2.0.
|
|
37
|
-
"@tradejs/node": "^2.0.
|
|
38
|
-
"@tradejs/strategies": "^2.0.
|
|
39
|
-
"@tradejs/types": "^2.0.
|
|
32
|
+
"@tradejs/base": "^2.0.18",
|
|
33
|
+
"@tradejs/connectors": "^2.0.18",
|
|
34
|
+
"@tradejs/core": "^2.0.18",
|
|
35
|
+
"@tradejs/indicators": "^2.0.18",
|
|
36
|
+
"@tradejs/infra": "^2.0.18",
|
|
37
|
+
"@tradejs/node": "^2.0.18",
|
|
38
|
+
"@tradejs/strategies": "^2.0.18",
|
|
39
|
+
"@tradejs/types": "^2.0.18",
|
|
40
40
|
"args": "^5.0.3",
|
|
41
41
|
"bcryptjs": "^2.4.3",
|
|
42
42
|
"chalk": "4.1.2",
|