@tradejs/cli 3.1.16 → 3.1.17
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 +581 -839
- package/dist/lib/runtimeDebugEvidence.js +7 -20
- package/dist/lib/runtimeEvidenceArtifacts.js +165 -0
- package/dist/lib/runtimeEvidenceDeployment.js +84 -4
- package/dist/lib/runtimeEvidenceSync.js +171 -112
- package/dist/lib/runtimeLineage.js +28 -306
- package/dist/lib/runtimeRedis.js +1 -3
- package/dist/lib/runtimeScorecard.js +177 -135
- package/dist/lib/strategyRelease.js +1 -1
- package/dist/scripts/replay.js +165 -370
- package/dist/scripts/replayRunner.js +165 -370
- package/dist/scripts/replayRuntimeEvidence.js +5 -16
- package/dist/scripts/runtimeEvidence.js +319 -187
- package/dist/scripts/runtimeEvidenceSync.js +172 -106
- package/dist/scripts/runtimeParity.js +1 -3
- package/dist/scripts/runtimeScorecard.js +220 -167
- package/dist/scripts/signals.js +47 -316
- package/dist/scripts/signalsDaemon.js +47 -316
- package/dist/scripts/signalsSummary.js +3 -8
- package/package.json +7 -7
package/dist/scripts/replay.js
CHANGED
|
@@ -111,9 +111,7 @@ var loadRuntimeTrades = async (userName, {
|
|
|
111
111
|
dedupedBucketTrades.set(trade.orderId, trade);
|
|
112
112
|
}
|
|
113
113
|
const windowedTrades = [...dedupedBucketTrades.values()].filter(filterByWindow).sort((left, right) => left.entryTimestamp - right.entryTimestamp);
|
|
114
|
-
|
|
115
|
-
return windowedTrades;
|
|
116
|
-
}
|
|
114
|
+
return windowedTrades;
|
|
117
115
|
}
|
|
118
116
|
const tradePrefix = import_redis.redisKeys.runtimeTrades(userName);
|
|
119
117
|
const bucketPrefix = import_redis.redisKeys.runtimeTradeBuckets(userName);
|
|
@@ -2706,27 +2704,27 @@ var createCoinMarketCapFetchStats = () => ({
|
|
|
2706
2704
|
totalCredits: 0,
|
|
2707
2705
|
byPath: /* @__PURE__ */ new Map()
|
|
2708
2706
|
});
|
|
2709
|
-
var getCoinMarketCapPathLabel = (
|
|
2710
|
-
if (
|
|
2711
|
-
if (
|
|
2712
|
-
if (
|
|
2713
|
-
if (
|
|
2714
|
-
if (
|
|
2715
|
-
if (
|
|
2716
|
-
return
|
|
2717
|
-
};
|
|
2718
|
-
var recordCoinMarketCapFetchStats = (stats,
|
|
2707
|
+
var getCoinMarketCapPathLabel = (path3) => {
|
|
2708
|
+
if (path3.includes("global-metrics")) return "global";
|
|
2709
|
+
if (path3.includes("cryptocurrency/quotes")) return "reference";
|
|
2710
|
+
if (path3.includes("exchange/quotes")) return "exchangeLiquidity";
|
|
2711
|
+
if (path3.includes("fear-and-greed")) return "fearGreed";
|
|
2712
|
+
if (path3.includes("cmc100")) return "cmc100";
|
|
2713
|
+
if (path3.includes("cmc20")) return "cmc20";
|
|
2714
|
+
return path3;
|
|
2715
|
+
};
|
|
2716
|
+
var recordCoinMarketCapFetchStats = (stats, path3, creditCount) => {
|
|
2719
2717
|
if (!stats) return;
|
|
2720
|
-
const existing = stats.byPath.get(
|
|
2718
|
+
const existing = stats.byPath.get(path3) ?? { requests: 0, credits: 0 };
|
|
2721
2719
|
existing.requests += 1;
|
|
2722
2720
|
existing.credits += creditCount ?? 0;
|
|
2723
2721
|
stats.requests += 1;
|
|
2724
2722
|
stats.totalCredits += creditCount ?? 0;
|
|
2725
|
-
stats.byPath.set(
|
|
2723
|
+
stats.byPath.set(path3, existing);
|
|
2726
2724
|
};
|
|
2727
2725
|
var formatCoinMarketCapFetchStats = (stats) => {
|
|
2728
2726
|
const byPath = [...stats.byPath.entries()].map(
|
|
2729
|
-
([
|
|
2727
|
+
([path3, item]) => `${getCoinMarketCapPathLabel(path3)}=${item.requests}req/${item.credits}cr`
|
|
2730
2728
|
).join(" ");
|
|
2731
2729
|
return `apiRequests=${stats.requests} apiCredits=${stats.totalCredits}${byPath ? ` ${byPath}` : ""}`;
|
|
2732
2730
|
};
|
|
@@ -4347,109 +4345,11 @@ var replayDeploymentId = String(
|
|
|
4347
4345
|
var isReplayUpdateOnlyRun = Boolean(replayFlags.updateOnly);
|
|
4348
4346
|
|
|
4349
4347
|
// src/lib/replay/runtimeEvidenceSource.ts
|
|
4350
|
-
var import_promises3 = __toESM(require("fs/promises"));
|
|
4351
|
-
var import_node_path3 = __toESM(require("path"));
|
|
4352
|
-
|
|
4353
|
-
// src/lib/runtimeLineage.ts
|
|
4354
|
-
var import_node_child_process = require("child_process");
|
|
4355
|
-
var import_node_crypto2 = require("crypto");
|
|
4356
|
-
var import_promises2 = __toESM(require("fs/promises"));
|
|
4357
|
-
var import_node_path2 = __toESM(require("path"));
|
|
4358
|
-
var import_types2 = require("@tradejs/types");
|
|
4359
|
-
var import_strategies3 = require("@tradejs/node/strategies");
|
|
4360
|
-
var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvidence");
|
|
4361
|
-
|
|
4362
|
-
// src/lib/strategyGateFingerprint.ts
|
|
4363
|
-
var import_node_crypto = require("crypto");
|
|
4364
|
-
var import_node_module = require("module");
|
|
4365
4348
|
var import_promises = __toESM(require("fs/promises"));
|
|
4366
4349
|
var import_node_path = __toESM(require("path"));
|
|
4367
|
-
var toPackageToken = (strategyName) => strategyName.replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/([a-z\d])([A-Z])/g, "$1-$2").toLowerCase();
|
|
4368
|
-
var getStrategyPackageName = (strategyName) => strategyName === "TrendLine" || strategyName === "ReverseTrendLine" ? "@tradejs/strategy-trend-line" : `@tradejs/strategy-${toPackageToken(strategyName)}`;
|
|
4369
|
-
var gateFingerprintSpecifiers = (strategyName) => [
|
|
4370
|
-
getStrategyPackageName(strategyName),
|
|
4371
|
-
"@tradejs/strategy-kit/ai-gate",
|
|
4372
|
-
"@tradejs/node/ai",
|
|
4373
|
-
"@tradejs/core/strategies"
|
|
4374
|
-
];
|
|
4375
|
-
var resolveStrategyGateFingerprint = async ({
|
|
4376
|
-
projectRoot,
|
|
4377
|
-
strategyName,
|
|
4378
|
-
gitSha
|
|
4379
|
-
}) => {
|
|
4380
|
-
const projectManifestPath = import_node_path.default.join(projectRoot, "package.json");
|
|
4381
|
-
const projectExists = await import_promises.default.access(projectManifestPath).then(() => true).catch(() => false);
|
|
4382
|
-
const requireFromProject = projectExists ? (0, import_node_module.createRequire)(projectManifestPath) : null;
|
|
4383
|
-
const candidates = requireFromProject ? await Promise.all(
|
|
4384
|
-
gateFingerprintSpecifiers(strategyName).map(async (specifier) => {
|
|
4385
|
-
let resolvedPath;
|
|
4386
|
-
try {
|
|
4387
|
-
resolvedPath = requireFromProject.resolve(specifier);
|
|
4388
|
-
} catch {
|
|
4389
|
-
return null;
|
|
4390
|
-
}
|
|
4391
|
-
const content = await import_promises.default.readFile(resolvedPath).catch(() => null);
|
|
4392
|
-
return content == null ? null : { specifier, content };
|
|
4393
|
-
})
|
|
4394
|
-
) : [];
|
|
4395
|
-
const sourceEntries = candidates.filter(
|
|
4396
|
-
(entry) => entry != null
|
|
4397
|
-
);
|
|
4398
|
-
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
4399
|
-
hash.update(strategyName);
|
|
4400
|
-
if (sourceEntries.length) {
|
|
4401
|
-
for (const entry of sourceEntries) {
|
|
4402
|
-
hash.update(entry.specifier);
|
|
4403
|
-
hash.update(entry.content);
|
|
4404
|
-
}
|
|
4405
|
-
} else {
|
|
4406
|
-
hash.update(gitSha ?? "unknown-git-sha");
|
|
4407
|
-
}
|
|
4408
|
-
return {
|
|
4409
|
-
gateFingerprint: hash.digest("hex").slice(0, 16),
|
|
4410
|
-
gateFingerprintFiles: sourceEntries.map((entry) => entry.specifier)
|
|
4411
|
-
};
|
|
4412
|
-
};
|
|
4413
4350
|
|
|
4414
4351
|
// src/lib/runtimeLineage.ts
|
|
4415
|
-
var
|
|
4416
|
-
"AI_MODE",
|
|
4417
|
-
"MIN_AI_QUALITY",
|
|
4418
|
-
"INTERVAL",
|
|
4419
|
-
"DERIVATIVES_CONTEXT_ENABLED",
|
|
4420
|
-
"DERIVATIVES_CONTEXT_TARGET_ENABLED",
|
|
4421
|
-
"DERIVATIVES_CONTEXT_LOOKBACK_HOURS",
|
|
4422
|
-
"DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS",
|
|
4423
|
-
"DERIVATIVES_CONTEXT_EXCHANGE_PRIORITY",
|
|
4424
|
-
"COINMARKETCAP_CONTEXT_ENABLED",
|
|
4425
|
-
"COINMARKETCAP_CONTEXT_MAX_AGE_MS",
|
|
4426
|
-
"COINMARKETCAP_CONTEXT_EXCHANGE_LIQUIDITY_ENABLED",
|
|
4427
|
-
"COINMARKETCAP_CONTEXT_EXCHANGE_SLUGS",
|
|
4428
|
-
"COINMARKETCAP_CONTEXT_FEAR_GREED_ENABLED",
|
|
4429
|
-
"COINMARKETCAP_CONTEXT_HISTORICAL_ACCESS_MONTHS",
|
|
4430
|
-
"COINMARKETCAP_CONTEXT_BACKFILL_ENABLED",
|
|
4431
|
-
"COINMARKETCAP_CONTEXT_BACKFILL_MAX_DAYS",
|
|
4432
|
-
"COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS",
|
|
4433
|
-
"HYPERLIQUID_WHALE_CONTEXT_ENABLED",
|
|
4434
|
-
"HYPERLIQUID_WHALE_BACKFILL_ENABLED",
|
|
4435
|
-
"HYPERLIQUID_WHALE_MIN_COVERAGE_PCT",
|
|
4436
|
-
"HYPERLIQUID_WHALE_CONCURRENCY",
|
|
4437
|
-
"HYPERLIQUID_WHALE_RATE_LIMIT_WEIGHT"
|
|
4438
|
-
];
|
|
4439
|
-
var RUNTIME_CONTEXT_DATA_MODEL = {
|
|
4440
|
-
derivativesSourceIntervals: "15m",
|
|
4441
|
-
derivativesDerivedIntervals: "1h",
|
|
4442
|
-
derivativesHourlyFallback: "stored-1h",
|
|
4443
|
-
derivativesDataModelVersion: 2,
|
|
4444
|
-
coinMarketCapAvailabilityModel: "closed-interval-all-daily",
|
|
4445
|
-
coinMarketCapDataModelVersion: 3,
|
|
4446
|
-
binanceBreadthUniverseModel: "fixed-versioned-top5-top10-top30-top50-top100",
|
|
4447
|
-
binanceBreadthDataModelVersion: 2,
|
|
4448
|
-
hyperliquidWhaleCanonicalInterval: "1m",
|
|
4449
|
-
hyperliquidWhaleGateMinNotionalUsd: 5e4,
|
|
4450
|
-
hyperliquidWhaleMinCoveragePct: 0.8,
|
|
4451
|
-
hyperliquidWhaleDataModelVersion: import_types2.HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
4452
|
-
};
|
|
4352
|
+
var import_node_crypto = require("crypto");
|
|
4453
4353
|
var normalizeForStableJson = (value) => {
|
|
4454
4354
|
if (Array.isArray(value)) {
|
|
4455
4355
|
return value.map(normalizeForStableJson);
|
|
@@ -4474,210 +4374,48 @@ var resolveRuntimeMaxLossValue = (config) => {
|
|
|
4474
4374
|
);
|
|
4475
4375
|
return Number.isFinite(maxLossValue) ? maxLossValue : null;
|
|
4476
4376
|
};
|
|
4477
|
-
var fingerprintRuntimeValue = (value) => (0,
|
|
4478
|
-
var
|
|
4479
|
-
var gateFingerprintCache = /* @__PURE__ */ new Map();
|
|
4480
|
-
var binanceBreadthFingerprintCache = /* @__PURE__ */ new Map();
|
|
4481
|
-
var snapshotFingerprintCache = /* @__PURE__ */ new Map();
|
|
4482
|
-
var resolveGitLineage = (projectRoot) => {
|
|
4483
|
-
const cached = gitLineageCache.get(projectRoot);
|
|
4484
|
-
if (cached) {
|
|
4485
|
-
return cached;
|
|
4486
|
-
}
|
|
4487
|
-
const envSha = String(process.env.TRADEJS_GIT_SHA ?? "").trim();
|
|
4488
|
-
if (envSha && envSha !== "unknown") {
|
|
4489
|
-
const result = {
|
|
4490
|
-
gitSha: envSha,
|
|
4491
|
-
gitDirty: false
|
|
4492
|
-
};
|
|
4493
|
-
gitLineageCache.set(projectRoot, result);
|
|
4494
|
-
return result;
|
|
4495
|
-
}
|
|
4496
|
-
try {
|
|
4497
|
-
const gitSha = (0, import_node_child_process.execFileSync)("git", ["rev-parse", "HEAD"], {
|
|
4498
|
-
cwd: projectRoot,
|
|
4499
|
-
encoding: "utf8",
|
|
4500
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
4501
|
-
}).trim();
|
|
4502
|
-
const status = (0, import_node_child_process.execFileSync)(
|
|
4503
|
-
"git",
|
|
4504
|
-
["status", "--porcelain", "--untracked-files=normal"],
|
|
4505
|
-
{
|
|
4506
|
-
cwd: projectRoot,
|
|
4507
|
-
encoding: "utf8",
|
|
4508
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
4509
|
-
}
|
|
4510
|
-
).trim();
|
|
4511
|
-
const result = {
|
|
4512
|
-
gitSha: gitSha || null,
|
|
4513
|
-
gitDirty: status.length > 0
|
|
4514
|
-
};
|
|
4515
|
-
gitLineageCache.set(projectRoot, result);
|
|
4516
|
-
return result;
|
|
4517
|
-
} catch {
|
|
4518
|
-
const result = {
|
|
4519
|
-
gitSha: null,
|
|
4520
|
-
gitDirty: null
|
|
4521
|
-
};
|
|
4522
|
-
gitLineageCache.set(projectRoot, result);
|
|
4523
|
-
return result;
|
|
4524
|
-
}
|
|
4525
|
-
};
|
|
4526
|
-
var readOptionalFile = async (filePath) => {
|
|
4527
|
-
try {
|
|
4528
|
-
return await import_promises2.default.readFile(filePath);
|
|
4529
|
-
} catch {
|
|
4530
|
-
return null;
|
|
4531
|
-
}
|
|
4532
|
-
};
|
|
4533
|
-
var resolveBinanceBreadthFingerprint = async (projectRoot) => {
|
|
4534
|
-
if (binanceBreadthFingerprintCache.has(projectRoot)) {
|
|
4535
|
-
return binanceBreadthFingerprintCache.get(projectRoot) ?? null;
|
|
4536
|
-
}
|
|
4537
|
-
const content = await readOptionalFile(
|
|
4538
|
-
import_node_path2.default.join(
|
|
4539
|
-
projectRoot,
|
|
4540
|
-
"packages/node/src/config/binanceBreadthUniverses.json"
|
|
4541
|
-
)
|
|
4542
|
-
);
|
|
4543
|
-
let result = null;
|
|
4544
|
-
if (content) {
|
|
4545
|
-
try {
|
|
4546
|
-
const parsed = JSON.parse(content.toString("utf8"));
|
|
4547
|
-
result = typeof parsed.fingerprint === "string" && parsed.fingerprint.trim() ? parsed.fingerprint.trim() : null;
|
|
4548
|
-
} catch {
|
|
4549
|
-
result = null;
|
|
4550
|
-
}
|
|
4551
|
-
}
|
|
4552
|
-
binanceBreadthFingerprintCache.set(projectRoot, result);
|
|
4553
|
-
return result;
|
|
4554
|
-
};
|
|
4555
|
-
var resolveSnapshotFingerprint = async (projectRoot, relativePath) => {
|
|
4556
|
-
const cacheKey = `${projectRoot}:${relativePath}`;
|
|
4557
|
-
if (snapshotFingerprintCache.has(cacheKey)) {
|
|
4558
|
-
return snapshotFingerprintCache.get(cacheKey) ?? null;
|
|
4559
|
-
}
|
|
4560
|
-
const content = await readOptionalFile(import_node_path2.default.join(projectRoot, relativePath));
|
|
4561
|
-
let result = null;
|
|
4562
|
-
if (content) {
|
|
4563
|
-
try {
|
|
4564
|
-
const parsed = JSON.parse(content.toString("utf8"));
|
|
4565
|
-
result = typeof parsed.fingerprint === "string" && parsed.fingerprint.trim() ? parsed.fingerprint.trim() : null;
|
|
4566
|
-
} catch {
|
|
4567
|
-
result = null;
|
|
4568
|
-
}
|
|
4569
|
-
}
|
|
4570
|
-
snapshotFingerprintCache.set(cacheKey, result);
|
|
4571
|
-
return result;
|
|
4572
|
-
};
|
|
4573
|
-
var resolveRuntimeGateFingerprint = async ({
|
|
4574
|
-
projectRoot,
|
|
4575
|
-
strategyName,
|
|
4576
|
-
gitSha
|
|
4577
|
-
}) => {
|
|
4578
|
-
const cacheKey = `${projectRoot}:${strategyName}:${gitSha ?? "unknown"}`;
|
|
4579
|
-
const cached = gateFingerprintCache.get(cacheKey);
|
|
4580
|
-
if (cached) return cached;
|
|
4581
|
-
const result = await resolveStrategyGateFingerprint({
|
|
4582
|
-
projectRoot,
|
|
4583
|
-
strategyName,
|
|
4584
|
-
gitSha
|
|
4585
|
-
});
|
|
4586
|
-
gateFingerprintCache.set(cacheKey, result);
|
|
4587
|
-
return result;
|
|
4588
|
-
};
|
|
4589
|
-
async function buildRuntimeLineage({
|
|
4590
|
-
projectRoot,
|
|
4591
|
-
strategyName,
|
|
4592
|
-
compositionId,
|
|
4377
|
+
var fingerprintRuntimeValue = (value) => (0, import_node_crypto.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
|
|
4378
|
+
var buildRuntimeLineage = async ({
|
|
4593
4379
|
strategyRevision,
|
|
4594
4380
|
deploymentCompositionId,
|
|
4595
4381
|
strategyPackageVersion,
|
|
4596
4382
|
strategyDependencyVersions,
|
|
4597
4383
|
runtimePackageVersion,
|
|
4598
|
-
config
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
})
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
([name, version]) => !name.startsWith("@tradejs/") || typeof version !== "string" || !version.trim()
|
|
4616
|
-
)) {
|
|
4617
|
-
throw new Error("Invalid strategy dependency versions");
|
|
4618
|
-
}
|
|
4619
|
-
return {
|
|
4620
|
-
schemaVersion: 3,
|
|
4621
|
-
strategyRevision,
|
|
4622
|
-
deploymentCompositionId,
|
|
4623
|
-
strategyPackageVersion,
|
|
4624
|
-
strategyDependencyVersions,
|
|
4625
|
-
runtimePackageVersion,
|
|
4626
|
-
maxLossValue: resolveRuntimeMaxLossValue(config)
|
|
4627
|
-
};
|
|
4384
|
+
config
|
|
4385
|
+
}) => {
|
|
4386
|
+
if (!/^sr1:[a-f0-9]{16}$/.test(strategyRevision)) {
|
|
4387
|
+
throw new Error(`Invalid strategy revision: ${strategyRevision}`);
|
|
4388
|
+
}
|
|
4389
|
+
if (!/^dc1:[a-f0-9]{16}$/.test(deploymentCompositionId)) {
|
|
4390
|
+
throw new Error(
|
|
4391
|
+
`Invalid deployment composition id: ${deploymentCompositionId}`
|
|
4392
|
+
);
|
|
4393
|
+
}
|
|
4394
|
+
if (!strategyPackageVersion.trim() || !runtimePackageVersion.trim()) {
|
|
4395
|
+
throw new Error("Invalid revision package versions");
|
|
4396
|
+
}
|
|
4397
|
+
if (Object.keys(strategyDependencyVersions).length === 0 || Object.entries(strategyDependencyVersions).some(
|
|
4398
|
+
([name, version]) => !name.startsWith("@tradejs/") || typeof version !== "string" || !version.trim()
|
|
4399
|
+
)) {
|
|
4400
|
+
throw new Error("Invalid strategy dependency versions");
|
|
4628
4401
|
}
|
|
4629
|
-
const git = resolveGitLineage(projectRoot);
|
|
4630
|
-
const hyperliquidPerpUniverseFingerprint = typeof import_strategies3.getHyperliquidPerpUniverseSnapshot === "function" ? (0, import_strategies3.getHyperliquidPerpUniverseSnapshot)().fingerprint : await resolveSnapshotFingerprint(
|
|
4631
|
-
projectRoot,
|
|
4632
|
-
"packages/node/src/config/hyperliquidPerpUniverse.json"
|
|
4633
|
-
);
|
|
4634
|
-
const hyperliquidWhaleRegistryFingerprint = typeof import_strategies3.getHyperliquidWhaleRegistrySnapshot === "function" ? (0, import_strategies3.getHyperliquidWhaleRegistrySnapshot)().fingerprint : await resolveSnapshotFingerprint(
|
|
4635
|
-
projectRoot,
|
|
4636
|
-
"packages/node/src/config/hyperliquidWhales.json"
|
|
4637
|
-
);
|
|
4638
|
-
const context = {
|
|
4639
|
-
...Object.fromEntries(
|
|
4640
|
-
RUNTIME_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
|
|
4641
|
-
),
|
|
4642
|
-
...RUNTIME_CONTEXT_DATA_MODEL,
|
|
4643
|
-
binanceBreadthUniverseFingerprint: await resolveBinanceBreadthFingerprint(projectRoot),
|
|
4644
|
-
hyperliquidPerpUniverseFingerprint,
|
|
4645
|
-
hyperliquidWhaleRegistryFingerprint,
|
|
4646
|
-
...runContext
|
|
4647
|
-
};
|
|
4648
4402
|
return {
|
|
4649
|
-
schemaVersion:
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
gitSha: git.gitSha
|
|
4656
|
-
})).gateFingerprint,
|
|
4657
|
-
// Position risk is tracked separately so changing trade size does not
|
|
4658
|
-
// masquerade as a change to the core + gate decision logic.
|
|
4659
|
-
configFingerprint: (0, import_strategyReleaseEvidence.strategyLogicConfigFingerprint)(config),
|
|
4660
|
-
contextFingerprint: fingerprintRuntimeValue(context),
|
|
4403
|
+
schemaVersion: 3,
|
|
4404
|
+
strategyRevision,
|
|
4405
|
+
deploymentCompositionId,
|
|
4406
|
+
strategyPackageVersion,
|
|
4407
|
+
strategyDependencyVersions,
|
|
4408
|
+
runtimePackageVersion,
|
|
4661
4409
|
maxLossValue: resolveRuntimeMaxLossValue(config)
|
|
4662
4410
|
};
|
|
4663
|
-
}
|
|
4664
|
-
var runtimeLineageKey = (lineage) =>
|
|
4411
|
+
};
|
|
4412
|
+
var runtimeLineageKey = (lineage) => [
|
|
4665
4413
|
"v3",
|
|
4666
4414
|
lineage.deploymentCompositionId,
|
|
4667
4415
|
lineage.strategyRevision,
|
|
4668
4416
|
lineage.strategyPackageVersion,
|
|
4669
|
-
`deps:${fingerprintRuntimeValue(
|
|
4670
|
-
lineage.strategyDependencyVersions
|
|
4671
|
-
).slice(0, 16)}`,
|
|
4417
|
+
`deps:${fingerprintRuntimeValue(lineage.strategyDependencyVersions).slice(0, 16)}`,
|
|
4672
4418
|
lineage.runtimePackageVersion
|
|
4673
|
-
].join(":") : [
|
|
4674
|
-
lineage.schemaVersion,
|
|
4675
|
-
lineage.compositionId ?? "unbound-composition",
|
|
4676
|
-
lineage.gitSha ?? "unknown",
|
|
4677
|
-
lineage.gitDirty == null ? "unknown" : lineage.gitDirty ? "dirty" : "clean",
|
|
4678
|
-
lineage.gateFingerprint,
|
|
4679
|
-
lineage.configFingerprint,
|
|
4680
|
-
lineage.contextFingerprint
|
|
4681
4419
|
].join(":");
|
|
4682
4420
|
var runtimeLineagesMatch = (left, right) => left != null && right != null && runtimeLineageKey(left) === runtimeLineageKey(right);
|
|
4683
4421
|
var runtimeLineagesComparable = (left, right) => runtimeLineagesMatch(left, right) && typeof left?.maxLossValue === "number" && Number.isFinite(left.maxLossValue) && typeof right?.maxLossValue === "number" && Number.isFinite(right.maxLossValue) && left.maxLossValue === right.maxLossValue;
|
|
@@ -4716,7 +4454,7 @@ var samePackageVersions = (left, right) => {
|
|
|
4716
4454
|
);
|
|
4717
4455
|
return JSON.stringify(leftEntries) === JSON.stringify(rightEntries);
|
|
4718
4456
|
};
|
|
4719
|
-
var
|
|
4457
|
+
var runtimeLineageMatchesStrategySnapshot = ({
|
|
4720
4458
|
lineage,
|
|
4721
4459
|
deployment,
|
|
4722
4460
|
strategy
|
|
@@ -4763,6 +4501,82 @@ var parseRuntimeEvidenceDeploymentSnapshot = (value) => {
|
|
|
4763
4501
|
};
|
|
4764
4502
|
};
|
|
4765
4503
|
var activeRuntimeEvidenceStrategies = (deployment) => deployment.enabled ? deployment.strategies.filter((strategy) => strategy.enabled) : [];
|
|
4504
|
+
var unwrapRuntimeEvidenceRows = (value, field) => (Array.isArray(value) ? value : []).map((candidate) => {
|
|
4505
|
+
const record = isRecord(candidate) ? candidate : null;
|
|
4506
|
+
const nested = record && isRecord(record[field]) ? record[field] : null;
|
|
4507
|
+
return nested ?? record;
|
|
4508
|
+
}).filter((record) => record != null);
|
|
4509
|
+
var assertCurrentRuntimeEvidenceArtifact = (artifact) => {
|
|
4510
|
+
if (!isRecord(artifact) || artifact.reportType !== "runtime-evidence") {
|
|
4511
|
+
throw new Error("Invalid runtime evidence artifact");
|
|
4512
|
+
}
|
|
4513
|
+
const deployment = parseRuntimeEvidenceDeploymentSnapshot(
|
|
4514
|
+
artifact.deployment
|
|
4515
|
+
);
|
|
4516
|
+
const activeStrategies = activeRuntimeEvidenceStrategies(deployment);
|
|
4517
|
+
if (activeStrategies.length === 0) {
|
|
4518
|
+
throw new Error("Runtime evidence deployment has no enabled strategies");
|
|
4519
|
+
}
|
|
4520
|
+
const runtime = isRecord(artifact.runtime) ? artifact.runtime : null;
|
|
4521
|
+
if (!runtime) {
|
|
4522
|
+
throw new Error("Runtime evidence payload is missing");
|
|
4523
|
+
}
|
|
4524
|
+
if ("strategyConfigs" in runtime) {
|
|
4525
|
+
throw new Error(
|
|
4526
|
+
"Runtime evidence payload contains removed strategyConfigs data"
|
|
4527
|
+
);
|
|
4528
|
+
}
|
|
4529
|
+
const rowFields = [
|
|
4530
|
+
"trades",
|
|
4531
|
+
"signals",
|
|
4532
|
+
"evaluations",
|
|
4533
|
+
"lineageScopes"
|
|
4534
|
+
];
|
|
4535
|
+
for (const field of rowFields) {
|
|
4536
|
+
if (!Array.isArray(runtime[field])) {
|
|
4537
|
+
throw new Error(`Runtime evidence payload ${field} must be an array`);
|
|
4538
|
+
}
|
|
4539
|
+
}
|
|
4540
|
+
const snapshots = new Map(
|
|
4541
|
+
activeStrategies.map((strategy) => [strategy.strategyName, strategy])
|
|
4542
|
+
);
|
|
4543
|
+
const assertRows = (label, rows, lineageField) => {
|
|
4544
|
+
for (const row of rows) {
|
|
4545
|
+
const strategyName = String(row.strategy ?? "").trim();
|
|
4546
|
+
const strategy = snapshots.get(strategyName);
|
|
4547
|
+
if (!strategy || row.deploymentId !== deployment.id || row.accountId !== deployment.accountId || !runtimeLineageMatchesStrategySnapshot({
|
|
4548
|
+
lineage: isRecord(row[lineageField]) ? row[lineageField] : void 0,
|
|
4549
|
+
deployment,
|
|
4550
|
+
strategy
|
|
4551
|
+
})) {
|
|
4552
|
+
throw new Error(
|
|
4553
|
+
`Runtime evidence ${label} row is outside the current embedded deployment lineage`
|
|
4554
|
+
);
|
|
4555
|
+
}
|
|
4556
|
+
}
|
|
4557
|
+
};
|
|
4558
|
+
assertRows(
|
|
4559
|
+
"trade",
|
|
4560
|
+
unwrapRuntimeEvidenceRows(runtime.trades, "trade"),
|
|
4561
|
+
"runtimeLineage"
|
|
4562
|
+
);
|
|
4563
|
+
assertRows(
|
|
4564
|
+
"signal",
|
|
4565
|
+
unwrapRuntimeEvidenceRows(runtime.signals, "signal"),
|
|
4566
|
+
"runtimeLineage"
|
|
4567
|
+
);
|
|
4568
|
+
assertRows(
|
|
4569
|
+
"evaluation",
|
|
4570
|
+
unwrapRuntimeEvidenceRows(runtime.evaluations, "evaluation"),
|
|
4571
|
+
"runtimeLineage"
|
|
4572
|
+
);
|
|
4573
|
+
assertRows(
|
|
4574
|
+
"lineage scope",
|
|
4575
|
+
unwrapRuntimeEvidenceRows(runtime.lineageScopes, "lineageScope"),
|
|
4576
|
+
"lineage"
|
|
4577
|
+
);
|
|
4578
|
+
return deployment;
|
|
4579
|
+
};
|
|
4766
4580
|
var resolveRuntimeEvidenceTickerUniverse = ({
|
|
4767
4581
|
deployment,
|
|
4768
4582
|
lineageScopes
|
|
@@ -4795,7 +4609,7 @@ var resolveRuntimeEvidenceTickerUniverse = ({
|
|
|
4795
4609
|
const tickers = /* @__PURE__ */ new Set();
|
|
4796
4610
|
for (const scope of lineageScopes) {
|
|
4797
4611
|
const strategy = strategiesByName.get(scope.strategy);
|
|
4798
|
-
if (!strategy || scope.deploymentId !== deployment.id || scope.accountId !== deployment.accountId || !isNonEmptyString(scope.symbol) || !
|
|
4612
|
+
if (!strategy || scope.deploymentId !== deployment.id || scope.accountId !== deployment.accountId || !isNonEmptyString(scope.symbol) || !runtimeLineageMatchesStrategySnapshot({
|
|
4799
4613
|
lineage: scope.lineage,
|
|
4800
4614
|
deployment,
|
|
4801
4615
|
strategy
|
|
@@ -4861,14 +4675,15 @@ var readRuntimeEvidenceArtifact = async ({
|
|
|
4861
4675
|
filePath,
|
|
4862
4676
|
projectRoot
|
|
4863
4677
|
}) => {
|
|
4864
|
-
const resolvedPath =
|
|
4678
|
+
const resolvedPath = import_node_path.default.resolve(projectRoot, filePath);
|
|
4865
4679
|
const root = asRecord(
|
|
4866
|
-
JSON.parse(await
|
|
4680
|
+
JSON.parse(await import_promises.default.readFile(resolvedPath, "utf8"))
|
|
4867
4681
|
);
|
|
4868
4682
|
const runtime = asRecord(root?.runtime) ?? root;
|
|
4869
4683
|
if (!root || !runtime) {
|
|
4870
4684
|
throw new Error(`Invalid runtime evidence JSON: ${resolvedPath}`);
|
|
4871
4685
|
}
|
|
4686
|
+
assertCurrentRuntimeEvidenceArtifact(root);
|
|
4872
4687
|
const rootWindow = asRecord(root.window);
|
|
4873
4688
|
const runtimeWindow = asRecord(runtime.window);
|
|
4874
4689
|
return {
|
|
@@ -5129,13 +4944,13 @@ var compactReplaySignal = (signal) => {
|
|
|
5129
4944
|
var import_chalk9 = __toESM(require("chalk"));
|
|
5130
4945
|
var import_progress4 = __toESM(require("progress"));
|
|
5131
4946
|
var import_backtest = require("@tradejs/core/backtest");
|
|
5132
|
-
var
|
|
4947
|
+
var import_strategies3 = require("@tradejs/core/strategies");
|
|
5133
4948
|
var import_time3 = require("@tradejs/core/time");
|
|
5134
4949
|
var import_logger = require("@tradejs/infra/logger");
|
|
5135
4950
|
var import_redis3 = require("@tradejs/infra/redis");
|
|
5136
4951
|
var import_connectors6 = require("@tradejs/node/connectors");
|
|
5137
4952
|
var import_cli4 = require("@tradejs/node/cli");
|
|
5138
|
-
var
|
|
4953
|
+
var import_strategies4 = require("@tradejs/node/strategies");
|
|
5139
4954
|
var import_runtimeStrategies3 = require("@tradejs/node/runtimeStrategies");
|
|
5140
4955
|
|
|
5141
4956
|
// src/lib/replay/portfolioReplayConnector.ts
|
|
@@ -5601,7 +5416,7 @@ var loadRuntimeStrategies = async (runtimeStrategies) => {
|
|
|
5601
5416
|
strategyConfig,
|
|
5602
5417
|
selection
|
|
5603
5418
|
} = runtimeStrategy;
|
|
5604
|
-
const strategyCreator = await (0,
|
|
5419
|
+
const strategyCreator = await (0, import_strategies4.getStrategyCreator)(
|
|
5605
5420
|
strategyName,
|
|
5606
5421
|
replayProjectRoot
|
|
5607
5422
|
);
|
|
@@ -5840,8 +5655,6 @@ var runHistoricalSignalsReplay = async ({
|
|
|
5840
5655
|
strategyResults
|
|
5841
5656
|
}) => {
|
|
5842
5657
|
const runtimeLineage = await buildRuntimeLineage({
|
|
5843
|
-
projectRoot: replayProjectRoot,
|
|
5844
|
-
strategyName,
|
|
5845
5658
|
strategyRevision,
|
|
5846
5659
|
deploymentCompositionId,
|
|
5847
5660
|
strategyPackageVersion,
|
|
@@ -5851,11 +5664,6 @@ var runHistoricalSignalsReplay = async ({
|
|
|
5851
5664
|
configId: strategyRevision,
|
|
5852
5665
|
strategyConfig,
|
|
5853
5666
|
symbolResultConfig: strategyResults?.[symbol]?.config ?? null
|
|
5854
|
-
},
|
|
5855
|
-
runContext: {
|
|
5856
|
-
connectorName: connectorName.toLowerCase(),
|
|
5857
|
-
interval: String(interval),
|
|
5858
|
-
universe: preparedRun.universe ?? null
|
|
5859
5667
|
}
|
|
5860
5668
|
});
|
|
5861
5669
|
runtimeLineages.push({
|
|
@@ -5986,7 +5794,7 @@ var runHistoricalSignalsReplay = async ({
|
|
|
5986
5794
|
);
|
|
5987
5795
|
if (result && typeof result !== "string") {
|
|
5988
5796
|
result.runtimeLineage = strategyRuntime.runtimeLineage;
|
|
5989
|
-
await (0,
|
|
5797
|
+
await (0, import_strategies4.enrichSignalWithBinanceMarketContext)({
|
|
5990
5798
|
signal: result,
|
|
5991
5799
|
env: "PARITY"
|
|
5992
5800
|
});
|
|
@@ -6010,8 +5818,8 @@ var runHistoricalSignalsReplay = async ({
|
|
|
6010
5818
|
}
|
|
6011
5819
|
} finally {
|
|
6012
5820
|
for (const keyPrefix of sharedReplayKeyPrefixes) {
|
|
6013
|
-
(0,
|
|
6014
|
-
(0,
|
|
5821
|
+
(0, import_strategies3.releaseStrategyIndicatorsReplayCache)(keyPrefix);
|
|
5822
|
+
(0, import_strategies3.releaseStrategyReplayCache)(keyPrefix);
|
|
6015
5823
|
}
|
|
6016
5824
|
}
|
|
6017
5825
|
const artifacts = replayConnector.getReplayArtifacts();
|
|
@@ -8668,8 +8476,8 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8668
8476
|
};
|
|
8669
8477
|
|
|
8670
8478
|
// src/lib/replay/outputReport.ts
|
|
8671
|
-
var
|
|
8672
|
-
var
|
|
8479
|
+
var import_promises2 = __toESM(require("fs/promises"));
|
|
8480
|
+
var import_node_path2 = __toESM(require("path"));
|
|
8673
8481
|
var import_data = require("@tradejs/core/data");
|
|
8674
8482
|
var formatMsk = (timestamp) => typeof timestamp === "number" && Number.isFinite(timestamp) ? new Intl.DateTimeFormat("ru-RU", {
|
|
8675
8483
|
timeZone: "Europe/Moscow",
|
|
@@ -8908,33 +8716,20 @@ var renderLineageRows = (runtimeComparison) => {
|
|
|
8908
8716
|
const existing = grouped.get(record.strategy) ?? {
|
|
8909
8717
|
strategy: record.strategy,
|
|
8910
8718
|
scopes: 0,
|
|
8911
|
-
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8719
|
+
deploymentCompositions: /* @__PURE__ */ new Set(),
|
|
8720
|
+
strategyRevisions: /* @__PURE__ */ new Set(),
|
|
8721
|
+
strategyPackages: /* @__PURE__ */ new Set(),
|
|
8722
|
+
dependencySets: /* @__PURE__ */ new Set(),
|
|
8723
|
+
runtimePackages: /* @__PURE__ */ new Set()
|
|
8916
8724
|
};
|
|
8917
8725
|
existing.scopes += 1;
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
record.lineage.strategyPackageVersion,
|
|
8926
|
-
record.lineage.runtimePackageVersion
|
|
8927
|
-
].join("/")
|
|
8928
|
-
);
|
|
8929
|
-
} else {
|
|
8930
|
-
existing.gitShas.add(record.lineage.gitSha ?? "unknown");
|
|
8931
|
-
existing.dirty.add(
|
|
8932
|
-
record.lineage.gitDirty == null ? "unknown" : String(record.lineage.gitDirty)
|
|
8933
|
-
);
|
|
8934
|
-
existing.gates.add(record.lineage.gateFingerprint);
|
|
8935
|
-
existing.configs.add(record.lineage.configFingerprint);
|
|
8936
|
-
existing.contexts.add(record.lineage.contextFingerprint);
|
|
8937
|
-
}
|
|
8726
|
+
existing.deploymentCompositions.add(record.lineage.deploymentCompositionId);
|
|
8727
|
+
existing.strategyRevisions.add(record.lineage.strategyRevision);
|
|
8728
|
+
existing.strategyPackages.add(record.lineage.strategyPackageVersion);
|
|
8729
|
+
existing.dependencySets.add(
|
|
8730
|
+
Object.entries(record.lineage.strategyDependencyVersions).sort(([left], [right]) => left.localeCompare(right)).map(([name, version]) => `${name}@${version}`).join(", ")
|
|
8731
|
+
);
|
|
8732
|
+
existing.runtimePackages.add(record.lineage.runtimePackageVersion);
|
|
8938
8733
|
grouped.set(record.strategy, existing);
|
|
8939
8734
|
}
|
|
8940
8735
|
const formatSet = (values) => {
|
|
@@ -8945,29 +8740,29 @@ var renderLineageRows = (runtimeComparison) => {
|
|
|
8945
8740
|
[
|
|
8946
8741
|
"Strategy",
|
|
8947
8742
|
"Scopes",
|
|
8948
|
-
"
|
|
8949
|
-
"
|
|
8950
|
-
"
|
|
8951
|
-
"
|
|
8952
|
-
"
|
|
8743
|
+
"Deployment composition",
|
|
8744
|
+
"Strategy revision",
|
|
8745
|
+
"Strategy package",
|
|
8746
|
+
"Dependencies",
|
|
8747
|
+
"Runtime package"
|
|
8953
8748
|
],
|
|
8954
8749
|
[...grouped.values()].sort((left, right) => left.strategy.localeCompare(right.strategy)).map(
|
|
8955
8750
|
({
|
|
8956
8751
|
strategy,
|
|
8957
8752
|
scopes,
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
|
|
8753
|
+
deploymentCompositions,
|
|
8754
|
+
strategyRevisions,
|
|
8755
|
+
strategyPackages,
|
|
8756
|
+
dependencySets,
|
|
8757
|
+
runtimePackages
|
|
8963
8758
|
}) => [
|
|
8964
8759
|
strategy,
|
|
8965
8760
|
scopes,
|
|
8966
|
-
formatSet(
|
|
8967
|
-
formatSet(
|
|
8968
|
-
formatSet(
|
|
8969
|
-
|
|
8970
|
-
formatSet(
|
|
8761
|
+
formatSet(deploymentCompositions),
|
|
8762
|
+
formatSet(strategyRevisions),
|
|
8763
|
+
formatSet(strategyPackages),
|
|
8764
|
+
formatSet(dependencySets),
|
|
8765
|
+
formatSet(runtimePackages)
|
|
8971
8766
|
]
|
|
8972
8767
|
)
|
|
8973
8768
|
) : "_No replay lineage records._";
|
|
@@ -8986,8 +8781,8 @@ var writeReplayOutputReport = async ({
|
|
|
8986
8781
|
strategySnapshot,
|
|
8987
8782
|
runtimeComparison
|
|
8988
8783
|
}) => {
|
|
8989
|
-
const outputDir =
|
|
8990
|
-
await
|
|
8784
|
+
const outputDir = import_node_path2.default.resolve(projectRoot, "data/replay/output");
|
|
8785
|
+
await import_promises2.default.mkdir(outputDir, { recursive: true });
|
|
8991
8786
|
const perTradeAnalysis = buildPerTradeAnalysis(runtimeComparison);
|
|
8992
8787
|
const report = {
|
|
8993
8788
|
reportType: "replay-output",
|
|
@@ -9022,8 +8817,8 @@ var writeReplayOutputReport = async ({
|
|
|
9022
8817
|
runtimeComparison,
|
|
9023
8818
|
perTradeAnalysis
|
|
9024
8819
|
};
|
|
9025
|
-
const markdownPath =
|
|
9026
|
-
const jsonPath =
|
|
8820
|
+
const markdownPath = import_node_path2.default.join(outputDir, `${timestamp}-replay-report.md`);
|
|
8821
|
+
const jsonPath = import_node_path2.default.join(outputDir, `${timestamp}-replay-report.json`);
|
|
9027
8822
|
const lines = [
|
|
9028
8823
|
"# Replay Report",
|
|
9029
8824
|
"",
|
|
@@ -9089,8 +8884,8 @@ var writeReplayOutputReport = async ({
|
|
|
9089
8884
|
""
|
|
9090
8885
|
];
|
|
9091
8886
|
await Promise.all([
|
|
9092
|
-
|
|
9093
|
-
|
|
8887
|
+
import_promises2.default.writeFile(markdownPath, lines.join("\n"), "utf8"),
|
|
8888
|
+
import_promises2.default.writeFile(jsonPath, `${(0, import_data.toJson)(report, true)}
|
|
9094
8889
|
`, "utf8")
|
|
9095
8890
|
]);
|
|
9096
8891
|
return {
|