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