@tradejs/cli 2.0.19 → 2.0.20
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 +8787 -3214
- package/dist/lib/aiExport.js +14 -1
- package/dist/lib/aiPocketSearch.js +499 -67
- package/dist/lib/aiPocketSearchCli.js +113 -0
- package/dist/lib/aiTrainCharts.js +14 -1
- package/dist/lib/aiTrainMetrics.js +4 -1
- package/dist/lib/aiTrainResearch.js +88 -54
- package/dist/lib/coreResearch.js +2738 -0
- package/dist/lib/executionCalibration.js +6 -3
- package/dist/lib/runtimeLineage.js +81 -48
- package/dist/lib/runtimeScorecard.js +153 -10
- package/dist/lib/strategyGateFingerprint.js +80 -0
- package/dist/lib/strategyRelease.js +1365 -0
- package/dist/scripts/aiExport.js +16 -3
- package/dist/scripts/aiPocketSearch.js +1069 -163
- package/dist/scripts/aiTrain.js +91 -65
- package/dist/scripts/backtest.js +10 -1
- package/dist/scripts/coreResearch.js +2830 -0
- package/dist/scripts/executionCalibration.js +6 -3
- package/dist/scripts/replay.js +106 -73
- package/dist/scripts/replayRunner.js +106 -73
- package/dist/scripts/replayRuntimeEvidence.js +27 -14
- package/dist/scripts/runtimeEvidence.js +16 -8
- package/dist/scripts/runtimeScorecard.js +883 -29
- package/dist/scripts/signals.js +133 -89
- package/dist/scripts/signalsDaemon.js +133 -89
- package/dist/scripts/strategyRelease.js +1469 -0
- package/package.json +11 -9
|
@@ -169,7 +169,8 @@ var extractRuntimeTradeRows = (runtimePayload) => {
|
|
|
169
169
|
const signal = asRecord(redisValues?.signal) ?? (signalId ? signalsById.get(signalId) ?? null : null) ?? (shapeKey ? signalsByShape.get(shapeKey) ?? null : null);
|
|
170
170
|
return {
|
|
171
171
|
trade,
|
|
172
|
-
signal
|
|
172
|
+
signal,
|
|
173
|
+
runtimeLineage: asRecord(trade.runtimeLineage) ?? null
|
|
173
174
|
};
|
|
174
175
|
}).filter((row) => row != null);
|
|
175
176
|
};
|
|
@@ -471,7 +472,8 @@ var buildSample = ({
|
|
|
471
472
|
direction: direction ?? match?.direction ?? null,
|
|
472
473
|
expectedPrice: match?.backtestPrice ?? null,
|
|
473
474
|
actualPrice: match?.runtimePrice ?? null
|
|
474
|
-
})
|
|
475
|
+
}),
|
|
476
|
+
runtimeLineage: row.runtimeLineage
|
|
475
477
|
};
|
|
476
478
|
};
|
|
477
479
|
var buildReplayOnlySample = (match) => ({
|
|
@@ -518,7 +520,8 @@ var buildReplayOnlySample = (match) => ({
|
|
|
518
520
|
direction: match.direction,
|
|
519
521
|
expectedPrice: match.backtestPrice,
|
|
520
522
|
actualPrice: match.runtimePrice
|
|
521
|
-
})
|
|
523
|
+
}),
|
|
524
|
+
runtimeLineage: null
|
|
522
525
|
});
|
|
523
526
|
var hasTelemetry = (sample) => sample.signalClosePrice != null || sample.arrivalMid != null || sample.orderSubmitTime != null || sample.fillAvgPrice != null || sample.fillTime != null || sample.fee != null;
|
|
524
527
|
var hasFullTelemetry = (sample) => sample.signalClosePrice != null && sample.arrivalMid != null && sample.orderSubmitTime != null && sample.orderAckTime != null && sample.fillAvgPrice != null && sample.fillTime != null;
|
package/dist/scripts/replay.js
CHANGED
|
@@ -2700,27 +2700,27 @@ var createCoinMarketCapFetchStats = () => ({
|
|
|
2700
2700
|
totalCredits: 0,
|
|
2701
2701
|
byPath: /* @__PURE__ */ new Map()
|
|
2702
2702
|
});
|
|
2703
|
-
var getCoinMarketCapPathLabel = (
|
|
2704
|
-
if (
|
|
2705
|
-
if (
|
|
2706
|
-
if (
|
|
2707
|
-
if (
|
|
2708
|
-
if (
|
|
2709
|
-
if (
|
|
2710
|
-
return
|
|
2711
|
-
};
|
|
2712
|
-
var recordCoinMarketCapFetchStats = (stats,
|
|
2703
|
+
var getCoinMarketCapPathLabel = (path5) => {
|
|
2704
|
+
if (path5.includes("global-metrics")) return "global";
|
|
2705
|
+
if (path5.includes("cryptocurrency/quotes")) return "reference";
|
|
2706
|
+
if (path5.includes("exchange/quotes")) return "exchangeLiquidity";
|
|
2707
|
+
if (path5.includes("fear-and-greed")) return "fearGreed";
|
|
2708
|
+
if (path5.includes("cmc100")) return "cmc100";
|
|
2709
|
+
if (path5.includes("cmc20")) return "cmc20";
|
|
2710
|
+
return path5;
|
|
2711
|
+
};
|
|
2712
|
+
var recordCoinMarketCapFetchStats = (stats, path5, creditCount) => {
|
|
2713
2713
|
if (!stats) return;
|
|
2714
|
-
const existing = stats.byPath.get(
|
|
2714
|
+
const existing = stats.byPath.get(path5) ?? { requests: 0, credits: 0 };
|
|
2715
2715
|
existing.requests += 1;
|
|
2716
2716
|
existing.credits += creditCount ?? 0;
|
|
2717
2717
|
stats.requests += 1;
|
|
2718
2718
|
stats.totalCredits += creditCount ?? 0;
|
|
2719
|
-
stats.byPath.set(
|
|
2719
|
+
stats.byPath.set(path5, existing);
|
|
2720
2720
|
};
|
|
2721
2721
|
var formatCoinMarketCapFetchStats = (stats) => {
|
|
2722
2722
|
const byPath = [...stats.byPath.entries()].map(
|
|
2723
|
-
([
|
|
2723
|
+
([path5, item]) => `${getCoinMarketCapPathLabel(path5)}=${item.requests}req/${item.credits}cr`
|
|
2724
2724
|
).join(" ");
|
|
2725
2725
|
return `apiRequests=${stats.requests} apiCredits=${stats.totalCredits}${byPath ? ` ${byPath}` : ""}`;
|
|
2726
2726
|
};
|
|
@@ -4901,11 +4901,56 @@ var invokeAfterSignalsHooks = async (hooks, params) => {
|
|
|
4901
4901
|
|
|
4902
4902
|
// src/lib/runtimeLineage.ts
|
|
4903
4903
|
var import_node_child_process = require("child_process");
|
|
4904
|
+
var import_node_crypto2 = require("crypto");
|
|
4905
|
+
var import_promises2 = __toESM(require("fs/promises"));
|
|
4906
|
+
var import_node_path2 = __toESM(require("path"));
|
|
4907
|
+
var import_types2 = require("@tradejs/types");
|
|
4908
|
+
var import_strategies3 = require("@tradejs/node/strategies");
|
|
4909
|
+
|
|
4910
|
+
// src/lib/strategyGateFingerprint.ts
|
|
4904
4911
|
var import_node_crypto = require("crypto");
|
|
4905
4912
|
var import_promises = __toESM(require("fs/promises"));
|
|
4906
4913
|
var import_node_path = __toESM(require("path"));
|
|
4907
|
-
var
|
|
4908
|
-
|
|
4914
|
+
var gateFingerprintRelativePaths = (strategyName) => [
|
|
4915
|
+
`packages/strategies/src/${strategyName}/adapters/ai.ts`,
|
|
4916
|
+
`packages/strategies/src/${strategyName}/guardrails.ts`,
|
|
4917
|
+
`packages/strategies/src/${strategyName}/pockets.ts`,
|
|
4918
|
+
`packages/strategies/src/${strategyName}/config.ts`,
|
|
4919
|
+
"packages/strategies/src/shared/localAiGate.ts",
|
|
4920
|
+
"packages/node/src/ai.ts",
|
|
4921
|
+
"packages/core/src/utils/strategyHelpers/signalBuilders.ts"
|
|
4922
|
+
];
|
|
4923
|
+
var resolveStrategyGateFingerprint = async ({
|
|
4924
|
+
projectRoot,
|
|
4925
|
+
strategyName,
|
|
4926
|
+
gitSha
|
|
4927
|
+
}) => {
|
|
4928
|
+
const candidates = await Promise.all(
|
|
4929
|
+
gateFingerprintRelativePaths(strategyName).map(async (relativePath) => {
|
|
4930
|
+
const content = await import_promises.default.readFile(import_node_path.default.join(projectRoot, relativePath)).catch(() => null);
|
|
4931
|
+
return content == null ? null : { relativePath, content };
|
|
4932
|
+
})
|
|
4933
|
+
);
|
|
4934
|
+
const sourceEntries = candidates.filter(
|
|
4935
|
+
(entry) => entry != null
|
|
4936
|
+
);
|
|
4937
|
+
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
4938
|
+
hash.update(strategyName);
|
|
4939
|
+
if (sourceEntries.length) {
|
|
4940
|
+
for (const entry of sourceEntries) {
|
|
4941
|
+
hash.update(entry.relativePath);
|
|
4942
|
+
hash.update(entry.content);
|
|
4943
|
+
}
|
|
4944
|
+
} else {
|
|
4945
|
+
hash.update(gitSha ?? "unknown-git-sha");
|
|
4946
|
+
}
|
|
4947
|
+
return {
|
|
4948
|
+
gateFingerprint: hash.digest("hex").slice(0, 16),
|
|
4949
|
+
gateFingerprintFiles: sourceEntries.map((entry) => entry.relativePath)
|
|
4950
|
+
};
|
|
4951
|
+
};
|
|
4952
|
+
|
|
4953
|
+
// src/lib/runtimeLineage.ts
|
|
4909
4954
|
var RUNTIME_CONTEXT_ENV_KEYS = [
|
|
4910
4955
|
"AI_MODE",
|
|
4911
4956
|
"MIN_AI_QUALITY",
|
|
@@ -4955,6 +5000,17 @@ var normalizeForStableJson = (value) => {
|
|
|
4955
5000
|
}
|
|
4956
5001
|
return value;
|
|
4957
5002
|
};
|
|
5003
|
+
var normalizeRuntimeLogicConfig = (value) => {
|
|
5004
|
+
if (Array.isArray(value)) {
|
|
5005
|
+
return value.map(normalizeRuntimeLogicConfig);
|
|
5006
|
+
}
|
|
5007
|
+
if (value && typeof value === "object") {
|
|
5008
|
+
return Object.fromEntries(
|
|
5009
|
+
Object.entries(value).filter(([key]) => key !== "MAX_LOSS_VALUE").sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalizeRuntimeLogicConfig(entry)])
|
|
5010
|
+
);
|
|
5011
|
+
}
|
|
5012
|
+
return value;
|
|
5013
|
+
};
|
|
4958
5014
|
var resolveRuntimeMaxLossValue = (config) => {
|
|
4959
5015
|
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
4960
5016
|
return null;
|
|
@@ -4968,7 +5024,7 @@ var resolveRuntimeMaxLossValue = (config) => {
|
|
|
4968
5024
|
);
|
|
4969
5025
|
return Number.isFinite(maxLossValue) ? maxLossValue : null;
|
|
4970
5026
|
};
|
|
4971
|
-
var fingerprintRuntimeValue = (value) => (0,
|
|
5027
|
+
var fingerprintRuntimeValue = (value) => (0, import_node_crypto2.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
|
|
4972
5028
|
var gitLineageCache = /* @__PURE__ */ new Map();
|
|
4973
5029
|
var gateFingerprintCache = /* @__PURE__ */ new Map();
|
|
4974
5030
|
var binanceBreadthFingerprintCache = /* @__PURE__ */ new Map();
|
|
@@ -5019,7 +5075,7 @@ var resolveGitLineage = (projectRoot) => {
|
|
|
5019
5075
|
};
|
|
5020
5076
|
var readOptionalFile = async (filePath) => {
|
|
5021
5077
|
try {
|
|
5022
|
-
return await
|
|
5078
|
+
return await import_promises2.default.readFile(filePath);
|
|
5023
5079
|
} catch {
|
|
5024
5080
|
return null;
|
|
5025
5081
|
}
|
|
@@ -5029,7 +5085,7 @@ var resolveBinanceBreadthFingerprint = async (projectRoot) => {
|
|
|
5029
5085
|
return binanceBreadthFingerprintCache.get(projectRoot) ?? null;
|
|
5030
5086
|
}
|
|
5031
5087
|
const content = await readOptionalFile(
|
|
5032
|
-
|
|
5088
|
+
import_node_path2.default.join(
|
|
5033
5089
|
projectRoot,
|
|
5034
5090
|
"packages/node/src/config/binanceBreadthUniverses.json"
|
|
5035
5091
|
)
|
|
@@ -5051,7 +5107,7 @@ var resolveSnapshotFingerprint = async (projectRoot, relativePath) => {
|
|
|
5051
5107
|
if (snapshotFingerprintCache.has(cacheKey)) {
|
|
5052
5108
|
return snapshotFingerprintCache.get(cacheKey) ?? null;
|
|
5053
5109
|
}
|
|
5054
|
-
const content = await readOptionalFile(
|
|
5110
|
+
const content = await readOptionalFile(import_node_path2.default.join(projectRoot, relativePath));
|
|
5055
5111
|
let result = null;
|
|
5056
5112
|
if (content) {
|
|
5057
5113
|
try {
|
|
@@ -5064,55 +5120,26 @@ var resolveSnapshotFingerprint = async (projectRoot, relativePath) => {
|
|
|
5064
5120
|
snapshotFingerprintCache.set(cacheKey, result);
|
|
5065
5121
|
return result;
|
|
5066
5122
|
};
|
|
5067
|
-
var
|
|
5123
|
+
var resolveRuntimeGateFingerprint = async ({
|
|
5068
5124
|
projectRoot,
|
|
5069
5125
|
strategyName,
|
|
5070
5126
|
gitSha
|
|
5071
5127
|
}) => {
|
|
5072
5128
|
const cacheKey = `${projectRoot}:${strategyName}:${gitSha ?? "unknown"}`;
|
|
5073
5129
|
const cached = gateFingerprintCache.get(cacheKey);
|
|
5074
|
-
if (cached)
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
"packages/node/src/ai.ts",
|
|
5083
|
-
"packages/core/src/utils/strategyHelpers/signalBuilders.ts"
|
|
5084
|
-
];
|
|
5085
|
-
const optionalEntries = await Promise.all(
|
|
5086
|
-
relativeCandidates.map(async (relativePath) => {
|
|
5087
|
-
const content = await readOptionalFile(
|
|
5088
|
-
import_node_path.default.join(projectRoot, relativePath)
|
|
5089
|
-
);
|
|
5090
|
-
return content == null ? null : { relativePath, content };
|
|
5091
|
-
})
|
|
5092
|
-
);
|
|
5093
|
-
const sourceEntries = [];
|
|
5094
|
-
for (const entry of optionalEntries) {
|
|
5095
|
-
if (entry != null) {
|
|
5096
|
-
sourceEntries.push(entry);
|
|
5097
|
-
}
|
|
5098
|
-
}
|
|
5099
|
-
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
5100
|
-
hash.update(strategyName);
|
|
5101
|
-
if (sourceEntries.length) {
|
|
5102
|
-
for (const entry of sourceEntries) {
|
|
5103
|
-
hash.update(entry.relativePath);
|
|
5104
|
-
hash.update(entry.content);
|
|
5105
|
-
}
|
|
5106
|
-
} else {
|
|
5107
|
-
hash.update(gitSha ?? "unknown-git-sha");
|
|
5108
|
-
}
|
|
5109
|
-
const fingerprint = hash.digest("hex").slice(0, 16);
|
|
5110
|
-
gateFingerprintCache.set(cacheKey, fingerprint);
|
|
5111
|
-
return fingerprint;
|
|
5130
|
+
if (cached) return cached;
|
|
5131
|
+
const result = await resolveStrategyGateFingerprint({
|
|
5132
|
+
projectRoot,
|
|
5133
|
+
strategyName,
|
|
5134
|
+
gitSha
|
|
5135
|
+
});
|
|
5136
|
+
gateFingerprintCache.set(cacheKey, result);
|
|
5137
|
+
return result;
|
|
5112
5138
|
};
|
|
5113
5139
|
var buildRuntimeLineage = async ({
|
|
5114
5140
|
projectRoot,
|
|
5115
5141
|
strategyName,
|
|
5142
|
+
compositionId,
|
|
5116
5143
|
config,
|
|
5117
5144
|
runContext,
|
|
5118
5145
|
env = process.env
|
|
@@ -5138,19 +5165,25 @@ var buildRuntimeLineage = async ({
|
|
|
5138
5165
|
};
|
|
5139
5166
|
return {
|
|
5140
5167
|
schemaVersion: 1,
|
|
5168
|
+
compositionId: compositionId?.trim() || null,
|
|
5141
5169
|
...git,
|
|
5142
|
-
gateFingerprint: await
|
|
5170
|
+
gateFingerprint: (await resolveRuntimeGateFingerprint({
|
|
5143
5171
|
projectRoot,
|
|
5144
5172
|
strategyName,
|
|
5145
5173
|
gitSha: git.gitSha
|
|
5146
|
-
}),
|
|
5147
|
-
|
|
5174
|
+
})).gateFingerprint,
|
|
5175
|
+
// Position risk is tracked separately so changing trade size does not
|
|
5176
|
+
// masquerade as a change to the core + gate decision logic.
|
|
5177
|
+
configFingerprint: fingerprintRuntimeValue(
|
|
5178
|
+
normalizeRuntimeLogicConfig(config)
|
|
5179
|
+
),
|
|
5148
5180
|
contextFingerprint: fingerprintRuntimeValue(context),
|
|
5149
5181
|
maxLossValue: resolveRuntimeMaxLossValue(config)
|
|
5150
5182
|
};
|
|
5151
5183
|
};
|
|
5152
5184
|
var runtimeLineageKey = (lineage) => [
|
|
5153
5185
|
lineage.schemaVersion,
|
|
5186
|
+
lineage.compositionId ?? "unbound-composition",
|
|
5154
5187
|
lineage.gitSha ?? "unknown",
|
|
5155
5188
|
lineage.gitDirty == null ? "unknown" : lineage.gitDirty ? "dirty" : "clean",
|
|
5156
5189
|
lineage.gateFingerprint,
|
|
@@ -7163,8 +7196,8 @@ var buildReplayExchangeComparisonDetails = ({
|
|
|
7163
7196
|
};
|
|
7164
7197
|
|
|
7165
7198
|
// src/lib/replay/runtimeEvidenceSource.ts
|
|
7166
|
-
var
|
|
7167
|
-
var
|
|
7199
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
7200
|
+
var import_node_path3 = __toESM(require("path"));
|
|
7168
7201
|
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
7169
7202
|
var asFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
7170
7203
|
var unwrapRows = (value, field) => (Array.isArray(value) ? value : []).map((item) => {
|
|
@@ -7225,9 +7258,9 @@ var loadReplayRuntimeEvidenceSource = async ({
|
|
|
7225
7258
|
expectedUserName,
|
|
7226
7259
|
expectedWindow
|
|
7227
7260
|
}) => {
|
|
7228
|
-
const resolvedPath =
|
|
7261
|
+
const resolvedPath = import_node_path3.default.resolve(projectRoot, filePath);
|
|
7229
7262
|
const root = asRecord(
|
|
7230
|
-
JSON.parse(await
|
|
7263
|
+
JSON.parse(await import_promises3.default.readFile(resolvedPath, "utf8"))
|
|
7231
7264
|
);
|
|
7232
7265
|
const runtime = asRecord(root?.runtime) ?? root;
|
|
7233
7266
|
if (!root || !runtime) {
|
|
@@ -8274,8 +8307,8 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8274
8307
|
};
|
|
8275
8308
|
|
|
8276
8309
|
// src/lib/replay/outputReport.ts
|
|
8277
|
-
var
|
|
8278
|
-
var
|
|
8310
|
+
var import_promises4 = __toESM(require("fs/promises"));
|
|
8311
|
+
var import_node_path4 = __toESM(require("path"));
|
|
8279
8312
|
var import_data = require("@tradejs/core/data");
|
|
8280
8313
|
var formatMsk = (timestamp) => typeof timestamp === "number" && Number.isFinite(timestamp) ? new Intl.DateTimeFormat("ru-RU", {
|
|
8281
8314
|
timeZone: "Europe/Moscow",
|
|
@@ -8579,8 +8612,8 @@ var writeReplayOutputReport = async ({
|
|
|
8579
8612
|
strategySnapshot,
|
|
8580
8613
|
runtimeComparison
|
|
8581
8614
|
}) => {
|
|
8582
|
-
const outputDir =
|
|
8583
|
-
await
|
|
8615
|
+
const outputDir = import_node_path4.default.resolve(projectRoot, "data/replay/output");
|
|
8616
|
+
await import_promises4.default.mkdir(outputDir, { recursive: true });
|
|
8584
8617
|
const perTradeAnalysis = buildPerTradeAnalysis(runtimeComparison);
|
|
8585
8618
|
const report = {
|
|
8586
8619
|
reportType: "replay-output",
|
|
@@ -8615,8 +8648,8 @@ var writeReplayOutputReport = async ({
|
|
|
8615
8648
|
runtimeComparison,
|
|
8616
8649
|
perTradeAnalysis
|
|
8617
8650
|
};
|
|
8618
|
-
const markdownPath =
|
|
8619
|
-
const jsonPath =
|
|
8651
|
+
const markdownPath = import_node_path4.default.join(outputDir, `${timestamp}-replay-report.md`);
|
|
8652
|
+
const jsonPath = import_node_path4.default.join(outputDir, `${timestamp}-replay-report.json`);
|
|
8620
8653
|
const lines = [
|
|
8621
8654
|
"# Replay Report",
|
|
8622
8655
|
"",
|
|
@@ -8682,8 +8715,8 @@ var writeReplayOutputReport = async ({
|
|
|
8682
8715
|
""
|
|
8683
8716
|
];
|
|
8684
8717
|
await Promise.all([
|
|
8685
|
-
|
|
8686
|
-
|
|
8718
|
+
import_promises4.default.writeFile(markdownPath, lines.join("\n"), "utf8"),
|
|
8719
|
+
import_promises4.default.writeFile(jsonPath, `${(0, import_data.toJson)(report, true)}
|
|
8687
8720
|
`, "utf8")
|
|
8688
8721
|
]);
|
|
8689
8722
|
return {
|
|
@@ -2703,27 +2703,27 @@ var createCoinMarketCapFetchStats = () => ({
|
|
|
2703
2703
|
totalCredits: 0,
|
|
2704
2704
|
byPath: /* @__PURE__ */ new Map()
|
|
2705
2705
|
});
|
|
2706
|
-
var getCoinMarketCapPathLabel = (
|
|
2707
|
-
if (
|
|
2708
|
-
if (
|
|
2709
|
-
if (
|
|
2710
|
-
if (
|
|
2711
|
-
if (
|
|
2712
|
-
if (
|
|
2713
|
-
return
|
|
2714
|
-
};
|
|
2715
|
-
var recordCoinMarketCapFetchStats = (stats,
|
|
2706
|
+
var getCoinMarketCapPathLabel = (path5) => {
|
|
2707
|
+
if (path5.includes("global-metrics")) return "global";
|
|
2708
|
+
if (path5.includes("cryptocurrency/quotes")) return "reference";
|
|
2709
|
+
if (path5.includes("exchange/quotes")) return "exchangeLiquidity";
|
|
2710
|
+
if (path5.includes("fear-and-greed")) return "fearGreed";
|
|
2711
|
+
if (path5.includes("cmc100")) return "cmc100";
|
|
2712
|
+
if (path5.includes("cmc20")) return "cmc20";
|
|
2713
|
+
return path5;
|
|
2714
|
+
};
|
|
2715
|
+
var recordCoinMarketCapFetchStats = (stats, path5, creditCount) => {
|
|
2716
2716
|
if (!stats) return;
|
|
2717
|
-
const existing = stats.byPath.get(
|
|
2717
|
+
const existing = stats.byPath.get(path5) ?? { requests: 0, credits: 0 };
|
|
2718
2718
|
existing.requests += 1;
|
|
2719
2719
|
existing.credits += creditCount ?? 0;
|
|
2720
2720
|
stats.requests += 1;
|
|
2721
2721
|
stats.totalCredits += creditCount ?? 0;
|
|
2722
|
-
stats.byPath.set(
|
|
2722
|
+
stats.byPath.set(path5, existing);
|
|
2723
2723
|
};
|
|
2724
2724
|
var formatCoinMarketCapFetchStats = (stats) => {
|
|
2725
2725
|
const byPath = [...stats.byPath.entries()].map(
|
|
2726
|
-
([
|
|
2726
|
+
([path5, item]) => `${getCoinMarketCapPathLabel(path5)}=${item.requests}req/${item.credits}cr`
|
|
2727
2727
|
).join(" ");
|
|
2728
2728
|
return `apiRequests=${stats.requests} apiCredits=${stats.totalCredits}${byPath ? ` ${byPath}` : ""}`;
|
|
2729
2729
|
};
|
|
@@ -4904,11 +4904,56 @@ var invokeAfterSignalsHooks = async (hooks, params) => {
|
|
|
4904
4904
|
|
|
4905
4905
|
// src/lib/runtimeLineage.ts
|
|
4906
4906
|
var import_node_child_process = require("child_process");
|
|
4907
|
+
var import_node_crypto2 = require("crypto");
|
|
4908
|
+
var import_promises2 = __toESM(require("fs/promises"));
|
|
4909
|
+
var import_node_path2 = __toESM(require("path"));
|
|
4910
|
+
var import_types2 = require("@tradejs/types");
|
|
4911
|
+
var import_strategies3 = require("@tradejs/node/strategies");
|
|
4912
|
+
|
|
4913
|
+
// src/lib/strategyGateFingerprint.ts
|
|
4907
4914
|
var import_node_crypto = require("crypto");
|
|
4908
4915
|
var import_promises = __toESM(require("fs/promises"));
|
|
4909
4916
|
var import_node_path = __toESM(require("path"));
|
|
4910
|
-
var
|
|
4911
|
-
|
|
4917
|
+
var gateFingerprintRelativePaths = (strategyName) => [
|
|
4918
|
+
`packages/strategies/src/${strategyName}/adapters/ai.ts`,
|
|
4919
|
+
`packages/strategies/src/${strategyName}/guardrails.ts`,
|
|
4920
|
+
`packages/strategies/src/${strategyName}/pockets.ts`,
|
|
4921
|
+
`packages/strategies/src/${strategyName}/config.ts`,
|
|
4922
|
+
"packages/strategies/src/shared/localAiGate.ts",
|
|
4923
|
+
"packages/node/src/ai.ts",
|
|
4924
|
+
"packages/core/src/utils/strategyHelpers/signalBuilders.ts"
|
|
4925
|
+
];
|
|
4926
|
+
var resolveStrategyGateFingerprint = async ({
|
|
4927
|
+
projectRoot,
|
|
4928
|
+
strategyName,
|
|
4929
|
+
gitSha
|
|
4930
|
+
}) => {
|
|
4931
|
+
const candidates = await Promise.all(
|
|
4932
|
+
gateFingerprintRelativePaths(strategyName).map(async (relativePath) => {
|
|
4933
|
+
const content = await import_promises.default.readFile(import_node_path.default.join(projectRoot, relativePath)).catch(() => null);
|
|
4934
|
+
return content == null ? null : { relativePath, content };
|
|
4935
|
+
})
|
|
4936
|
+
);
|
|
4937
|
+
const sourceEntries = candidates.filter(
|
|
4938
|
+
(entry) => entry != null
|
|
4939
|
+
);
|
|
4940
|
+
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
4941
|
+
hash.update(strategyName);
|
|
4942
|
+
if (sourceEntries.length) {
|
|
4943
|
+
for (const entry of sourceEntries) {
|
|
4944
|
+
hash.update(entry.relativePath);
|
|
4945
|
+
hash.update(entry.content);
|
|
4946
|
+
}
|
|
4947
|
+
} else {
|
|
4948
|
+
hash.update(gitSha ?? "unknown-git-sha");
|
|
4949
|
+
}
|
|
4950
|
+
return {
|
|
4951
|
+
gateFingerprint: hash.digest("hex").slice(0, 16),
|
|
4952
|
+
gateFingerprintFiles: sourceEntries.map((entry) => entry.relativePath)
|
|
4953
|
+
};
|
|
4954
|
+
};
|
|
4955
|
+
|
|
4956
|
+
// src/lib/runtimeLineage.ts
|
|
4912
4957
|
var RUNTIME_CONTEXT_ENV_KEYS = [
|
|
4913
4958
|
"AI_MODE",
|
|
4914
4959
|
"MIN_AI_QUALITY",
|
|
@@ -4958,6 +5003,17 @@ var normalizeForStableJson = (value) => {
|
|
|
4958
5003
|
}
|
|
4959
5004
|
return value;
|
|
4960
5005
|
};
|
|
5006
|
+
var normalizeRuntimeLogicConfig = (value) => {
|
|
5007
|
+
if (Array.isArray(value)) {
|
|
5008
|
+
return value.map(normalizeRuntimeLogicConfig);
|
|
5009
|
+
}
|
|
5010
|
+
if (value && typeof value === "object") {
|
|
5011
|
+
return Object.fromEntries(
|
|
5012
|
+
Object.entries(value).filter(([key]) => key !== "MAX_LOSS_VALUE").sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalizeRuntimeLogicConfig(entry)])
|
|
5013
|
+
);
|
|
5014
|
+
}
|
|
5015
|
+
return value;
|
|
5016
|
+
};
|
|
4961
5017
|
var resolveRuntimeMaxLossValue = (config) => {
|
|
4962
5018
|
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
4963
5019
|
return null;
|
|
@@ -4971,7 +5027,7 @@ var resolveRuntimeMaxLossValue = (config) => {
|
|
|
4971
5027
|
);
|
|
4972
5028
|
return Number.isFinite(maxLossValue) ? maxLossValue : null;
|
|
4973
5029
|
};
|
|
4974
|
-
var fingerprintRuntimeValue = (value) => (0,
|
|
5030
|
+
var fingerprintRuntimeValue = (value) => (0, import_node_crypto2.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
|
|
4975
5031
|
var gitLineageCache = /* @__PURE__ */ new Map();
|
|
4976
5032
|
var gateFingerprintCache = /* @__PURE__ */ new Map();
|
|
4977
5033
|
var binanceBreadthFingerprintCache = /* @__PURE__ */ new Map();
|
|
@@ -5022,7 +5078,7 @@ var resolveGitLineage = (projectRoot) => {
|
|
|
5022
5078
|
};
|
|
5023
5079
|
var readOptionalFile = async (filePath) => {
|
|
5024
5080
|
try {
|
|
5025
|
-
return await
|
|
5081
|
+
return await import_promises2.default.readFile(filePath);
|
|
5026
5082
|
} catch {
|
|
5027
5083
|
return null;
|
|
5028
5084
|
}
|
|
@@ -5032,7 +5088,7 @@ var resolveBinanceBreadthFingerprint = async (projectRoot) => {
|
|
|
5032
5088
|
return binanceBreadthFingerprintCache.get(projectRoot) ?? null;
|
|
5033
5089
|
}
|
|
5034
5090
|
const content = await readOptionalFile(
|
|
5035
|
-
|
|
5091
|
+
import_node_path2.default.join(
|
|
5036
5092
|
projectRoot,
|
|
5037
5093
|
"packages/node/src/config/binanceBreadthUniverses.json"
|
|
5038
5094
|
)
|
|
@@ -5054,7 +5110,7 @@ var resolveSnapshotFingerprint = async (projectRoot, relativePath) => {
|
|
|
5054
5110
|
if (snapshotFingerprintCache.has(cacheKey)) {
|
|
5055
5111
|
return snapshotFingerprintCache.get(cacheKey) ?? null;
|
|
5056
5112
|
}
|
|
5057
|
-
const content = await readOptionalFile(
|
|
5113
|
+
const content = await readOptionalFile(import_node_path2.default.join(projectRoot, relativePath));
|
|
5058
5114
|
let result = null;
|
|
5059
5115
|
if (content) {
|
|
5060
5116
|
try {
|
|
@@ -5067,55 +5123,26 @@ var resolveSnapshotFingerprint = async (projectRoot, relativePath) => {
|
|
|
5067
5123
|
snapshotFingerprintCache.set(cacheKey, result);
|
|
5068
5124
|
return result;
|
|
5069
5125
|
};
|
|
5070
|
-
var
|
|
5126
|
+
var resolveRuntimeGateFingerprint = async ({
|
|
5071
5127
|
projectRoot,
|
|
5072
5128
|
strategyName,
|
|
5073
5129
|
gitSha
|
|
5074
5130
|
}) => {
|
|
5075
5131
|
const cacheKey = `${projectRoot}:${strategyName}:${gitSha ?? "unknown"}`;
|
|
5076
5132
|
const cached = gateFingerprintCache.get(cacheKey);
|
|
5077
|
-
if (cached)
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
"packages/node/src/ai.ts",
|
|
5086
|
-
"packages/core/src/utils/strategyHelpers/signalBuilders.ts"
|
|
5087
|
-
];
|
|
5088
|
-
const optionalEntries = await Promise.all(
|
|
5089
|
-
relativeCandidates.map(async (relativePath) => {
|
|
5090
|
-
const content = await readOptionalFile(
|
|
5091
|
-
import_node_path.default.join(projectRoot, relativePath)
|
|
5092
|
-
);
|
|
5093
|
-
return content == null ? null : { relativePath, content };
|
|
5094
|
-
})
|
|
5095
|
-
);
|
|
5096
|
-
const sourceEntries = [];
|
|
5097
|
-
for (const entry of optionalEntries) {
|
|
5098
|
-
if (entry != null) {
|
|
5099
|
-
sourceEntries.push(entry);
|
|
5100
|
-
}
|
|
5101
|
-
}
|
|
5102
|
-
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
5103
|
-
hash.update(strategyName);
|
|
5104
|
-
if (sourceEntries.length) {
|
|
5105
|
-
for (const entry of sourceEntries) {
|
|
5106
|
-
hash.update(entry.relativePath);
|
|
5107
|
-
hash.update(entry.content);
|
|
5108
|
-
}
|
|
5109
|
-
} else {
|
|
5110
|
-
hash.update(gitSha ?? "unknown-git-sha");
|
|
5111
|
-
}
|
|
5112
|
-
const fingerprint = hash.digest("hex").slice(0, 16);
|
|
5113
|
-
gateFingerprintCache.set(cacheKey, fingerprint);
|
|
5114
|
-
return fingerprint;
|
|
5133
|
+
if (cached) return cached;
|
|
5134
|
+
const result = await resolveStrategyGateFingerprint({
|
|
5135
|
+
projectRoot,
|
|
5136
|
+
strategyName,
|
|
5137
|
+
gitSha
|
|
5138
|
+
});
|
|
5139
|
+
gateFingerprintCache.set(cacheKey, result);
|
|
5140
|
+
return result;
|
|
5115
5141
|
};
|
|
5116
5142
|
var buildRuntimeLineage = async ({
|
|
5117
5143
|
projectRoot,
|
|
5118
5144
|
strategyName,
|
|
5145
|
+
compositionId,
|
|
5119
5146
|
config,
|
|
5120
5147
|
runContext,
|
|
5121
5148
|
env = process.env
|
|
@@ -5141,19 +5168,25 @@ var buildRuntimeLineage = async ({
|
|
|
5141
5168
|
};
|
|
5142
5169
|
return {
|
|
5143
5170
|
schemaVersion: 1,
|
|
5171
|
+
compositionId: compositionId?.trim() || null,
|
|
5144
5172
|
...git,
|
|
5145
|
-
gateFingerprint: await
|
|
5173
|
+
gateFingerprint: (await resolveRuntimeGateFingerprint({
|
|
5146
5174
|
projectRoot,
|
|
5147
5175
|
strategyName,
|
|
5148
5176
|
gitSha: git.gitSha
|
|
5149
|
-
}),
|
|
5150
|
-
|
|
5177
|
+
})).gateFingerprint,
|
|
5178
|
+
// Position risk is tracked separately so changing trade size does not
|
|
5179
|
+
// masquerade as a change to the core + gate decision logic.
|
|
5180
|
+
configFingerprint: fingerprintRuntimeValue(
|
|
5181
|
+
normalizeRuntimeLogicConfig(config)
|
|
5182
|
+
),
|
|
5151
5183
|
contextFingerprint: fingerprintRuntimeValue(context),
|
|
5152
5184
|
maxLossValue: resolveRuntimeMaxLossValue(config)
|
|
5153
5185
|
};
|
|
5154
5186
|
};
|
|
5155
5187
|
var runtimeLineageKey = (lineage) => [
|
|
5156
5188
|
lineage.schemaVersion,
|
|
5189
|
+
lineage.compositionId ?? "unbound-composition",
|
|
5157
5190
|
lineage.gitSha ?? "unknown",
|
|
5158
5191
|
lineage.gitDirty == null ? "unknown" : lineage.gitDirty ? "dirty" : "clean",
|
|
5159
5192
|
lineage.gateFingerprint,
|
|
@@ -7166,8 +7199,8 @@ var buildReplayExchangeComparisonDetails = ({
|
|
|
7166
7199
|
};
|
|
7167
7200
|
|
|
7168
7201
|
// src/lib/replay/runtimeEvidenceSource.ts
|
|
7169
|
-
var
|
|
7170
|
-
var
|
|
7202
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
7203
|
+
var import_node_path3 = __toESM(require("path"));
|
|
7171
7204
|
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
7172
7205
|
var asFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
7173
7206
|
var unwrapRows = (value, field) => (Array.isArray(value) ? value : []).map((item) => {
|
|
@@ -7228,9 +7261,9 @@ var loadReplayRuntimeEvidenceSource = async ({
|
|
|
7228
7261
|
expectedUserName,
|
|
7229
7262
|
expectedWindow
|
|
7230
7263
|
}) => {
|
|
7231
|
-
const resolvedPath =
|
|
7264
|
+
const resolvedPath = import_node_path3.default.resolve(projectRoot, filePath);
|
|
7232
7265
|
const root = asRecord(
|
|
7233
|
-
JSON.parse(await
|
|
7266
|
+
JSON.parse(await import_promises3.default.readFile(resolvedPath, "utf8"))
|
|
7234
7267
|
);
|
|
7235
7268
|
const runtime = asRecord(root?.runtime) ?? root;
|
|
7236
7269
|
if (!root || !runtime) {
|
|
@@ -8277,8 +8310,8 @@ var saveAndPrintReplayRuntimeComparison = async ({
|
|
|
8277
8310
|
};
|
|
8278
8311
|
|
|
8279
8312
|
// src/lib/replay/outputReport.ts
|
|
8280
|
-
var
|
|
8281
|
-
var
|
|
8313
|
+
var import_promises4 = __toESM(require("fs/promises"));
|
|
8314
|
+
var import_node_path4 = __toESM(require("path"));
|
|
8282
8315
|
var import_data = require("@tradejs/core/data");
|
|
8283
8316
|
var formatMsk = (timestamp) => typeof timestamp === "number" && Number.isFinite(timestamp) ? new Intl.DateTimeFormat("ru-RU", {
|
|
8284
8317
|
timeZone: "Europe/Moscow",
|
|
@@ -8582,8 +8615,8 @@ var writeReplayOutputReport = async ({
|
|
|
8582
8615
|
strategySnapshot,
|
|
8583
8616
|
runtimeComparison
|
|
8584
8617
|
}) => {
|
|
8585
|
-
const outputDir =
|
|
8586
|
-
await
|
|
8618
|
+
const outputDir = import_node_path4.default.resolve(projectRoot, "data/replay/output");
|
|
8619
|
+
await import_promises4.default.mkdir(outputDir, { recursive: true });
|
|
8587
8620
|
const perTradeAnalysis = buildPerTradeAnalysis(runtimeComparison);
|
|
8588
8621
|
const report = {
|
|
8589
8622
|
reportType: "replay-output",
|
|
@@ -8618,8 +8651,8 @@ var writeReplayOutputReport = async ({
|
|
|
8618
8651
|
runtimeComparison,
|
|
8619
8652
|
perTradeAnalysis
|
|
8620
8653
|
};
|
|
8621
|
-
const markdownPath =
|
|
8622
|
-
const jsonPath =
|
|
8654
|
+
const markdownPath = import_node_path4.default.join(outputDir, `${timestamp}-replay-report.md`);
|
|
8655
|
+
const jsonPath = import_node_path4.default.join(outputDir, `${timestamp}-replay-report.json`);
|
|
8623
8656
|
const lines = [
|
|
8624
8657
|
"# Replay Report",
|
|
8625
8658
|
"",
|
|
@@ -8685,8 +8718,8 @@ var writeReplayOutputReport = async ({
|
|
|
8685
8718
|
""
|
|
8686
8719
|
];
|
|
8687
8720
|
await Promise.all([
|
|
8688
|
-
|
|
8689
|
-
|
|
8721
|
+
import_promises4.default.writeFile(markdownPath, lines.join("\n"), "utf8"),
|
|
8722
|
+
import_promises4.default.writeFile(jsonPath, `${(0, import_data.toJson)(report, true)}
|
|
8690
8723
|
`, "utf8")
|
|
8691
8724
|
]);
|
|
8692
8725
|
return {
|