@tradejs/cli 3.1.29-beta.262 → 3.1.29-beta.264
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 +864 -1424
- package/dist/lib/runtimeEvidenceArtifacts.js +3 -416
- package/dist/lib/runtimeEvidenceCompositionSnapshots.js +42 -211
- package/dist/lib/runtimeEvidenceCompositions.js +33 -310
- package/dist/lib/runtimeEvidenceDeployment.js +3 -315
- package/dist/lib/runtimeEvidenceProducer.js +3 -77
- package/dist/lib/runtimeEvidenceSync.js +32 -299
- package/dist/lib/runtimeFeedbackArtifacts.js +3 -545
- package/dist/lib/runtimeFeedbackNotification.js +7 -195
- package/dist/lib/runtimeFeedbackReplay.js +26 -525
- package/dist/lib/runtimeFeedbackSync.js +26 -219
- package/dist/lib/runtimeScorecard.js +5 -163
- package/dist/scripts/backtest.js +6 -1
- package/dist/scripts/mcpWorker.js +273 -0
- package/dist/scripts/replay.js +14 -250
- package/dist/scripts/replayRunner.js +14 -250
- package/dist/scripts/runtimeEvidence.js +47 -597
- package/dist/scripts/runtimeEvidenceSync.js +29 -296
- package/dist/scripts/runtimeFeedbackNotify.js +10 -188
- package/dist/scripts/runtimeFeedbackReplay.js +29 -528
- package/dist/scripts/runtimeFeedbackSync.js +28 -221
- package/dist/scripts/runtimeScorecard.js +88 -503
- package/dist/scripts/signals.js +55 -224
- package/dist/scripts/signalsDaemon.js +55 -224
- package/package.json +7 -7
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/scripts/mcpWorker.ts
|
|
21
|
+
var mcpWorker_exports = {};
|
|
22
|
+
__export(mcpWorker_exports, {
|
|
23
|
+
main: () => main
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(mcpWorker_exports);
|
|
26
|
+
var import_node_child_process = require("child_process");
|
|
27
|
+
var import_node_crypto = require("crypto");
|
|
28
|
+
var import_promises = require("fs/promises");
|
|
29
|
+
var import_redis = require("@tradejs/infra/redis");
|
|
30
|
+
var import_mcp = require("@tradejs/node/mcp");
|
|
31
|
+
var pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
32
|
+
var leaseKey = "worker:lease";
|
|
33
|
+
var LEASE_SECONDS = 15;
|
|
34
|
+
var JOB_TIMEOUT_MS = 60 * 60 * 1e3;
|
|
35
|
+
var main = async (options = {}) => {
|
|
36
|
+
const owner = (0, import_node_crypto.randomUUID)();
|
|
37
|
+
if (!await import_redis.mcpStorage.put(leaseKey, owner, LEASE_SECONDS, true))
|
|
38
|
+
throw new Error("Another MCP worker owns this storage");
|
|
39
|
+
let stopping = false;
|
|
40
|
+
let child;
|
|
41
|
+
let renewing = false;
|
|
42
|
+
let leaseLost = false;
|
|
43
|
+
let manifestHash = "";
|
|
44
|
+
const signalChild = (signal) => {
|
|
45
|
+
if (!child?.pid) return;
|
|
46
|
+
try {
|
|
47
|
+
process.kill(
|
|
48
|
+
process.platform === "win32" ? child.pid : -child.pid,
|
|
49
|
+
signal
|
|
50
|
+
);
|
|
51
|
+
} catch {
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const heartbeatValue = () => ({
|
|
55
|
+
observedAt: Date.now(),
|
|
56
|
+
packageManifestSha256: manifestHash,
|
|
57
|
+
replayAvailable: Boolean(process.env.MCP_REPLAY_REDIS_HOST)
|
|
58
|
+
});
|
|
59
|
+
const stop = () => {
|
|
60
|
+
stopping = true;
|
|
61
|
+
signalChild("SIGTERM");
|
|
62
|
+
};
|
|
63
|
+
options.signal?.addEventListener("abort", stop, { once: true });
|
|
64
|
+
if (options.signal?.aborted) stop();
|
|
65
|
+
process.once("SIGINT", stop);
|
|
66
|
+
process.once("SIGTERM", stop);
|
|
67
|
+
const heartbeat = setInterval(async () => {
|
|
68
|
+
if (renewing || leaseLost) return;
|
|
69
|
+
renewing = true;
|
|
70
|
+
try {
|
|
71
|
+
if (!await import_redis.mcpStorage.compare(leaseKey, owner, owner, LEASE_SECONDS))
|
|
72
|
+
throw new Error("Worker lease lost");
|
|
73
|
+
await import_redis.mcpStorage.put("worker:heartbeat", heartbeatValue(), LEASE_SECONDS);
|
|
74
|
+
} catch {
|
|
75
|
+
leaseLost = true;
|
|
76
|
+
stop();
|
|
77
|
+
} finally {
|
|
78
|
+
renewing = false;
|
|
79
|
+
}
|
|
80
|
+
}, 2e3);
|
|
81
|
+
try {
|
|
82
|
+
manifestHash = await (0, import_mcp.mcpPackageManifestHash)();
|
|
83
|
+
await import_redis.mcpStorage.put("worker:heartbeat", heartbeatValue(), LEASE_SECONDS);
|
|
84
|
+
let cursor = "0";
|
|
85
|
+
do {
|
|
86
|
+
const page = await import_redis.mcpStorage.scan("jobs:", cursor);
|
|
87
|
+
cursor = page.cursor;
|
|
88
|
+
for (const key of page.ids) {
|
|
89
|
+
const job = await import_redis.mcpStorage.get(key);
|
|
90
|
+
if (job?.status === "running")
|
|
91
|
+
await import_redis.mcpStorage.compare(
|
|
92
|
+
key,
|
|
93
|
+
job,
|
|
94
|
+
{
|
|
95
|
+
...job,
|
|
96
|
+
status: "failed",
|
|
97
|
+
error: "Worker restarted; this job was not replayed",
|
|
98
|
+
finishedAt: Date.now(),
|
|
99
|
+
updatedAt: Date.now()
|
|
100
|
+
},
|
|
101
|
+
import_mcp.MCP_JOB_TTL
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
} while (cursor !== "0" && !stopping);
|
|
105
|
+
console.log(
|
|
106
|
+
"MCP worker ready (one job at a time, cached history, no live order commands)"
|
|
107
|
+
);
|
|
108
|
+
while (!stopping) {
|
|
109
|
+
let queued;
|
|
110
|
+
cursor = "0";
|
|
111
|
+
do {
|
|
112
|
+
const page = await import_redis.mcpStorage.scan("jobs:", cursor);
|
|
113
|
+
cursor = page.cursor;
|
|
114
|
+
for (const key of page.ids) {
|
|
115
|
+
const job = await import_redis.mcpStorage.get(key);
|
|
116
|
+
if (job?.status === "queued" && (!queued || job.createdAt < queued.createdAt))
|
|
117
|
+
queued = job;
|
|
118
|
+
}
|
|
119
|
+
} while (cursor !== "0" && !stopping);
|
|
120
|
+
if (!queued) {
|
|
121
|
+
await pause(1e3);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const jobKey = (0, import_mcp.mcpJobStorageId)(queued.userName, queued.id);
|
|
125
|
+
const started = {
|
|
126
|
+
...queued,
|
|
127
|
+
status: "running",
|
|
128
|
+
startedAt: Date.now(),
|
|
129
|
+
updatedAt: Date.now()
|
|
130
|
+
};
|
|
131
|
+
if (!await import_redis.mcpStorage.compare(jobKey, queued, started, import_mcp.MCP_JOB_TTL))
|
|
132
|
+
continue;
|
|
133
|
+
if (started.packageManifestSha256 !== manifestHash) {
|
|
134
|
+
await import_redis.mcpStorage.compare(
|
|
135
|
+
jobKey,
|
|
136
|
+
started,
|
|
137
|
+
{
|
|
138
|
+
...started,
|
|
139
|
+
status: "failed",
|
|
140
|
+
error: "Package composition changed since submission",
|
|
141
|
+
finishedAt: Date.now()
|
|
142
|
+
},
|
|
143
|
+
import_mcp.MCP_JOB_TTL
|
|
144
|
+
);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
await (0, import_promises.mkdir)((0, import_mcp.mcpJobDirectory)(started), { recursive: true });
|
|
148
|
+
if (started.kind === "backtest")
|
|
149
|
+
await (0, import_redis.setDataStrict)(
|
|
150
|
+
import_redis.redisKeys.backtestConfig(
|
|
151
|
+
started.userName,
|
|
152
|
+
String(started.request.frozenConfigId)
|
|
153
|
+
),
|
|
154
|
+
started.request.resolvedConfig,
|
|
155
|
+
{ expire: import_mcp.MCP_JOB_TTL }
|
|
156
|
+
);
|
|
157
|
+
const jobEnv = {
|
|
158
|
+
...process.env,
|
|
159
|
+
NODE_OPTIONS: "--max-old-space-size=1024",
|
|
160
|
+
MCP_WORKER: "true",
|
|
161
|
+
BACKTEST_WORKER_HEAP_MB: "768",
|
|
162
|
+
BACKTEST_MAX_PARALLEL: "1",
|
|
163
|
+
AI_ENABLED: "false",
|
|
164
|
+
ML_ENABLED: "false",
|
|
165
|
+
MAKE_ORDERS: "false",
|
|
166
|
+
TRADEJS_EXTERNAL_ORDER_PLACEMENT: "false"
|
|
167
|
+
};
|
|
168
|
+
if (started.kind === "runtime-feedback-replay") {
|
|
169
|
+
for (const name of Object.keys(jobEnv)) {
|
|
170
|
+
if (/secret|token|api.?key|private.?key|cookie|credential/i.test(name))
|
|
171
|
+
delete jobEnv[name];
|
|
172
|
+
}
|
|
173
|
+
Object.assign(jobEnv, {
|
|
174
|
+
DOTENV_CONFIG_PATH: "/dev/null",
|
|
175
|
+
REDIS_HOST: process.env.MCP_REPLAY_REDIS_HOST,
|
|
176
|
+
REDIS_PORT: "6379",
|
|
177
|
+
REDIS_DB: "0",
|
|
178
|
+
RUNTIME_FEEDBACK_ISOLATED_REDIS: "true",
|
|
179
|
+
PGOPTIONS: "-c default_transaction_read_only=on",
|
|
180
|
+
TRADEJS_TIMESCALE_READ_ONLY: "true"
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
child = (0, import_node_child_process.spawn)(process.execPath, [process.argv[1], ...started.args], {
|
|
184
|
+
cwd: process.env.PROJECT_CWD || process.cwd(),
|
|
185
|
+
env: jobEnv,
|
|
186
|
+
detached: process.platform !== "win32",
|
|
187
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
188
|
+
});
|
|
189
|
+
const currentChild = child;
|
|
190
|
+
let timedOut = false;
|
|
191
|
+
const timeout = setTimeout(() => {
|
|
192
|
+
timedOut = true;
|
|
193
|
+
signalChild("SIGTERM");
|
|
194
|
+
}, JOB_TIMEOUT_MS);
|
|
195
|
+
const logs = [];
|
|
196
|
+
const log = (chunk) => {
|
|
197
|
+
const lines = chunk.toString().replace(/\u001b\[[0-9;]*m/g, "").split(/[\r\n]+/).filter(Boolean);
|
|
198
|
+
logs.push(
|
|
199
|
+
...lines.map(
|
|
200
|
+
(line) => line.slice(0, 1e3).replace(
|
|
201
|
+
/((?:secret|password|api.?key|token|authorization)\s*[:=]\s*)\S+/gi,
|
|
202
|
+
"$1[redacted]"
|
|
203
|
+
)
|
|
204
|
+
)
|
|
205
|
+
);
|
|
206
|
+
if (logs.length > 150) logs.splice(0, logs.length - 150);
|
|
207
|
+
};
|
|
208
|
+
currentChild.stdout?.on("data", log);
|
|
209
|
+
currentChild.stderr?.on("data", log);
|
|
210
|
+
let killAt;
|
|
211
|
+
const monitor = setInterval(async () => {
|
|
212
|
+
try {
|
|
213
|
+
const latest = await import_redis.mcpStorage.get(jobKey);
|
|
214
|
+
if (!latest || latest.cancelRequested || stopping || timedOut) {
|
|
215
|
+
killAt ??= Date.now();
|
|
216
|
+
signalChild(Date.now() - killAt > 8e3 ? "SIGKILL" : "SIGTERM");
|
|
217
|
+
}
|
|
218
|
+
if (latest && !leaseLost)
|
|
219
|
+
await import_redis.mcpStorage.compare(
|
|
220
|
+
jobKey,
|
|
221
|
+
latest,
|
|
222
|
+
{ ...latest, logs: [...logs], updatedAt: Date.now() },
|
|
223
|
+
import_mcp.MCP_JOB_TTL
|
|
224
|
+
);
|
|
225
|
+
} catch {
|
|
226
|
+
leaseLost = true;
|
|
227
|
+
stop();
|
|
228
|
+
}
|
|
229
|
+
}, 1e3);
|
|
230
|
+
const outcome = await new Promise((resolve) => {
|
|
231
|
+
currentChild.once(
|
|
232
|
+
"error",
|
|
233
|
+
() => resolve({ code: null, error: "Unable to start research process" })
|
|
234
|
+
);
|
|
235
|
+
currentChild.once("exit", (code) => resolve({ code }));
|
|
236
|
+
});
|
|
237
|
+
clearTimeout(timeout);
|
|
238
|
+
clearInterval(monitor);
|
|
239
|
+
child = void 0;
|
|
240
|
+
if (!leaseLost) {
|
|
241
|
+
for (let attempt = 0; attempt < 4; attempt++) {
|
|
242
|
+
const latest = await import_redis.mcpStorage.get(jobKey);
|
|
243
|
+
if (!latest) break;
|
|
244
|
+
const cancelled = latest.cancelRequested;
|
|
245
|
+
const next = {
|
|
246
|
+
...latest,
|
|
247
|
+
logs,
|
|
248
|
+
status: cancelled ? "cancelled" : outcome.code === 0 && !timedOut && !stopping ? "completed" : "failed",
|
|
249
|
+
exitCode: outcome.code,
|
|
250
|
+
updatedAt: Date.now(),
|
|
251
|
+
finishedAt: Date.now(),
|
|
252
|
+
...cancelled ? {} : timedOut ? { error: "Job exceeded one hour" } : stopping ? { error: "Worker stopped; this job was not replayed" } : outcome.code !== 0 ? { error: outcome.error || "Research process failed" } : {}
|
|
253
|
+
};
|
|
254
|
+
if (await import_redis.mcpStorage.compare(jobKey, latest, next, import_mcp.MCP_JOB_TTL))
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
} finally {
|
|
260
|
+
clearInterval(heartbeat);
|
|
261
|
+
options.signal?.removeEventListener("abort", stop);
|
|
262
|
+
process.removeListener("SIGINT", stop);
|
|
263
|
+
process.removeListener("SIGTERM", stop);
|
|
264
|
+
if (await import_redis.mcpStorage.release(leaseKey, owner))
|
|
265
|
+
await import_redis.mcpStorage.remove("worker:heartbeat");
|
|
266
|
+
}
|
|
267
|
+
if (leaseLost)
|
|
268
|
+
throw new Error("Worker lease lost; active computation was stopped");
|
|
269
|
+
};
|
|
270
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
271
|
+
0 && (module.exports = {
|
|
272
|
+
main
|
|
273
|
+
});
|
package/dist/scripts/replay.js
CHANGED
|
@@ -17,6 +17,7 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
17
|
}
|
|
18
18
|
return to;
|
|
19
19
|
};
|
|
20
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
20
21
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
22
|
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
23
|
// file that has been converted to a CommonJS file using a Babel-
|
|
@@ -41,7 +42,7 @@ var import_data4 = require("@tradejs/core/data");
|
|
|
41
42
|
var import_time7 = require("@tradejs/core/time");
|
|
42
43
|
var import_redis6 = require("@tradejs/infra/redis");
|
|
43
44
|
var import_candles = require("@tradejs/infra/timescale/candles");
|
|
44
|
-
var
|
|
45
|
+
var import_runtimeStrategies3 = require("@tradejs/node/runtimeStrategies");
|
|
45
46
|
var import_strategies5 = require("@tradejs/node/strategies");
|
|
46
47
|
|
|
47
48
|
// src/lib/runFormatting.ts
|
|
@@ -4447,245 +4448,8 @@ var runtimeLineagesMatch = (left, right) => left != null && right != null && run
|
|
|
4447
4448
|
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;
|
|
4448
4449
|
|
|
4449
4450
|
// src/lib/runtimeEvidenceDeployment.ts
|
|
4450
|
-
var
|
|
4451
|
-
|
|
4452
|
-
var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
|
|
4453
|
-
var isPackageVersionMap = (value) => isRecord(value) && Object.keys(value).length > 0 && Object.entries(value).every(
|
|
4454
|
-
([name, version]) => name.startsWith("@tradejs/") && isNonEmptyString(version)
|
|
4455
|
-
);
|
|
4456
|
-
var INTERVALS = /* @__PURE__ */ new Set([
|
|
4457
|
-
"1",
|
|
4458
|
-
"3",
|
|
4459
|
-
"5",
|
|
4460
|
-
"15",
|
|
4461
|
-
"30",
|
|
4462
|
-
"60",
|
|
4463
|
-
"120",
|
|
4464
|
-
"240",
|
|
4465
|
-
"360",
|
|
4466
|
-
"720",
|
|
4467
|
-
"D",
|
|
4468
|
-
"W",
|
|
4469
|
-
"M"
|
|
4470
|
-
]);
|
|
4471
|
-
var UNIVERSES = /* @__PURE__ */ new Set(["crypto", "tradfi"]);
|
|
4472
|
-
var isRuntimeStrategySelection = (value) => value === void 0 || isRecord(value) && Object.keys(value).length === 1 && Object.keys(value).every((key) => key === "tickers") && Array.isArray(value.tickers) && value.tickers.length > 0 && value.tickers.every((ticker) => isNonEmptyString(ticker));
|
|
4473
|
-
var samePackageVersions = (left, right) => {
|
|
4474
|
-
if (!left || !right) return false;
|
|
4475
|
-
const leftEntries = Object.entries(left).sort(
|
|
4476
|
-
([a], [b]) => a.localeCompare(b)
|
|
4477
|
-
);
|
|
4478
|
-
const rightEntries = Object.entries(right).sort(
|
|
4479
|
-
([a], [b]) => a.localeCompare(b)
|
|
4480
|
-
);
|
|
4481
|
-
return JSON.stringify(leftEntries) === JSON.stringify(rightEntries);
|
|
4482
|
-
};
|
|
4483
|
-
var runtimeLineageMatchesStrategySnapshot = ({
|
|
4484
|
-
lineage,
|
|
4485
|
-
deployment,
|
|
4486
|
-
strategy
|
|
4487
|
-
}) => lineage?.schemaVersion === 3 && lineage.strategyRevision === strategy.strategyRevision && lineage.deploymentCompositionId === deployment.deploymentCompositionId && lineage.strategyPackageVersion === strategy.strategyPackageVersion && lineage.runtimePackageVersion === strategy.runtimePackageVersion && samePackageVersions(
|
|
4488
|
-
lineage.strategyDependencyVersions,
|
|
4489
|
-
strategy.strategyDependencyVersions
|
|
4490
|
-
);
|
|
4491
|
-
var parseRuntimeEvidenceDeploymentSnapshot = (value) => {
|
|
4492
|
-
if (!isRecord(value) || value.schemaVersion !== 2 || !isNonEmptyString(value.id) || !isNonEmptyString(value.deploymentCompositionId) || !/^dc1:[a-f0-9]{16}$/.test(value.deploymentCompositionId) || !isNonEmptyString(value.label) || !isNonEmptyString(value.connectorName) || !isNonEmptyString(value.provider) || !isNonEmptyString(value.accountId) || typeof value.enabled !== "boolean" || !Array.isArray(value.strategies)) {
|
|
4493
|
-
throw new Error(
|
|
4494
|
-
"Runtime evidence deployment snapshot is missing or invalid"
|
|
4495
|
-
);
|
|
4496
|
-
}
|
|
4497
|
-
const strategies = value.strategies.map((candidate) => {
|
|
4498
|
-
if (!isRecord(candidate) || !isNonEmptyString(candidate.strategyName) || !isNonEmptyString(candidate.strategyRevision) || !/^sr1:[a-f0-9]{16}$/.test(candidate.strategyRevision) || typeof candidate.enabled !== "boolean" || !["active", "entries_paused"].includes(String(candidate.controlState)) || !INTERVALS.has(String(candidate.interval)) || !UNIVERSES.has(String(candidate.universe)) || !isNonEmptyString(candidate.strategyPackage) || !isNonEmptyString(candidate.strategyPackageVersion) || !isPackageVersionMap(candidate.strategyDependencyVersions) || !isNonEmptyString(candidate.runtimePackageVersion) || !isRecord(candidate.strategyConfig) || !isRuntimeStrategySelection(candidate.selection)) {
|
|
4499
|
-
throw new Error(
|
|
4500
|
-
"Runtime evidence deployment strategy snapshot is invalid"
|
|
4501
|
-
);
|
|
4502
|
-
}
|
|
4503
|
-
if (String(candidate.strategyConfig.INTERVAL) !== String(candidate.interval) || String(candidate.strategyConfig.UNIVERSE) !== String(candidate.universe)) {
|
|
4504
|
-
throw new Error(
|
|
4505
|
-
`Runtime evidence strategy config does not match its scope: ${candidate.strategyName}`
|
|
4506
|
-
);
|
|
4507
|
-
}
|
|
4508
|
-
if (candidate.accountId != null && (!isNonEmptyString(candidate.accountId) || candidate.accountId !== value.accountId)) {
|
|
4509
|
-
throw new Error(
|
|
4510
|
-
`Runtime evidence strategy account does not match deployment: ${candidate.strategyName}`
|
|
4511
|
-
);
|
|
4512
|
-
}
|
|
4513
|
-
return candidate;
|
|
4514
|
-
});
|
|
4515
|
-
const identities = /* @__PURE__ */ new Set();
|
|
4516
|
-
for (const strategy of strategies) {
|
|
4517
|
-
if (identities.has(strategy.strategyName)) {
|
|
4518
|
-
throw new Error(
|
|
4519
|
-
`Duplicate runtime evidence strategy: ${strategy.strategyName}`
|
|
4520
|
-
);
|
|
4521
|
-
}
|
|
4522
|
-
identities.add(strategy.strategyName);
|
|
4523
|
-
}
|
|
4524
|
-
return {
|
|
4525
|
-
...value,
|
|
4526
|
-
strategies
|
|
4527
|
-
};
|
|
4528
|
-
};
|
|
4529
|
-
var activeRuntimeEvidenceStrategies = (deployment) => deployment.enabled ? deployment.strategies.filter((strategy) => strategy.enabled) : [];
|
|
4530
|
-
var unwrapRuntimeEvidenceRows = (value, field) => (Array.isArray(value) ? value : []).map((candidate) => {
|
|
4531
|
-
const record = isRecord(candidate) ? candidate : null;
|
|
4532
|
-
const nested = record && isRecord(record[field]) ? record[field] : null;
|
|
4533
|
-
return nested ?? record;
|
|
4534
|
-
}).filter((record) => record != null);
|
|
4535
|
-
var assertCurrentRuntimeEvidenceArtifact = (artifact) => {
|
|
4536
|
-
if (!isRecord(artifact) || artifact.reportType !== "runtime-evidence") {
|
|
4537
|
-
throw new Error("Invalid runtime evidence artifact");
|
|
4538
|
-
}
|
|
4539
|
-
const deployment = parseRuntimeEvidenceDeploymentSnapshot(
|
|
4540
|
-
artifact.deployment
|
|
4541
|
-
);
|
|
4542
|
-
const activeStrategies = activeRuntimeEvidenceStrategies(deployment);
|
|
4543
|
-
if (activeStrategies.length === 0) {
|
|
4544
|
-
throw new Error("Runtime evidence deployment has no enabled strategies");
|
|
4545
|
-
}
|
|
4546
|
-
const runtime = isRecord(artifact.runtime) ? artifact.runtime : null;
|
|
4547
|
-
if (!runtime) {
|
|
4548
|
-
throw new Error("Runtime evidence payload is missing");
|
|
4549
|
-
}
|
|
4550
|
-
if ("strategyConfigs" in runtime) {
|
|
4551
|
-
throw new Error(
|
|
4552
|
-
"Runtime evidence payload contains removed strategyConfigs data"
|
|
4553
|
-
);
|
|
4554
|
-
}
|
|
4555
|
-
if ("evaluationStatsBuckets" in runtime) {
|
|
4556
|
-
throw new Error(
|
|
4557
|
-
"Runtime evidence payload contains removed lineage-less evaluationStatsBuckets data"
|
|
4558
|
-
);
|
|
4559
|
-
}
|
|
4560
|
-
const rowFields = [
|
|
4561
|
-
"trades",
|
|
4562
|
-
"signals",
|
|
4563
|
-
"evaluations",
|
|
4564
|
-
"lineageScopes"
|
|
4565
|
-
];
|
|
4566
|
-
for (const field of rowFields) {
|
|
4567
|
-
if (!Array.isArray(runtime[field])) {
|
|
4568
|
-
throw new Error(`Runtime evidence payload ${field} must be an array`);
|
|
4569
|
-
}
|
|
4570
|
-
}
|
|
4571
|
-
const snapshots = new Map(
|
|
4572
|
-
activeStrategies.map((strategy) => [strategy.strategyName, strategy])
|
|
4573
|
-
);
|
|
4574
|
-
const assertRows = (label, rows, lineageField) => {
|
|
4575
|
-
for (const row of rows) {
|
|
4576
|
-
const strategyName = String(row.strategy ?? "").trim();
|
|
4577
|
-
const strategy = snapshots.get(strategyName);
|
|
4578
|
-
if (!strategy || row.deploymentId !== deployment.id || row.accountId !== deployment.accountId || !runtimeLineageMatchesStrategySnapshot({
|
|
4579
|
-
lineage: isRecord(row[lineageField]) ? row[lineageField] : void 0,
|
|
4580
|
-
deployment,
|
|
4581
|
-
strategy
|
|
4582
|
-
})) {
|
|
4583
|
-
throw new Error(
|
|
4584
|
-
`Runtime evidence ${label} row is outside the current embedded deployment lineage`
|
|
4585
|
-
);
|
|
4586
|
-
}
|
|
4587
|
-
}
|
|
4588
|
-
};
|
|
4589
|
-
assertRows(
|
|
4590
|
-
"trade",
|
|
4591
|
-
unwrapRuntimeEvidenceRows(runtime.trades, "trade"),
|
|
4592
|
-
"runtimeLineage"
|
|
4593
|
-
);
|
|
4594
|
-
assertRows(
|
|
4595
|
-
"signal",
|
|
4596
|
-
unwrapRuntimeEvidenceRows(runtime.signals, "signal"),
|
|
4597
|
-
"runtimeLineage"
|
|
4598
|
-
);
|
|
4599
|
-
assertRows(
|
|
4600
|
-
"evaluation",
|
|
4601
|
-
unwrapRuntimeEvidenceRows(runtime.evaluations, "evaluation"),
|
|
4602
|
-
"runtimeLineage"
|
|
4603
|
-
);
|
|
4604
|
-
assertRows(
|
|
4605
|
-
"lineage scope",
|
|
4606
|
-
unwrapRuntimeEvidenceRows(runtime.lineageScopes, "lineageScope"),
|
|
4607
|
-
"lineage"
|
|
4608
|
-
);
|
|
4609
|
-
return deployment;
|
|
4610
|
-
};
|
|
4611
|
-
var resolveRuntimeEvidenceTickerUniverse = ({
|
|
4612
|
-
deployment,
|
|
4613
|
-
lineageScopes,
|
|
4614
|
-
fallbackTickers
|
|
4615
|
-
}) => {
|
|
4616
|
-
const declaredTickers = deployment.tickers?.filter(isNonEmptyString) ?? [];
|
|
4617
|
-
if (declaredTickers.length > 0) {
|
|
4618
|
-
return {
|
|
4619
|
-
...deployment,
|
|
4620
|
-
tickers: [
|
|
4621
|
-
...new Set(declaredTickers.map((ticker) => ticker.trim()))
|
|
4622
|
-
].sort()
|
|
4623
|
-
};
|
|
4624
|
-
}
|
|
4625
|
-
const activeStrategies = activeRuntimeEvidenceStrategies(deployment);
|
|
4626
|
-
if (activeStrategies.length > 0 && activeStrategies.every((strategy) => strategy.selection?.tickers?.length)) {
|
|
4627
|
-
return {
|
|
4628
|
-
...deployment,
|
|
4629
|
-
tickers: [
|
|
4630
|
-
...new Set(
|
|
4631
|
-
activeStrategies.flatMap(
|
|
4632
|
-
(strategy) => strategy.selection?.tickers ?? []
|
|
4633
|
-
)
|
|
4634
|
-
)
|
|
4635
|
-
].sort()
|
|
4636
|
-
};
|
|
4637
|
-
}
|
|
4638
|
-
const strategiesByName = new Map(
|
|
4639
|
-
activeStrategies.map((strategy) => [strategy.strategyName, strategy])
|
|
4640
|
-
);
|
|
4641
|
-
const tickers = /* @__PURE__ */ new Set();
|
|
4642
|
-
for (const scope of lineageScopes) {
|
|
4643
|
-
const strategy = strategiesByName.get(scope.strategy);
|
|
4644
|
-
if (!strategy || scope.deploymentId !== deployment.id || scope.accountId !== deployment.accountId || !isNonEmptyString(scope.symbol) || !runtimeLineageMatchesStrategySnapshot({
|
|
4645
|
-
lineage: scope.lineage,
|
|
4646
|
-
deployment,
|
|
4647
|
-
strategy
|
|
4648
|
-
})) {
|
|
4649
|
-
continue;
|
|
4650
|
-
}
|
|
4651
|
-
tickers.add(scope.symbol.trim());
|
|
4652
|
-
}
|
|
4653
|
-
if (tickers.size === 0) {
|
|
4654
|
-
const verifiedFallbackTickers = fallbackTickers?.filter(isNonEmptyString) ?? [];
|
|
4655
|
-
if (verifiedFallbackTickers.length > 0) {
|
|
4656
|
-
return {
|
|
4657
|
-
...deployment,
|
|
4658
|
-
tickers: [
|
|
4659
|
-
...new Set(verifiedFallbackTickers.map((ticker) => ticker.trim()))
|
|
4660
|
-
].sort()
|
|
4661
|
-
};
|
|
4662
|
-
}
|
|
4663
|
-
throw new Error(
|
|
4664
|
-
"Runtime evidence has no immutable ticker universe for the embedded deployment composition"
|
|
4665
|
-
);
|
|
4666
|
-
}
|
|
4667
|
-
return { ...deployment, tickers: [...tickers].sort() };
|
|
4668
|
-
};
|
|
4669
|
-
var runtimeDeploymentFromEvidence = (deployment) => ({
|
|
4670
|
-
id: deployment.id,
|
|
4671
|
-
deploymentCompositionId: deployment.deploymentCompositionId,
|
|
4672
|
-
label: deployment.label,
|
|
4673
|
-
connectorName: deployment.connectorName,
|
|
4674
|
-
provider: deployment.provider,
|
|
4675
|
-
accountId: deployment.accountId,
|
|
4676
|
-
enabled: deployment.enabled,
|
|
4677
|
-
strategies: deployment.strategies.map(
|
|
4678
|
-
({ strategyName, strategyRevision, enabled, controlState, selection }) => ({
|
|
4679
|
-
strategyName,
|
|
4680
|
-
strategyRevision,
|
|
4681
|
-
enabled,
|
|
4682
|
-
controlState,
|
|
4683
|
-
...selection ? { selection } : {}
|
|
4684
|
-
})
|
|
4685
|
-
),
|
|
4686
|
-
...deployment.assetClasses ? { assetClasses: [...deployment.assetClasses] } : {},
|
|
4687
|
-
...deployment.tickers ? { tickers: [...deployment.tickers] } : {}
|
|
4688
|
-
});
|
|
4451
|
+
var runtimeEvidenceDeployment_exports = {};
|
|
4452
|
+
__reExport(runtimeEvidenceDeployment_exports, require("@tradejs/node/diagnostics"));
|
|
4689
4453
|
|
|
4690
4454
|
// src/lib/replay/runtimeEvidenceSource.ts
|
|
4691
4455
|
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
@@ -4700,7 +4464,7 @@ var assertRuntimeRowsMatchDeployment = ({
|
|
|
4700
4464
|
deployment
|
|
4701
4465
|
}) => {
|
|
4702
4466
|
const activeStrategies = new Set(
|
|
4703
|
-
activeRuntimeEvidenceStrategies(deployment).map(
|
|
4467
|
+
(0, runtimeEvidenceDeployment_exports.activeRuntimeEvidenceStrategies)(deployment).map(
|
|
4704
4468
|
({ strategyName }) => strategyName
|
|
4705
4469
|
)
|
|
4706
4470
|
);
|
|
@@ -4724,7 +4488,7 @@ var readRuntimeEvidenceArtifact = async ({
|
|
|
4724
4488
|
if (!root || !runtime) {
|
|
4725
4489
|
throw new Error(`Invalid runtime evidence JSON: ${resolvedPath}`);
|
|
4726
4490
|
}
|
|
4727
|
-
assertCurrentRuntimeEvidenceArtifact(root);
|
|
4491
|
+
(0, runtimeEvidenceDeployment_exports.assertCurrentRuntimeEvidenceArtifact)(root);
|
|
4728
4492
|
const rootWindow = asRecord(root.window);
|
|
4729
4493
|
const runtimeWindow = asRecord(runtime.window);
|
|
4730
4494
|
return {
|
|
@@ -4734,8 +4498,8 @@ var readRuntimeEvidenceArtifact = async ({
|
|
|
4734
4498
|
userName: String(root.userName ?? runtime.userName ?? "").trim(),
|
|
4735
4499
|
startTime: asFiniteNumber(rootWindow?.startTime) ?? asFiniteNumber(runtimeWindow?.startTime),
|
|
4736
4500
|
endTime: asFiniteNumber(rootWindow?.endTime) ?? asFiniteNumber(runtimeWindow?.endTime),
|
|
4737
|
-
deployment: resolveRuntimeEvidenceTickerUniverse({
|
|
4738
|
-
deployment: parseRuntimeEvidenceDeploymentSnapshot(root.deployment),
|
|
4501
|
+
deployment: (0, runtimeEvidenceDeployment_exports.resolveRuntimeEvidenceTickerUniverse)({
|
|
4502
|
+
deployment: (0, runtimeEvidenceDeployment_exports.parseRuntimeEvidenceDeploymentSnapshot)(root.deployment),
|
|
4739
4503
|
lineageScopes: unwrapRows(
|
|
4740
4504
|
runtime.lineageScopes,
|
|
4741
4505
|
"lineageScope"
|
|
@@ -4996,7 +4760,7 @@ var import_redis3 = require("@tradejs/infra/redis");
|
|
|
4996
4760
|
var import_connectors6 = require("@tradejs/node/connectors");
|
|
4997
4761
|
var import_cli4 = require("@tradejs/node/cli");
|
|
4998
4762
|
var import_strategies4 = require("@tradejs/node/strategies");
|
|
4999
|
-
var
|
|
4763
|
+
var import_runtimeStrategies2 = require("@tradejs/node/runtimeStrategies");
|
|
5000
4764
|
|
|
5001
4765
|
// src/lib/replay/portfolioReplayConnector.ts
|
|
5002
4766
|
var import_constants3 = require("@tradejs/core/constants");
|
|
@@ -5987,7 +5751,7 @@ var loadRuntimeStrategies = async (runtimeStrategies) => {
|
|
|
5987
5751
|
if (!strategyCreator) {
|
|
5988
5752
|
throw new Error(`Unknown strategy: ${strategyName}`);
|
|
5989
5753
|
}
|
|
5990
|
-
const installed = await (0,
|
|
5754
|
+
const installed = await (0, import_runtimeStrategies2.getRuntimeStrategyPackageMetadata)({
|
|
5991
5755
|
strategyName,
|
|
5992
5756
|
projectRoot: replayProjectRoot,
|
|
5993
5757
|
strategyModule
|
|
@@ -9906,8 +9670,8 @@ var replayBacktest = async () => {
|
|
|
9906
9670
|
);
|
|
9907
9671
|
}
|
|
9908
9672
|
const replayComposition = evidenceMetadata ? {
|
|
9909
|
-
deployment: runtimeDeploymentFromEvidence(evidenceMetadata.deployment),
|
|
9910
|
-
strategies: activeRuntimeEvidenceStrategies(
|
|
9673
|
+
deployment: (0, runtimeEvidenceDeployment_exports.runtimeDeploymentFromEvidence)(evidenceMetadata.deployment),
|
|
9674
|
+
strategies: (0, runtimeEvidenceDeployment_exports.activeRuntimeEvidenceStrategies)(
|
|
9911
9675
|
evidenceMetadata.deployment
|
|
9912
9676
|
).map(
|
|
9913
9677
|
({
|
|
@@ -9959,7 +9723,7 @@ var replayBacktest = async () => {
|
|
|
9959
9723
|
return {
|
|
9960
9724
|
...strategy,
|
|
9961
9725
|
strategyConfig,
|
|
9962
|
-
strategyRevision: (0,
|
|
9726
|
+
strategyRevision: (0, import_runtimeStrategies3.computeStrategyRevision)({
|
|
9963
9727
|
strategyName: strategy.strategyName,
|
|
9964
9728
|
strategyPackage: strategy.strategyPackage,
|
|
9965
9729
|
strategyPackageVersion: strategy.strategyPackageVersion,
|
|
@@ -9969,7 +9733,7 @@ var replayBacktest = async () => {
|
|
|
9969
9733
|
})
|
|
9970
9734
|
};
|
|
9971
9735
|
});
|
|
9972
|
-
const researchDeploymentCompositionId = (0,
|
|
9736
|
+
const researchDeploymentCompositionId = (0, import_runtimeStrategies3.computeDeploymentCompositionId)({
|
|
9973
9737
|
deploymentId: deployment.id,
|
|
9974
9738
|
connectorName: deployment.connectorName,
|
|
9975
9739
|
provider: deployment.provider,
|