@tradejs/cli 3.1.29-beta.286 → 3.1.29-beta.287
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 +647 -486
- package/dist/lib/mcpReplayProcess.js +4 -2
- package/dist/lib/strategyResearch.js +24 -11
- package/dist/scripts/mcpWorker.js +4 -2
- package/dist/scripts/replay.js +644 -498
- package/dist/scripts/replayRunner.js +646 -498
- package/dist/scripts/strategyResearch.js +24 -11
- package/package.json +7 -7
|
@@ -62,14 +62,16 @@ const timer = setInterval(() => {
|
|
|
62
62
|
if (result.exitCode !== 0) {
|
|
63
63
|
let fd;
|
|
64
64
|
try {
|
|
65
|
-
|
|
65
|
+
const logId = result.logRequestId === undefined ? id : result.logRequestId;
|
|
66
|
+
if (typeof logId !== 'string' || !/^[a-f0-9]{64}$/.test(logId)) throw new Error('Invalid replay log identity');
|
|
67
|
+
fd = fs.openSync(path.join(root, logId) + '.log', fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
66
68
|
const size = fs.fstatSync(fd).size;
|
|
67
69
|
const tail = Buffer.alloc(Math.min(size, 65536));
|
|
68
70
|
fs.readSync(fd, tail, 0, tail.length, Math.max(0, size - tail.length));
|
|
69
71
|
// The worker redacts each line before persisting or returning it through MCP.
|
|
70
72
|
for (const line of tail.toString('utf8').split(/[\\r\\n]+/).filter(Boolean).slice(-150)) console.error('Host replay: ' + line);
|
|
71
73
|
} catch (error) {
|
|
72
|
-
console.error('Host replay log unavailable: ' + error.code);
|
|
74
|
+
console.error('Host replay: ' + result.message + '; log unavailable: ' + (error.code || error.message));
|
|
73
75
|
} finally { if (fd !== undefined) fs.closeSync(fd); }
|
|
74
76
|
}
|
|
75
77
|
clearInterval(timer);
|
|
@@ -2364,6 +2364,10 @@ var validateStrategyResearchSpec = (value) => {
|
|
|
2364
2364
|
};
|
|
2365
2365
|
if (!spec.budget || !spec.selection || !spec.diagnostics)
|
|
2366
2366
|
throw new Error("Missing research budgets or criteria");
|
|
2367
|
+
if (spec.featurePaths !== void 0 && (!Array.isArray(spec.featurePaths) || !spec.featurePaths.length || spec.featurePaths.length > 64 || new Set(spec.featurePaths).size !== spec.featurePaths.length || spec.featurePaths.some(
|
|
2368
|
+
(path13) => typeof path13 !== "string" || !/^additionalIndicators\.[a-zA-Z0-9_.]+$/.test(path13) || classifyAiPocketFeaturePath(path13.split(".")) !== "eligible"
|
|
2369
|
+
)))
|
|
2370
|
+
throw new Error("Feature paths must be unique causal market features");
|
|
2367
2371
|
positive(spec.budget.maxVariants, "maxVariants", 12);
|
|
2368
2372
|
if (!Number.isInteger(spec.minQuality) || spec.minQuality < 1 || spec.minQuality > 5)
|
|
2369
2373
|
throw new Error("minQuality must be 1 through 5");
|
|
@@ -4392,6 +4396,7 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4392
4396
|
parentSpec.selection,
|
|
4393
4397
|
parentSpec.portfolio,
|
|
4394
4398
|
parentSpec.minQuality,
|
|
4399
|
+
parentSpec.featurePaths,
|
|
4395
4400
|
parentSpec.diagnostics
|
|
4396
4401
|
]) !== sha256Json([
|
|
4397
4402
|
spec.core.variants.map((item) => item.resolvedConfig),
|
|
@@ -4400,6 +4405,7 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4400
4405
|
spec.selection,
|
|
4401
4406
|
spec.portfolio,
|
|
4402
4407
|
spec.minQuality,
|
|
4408
|
+
spec.featurePaths,
|
|
4403
4409
|
spec.diagnostics
|
|
4404
4410
|
]) || parentState.lineage.runtimeSha256 !== state.lineage.runtimeSha256 || parentState.lineage.contextEnvironmentSha256 !== state.lineage.contextEnvironmentSha256)
|
|
4405
4411
|
throw new Error(
|
|
@@ -5779,9 +5785,10 @@ var createNativeStrategyResearchBackend = (parameters) => {
|
|
|
5779
5785
|
throw new Error(
|
|
5780
5786
|
"Tester workers are already active; research will resume when the host is free"
|
|
5781
5787
|
);
|
|
5782
|
-
|
|
5788
|
+
const availableBytes = import_node_os2.default.platform() === "darwin" ? process.availableMemory() : import_node_os2.default.freemem();
|
|
5789
|
+
if (!Number.isFinite(availableBytes) || availableBytes < spec.budget.minimumFreeMemoryMb * 1024 * 1024)
|
|
5783
5790
|
throw new Error(
|
|
5784
|
-
"Insufficient
|
|
5791
|
+
"Insufficient available host memory for the frozen research budget"
|
|
5785
5792
|
);
|
|
5786
5793
|
};
|
|
5787
5794
|
return {
|
|
@@ -6234,9 +6241,14 @@ var createNativeStrategyResearchBackend = (parameters) => {
|
|
|
6234
6241
|
throw new Error(
|
|
6235
6242
|
`Native/export reconciliation is ${reconciliation.status}: ${reconciliation.reasons.join("; ")}`
|
|
6236
6243
|
);
|
|
6244
|
+
const researchSignalId = (symbol, signalId) => JSON.stringify([symbol, signalId]);
|
|
6237
6245
|
const trades = loaded.trades.filter(
|
|
6238
6246
|
(trade) => trade.signalTimestamp >= spec.core.window.start && trade.signalTimestamp < spec.core.window.end && trade.exitTimestamp < spec.core.window.end
|
|
6239
|
-
)
|
|
6247
|
+
).map((trade) => ({
|
|
6248
|
+
...trade,
|
|
6249
|
+
// Strategy-generated signal IDs need not be unique across symbols.
|
|
6250
|
+
signalId: researchSignalId(trade.symbol, trade.signalId)
|
|
6251
|
+
}));
|
|
6240
6252
|
const tradeBySignal = new Map(
|
|
6241
6253
|
trades.map((trade) => [trade.signalId, trade])
|
|
6242
6254
|
);
|
|
@@ -6260,9 +6272,10 @@ var createNativeStrategyResearchBackend = (parameters) => {
|
|
|
6260
6272
|
for await (const line of lines) {
|
|
6261
6273
|
if (!line.trim()) continue;
|
|
6262
6274
|
const row = JSON.parse(line);
|
|
6263
|
-
const
|
|
6264
|
-
|
|
6265
|
-
seen.
|
|
6275
|
+
const signalId = researchSignalId(row.symbol, row.signalId);
|
|
6276
|
+
const trade = tradeBySignal.get(signalId);
|
|
6277
|
+
if (!trade || seen.has(signalId)) continue;
|
|
6278
|
+
seen.add(signalId);
|
|
6266
6279
|
if (discoveryOnly && (row.timestamp >= spec.testSince || trade.exitTimestamp >= spec.testSince)) {
|
|
6267
6280
|
if (row.timestamp < spec.testSince) purgedLabels++;
|
|
6268
6281
|
continue;
|
|
@@ -6300,7 +6313,7 @@ var createNativeStrategyResearchBackend = (parameters) => {
|
|
|
6300
6313
|
featureProfile: "all"
|
|
6301
6314
|
});
|
|
6302
6315
|
for (const key of Object.keys(features))
|
|
6303
|
-
if (!key.startsWith("additionalIndicators.") || features[key] == null)
|
|
6316
|
+
if (!key.startsWith("additionalIndicators.") || features[key] == null || spec.featurePaths && !spec.featurePaths.includes(key))
|
|
6304
6317
|
delete features[key];
|
|
6305
6318
|
featureValues += Object.keys(features).length;
|
|
6306
6319
|
if (featureValues > 5e6)
|
|
@@ -6319,7 +6332,7 @@ var createNativeStrategyResearchBackend = (parameters) => {
|
|
|
6319
6332
|
pathCoverage[key]++;
|
|
6320
6333
|
}
|
|
6321
6334
|
rows.push({
|
|
6322
|
-
signalId
|
|
6335
|
+
signalId,
|
|
6323
6336
|
symbol: row.symbol,
|
|
6324
6337
|
strategy: row.strategyName,
|
|
6325
6338
|
timestamp: row.timestamp,
|
|
@@ -6334,7 +6347,7 @@ var createNativeStrategyResearchBackend = (parameters) => {
|
|
|
6334
6347
|
});
|
|
6335
6348
|
if (discoveryOnly) {
|
|
6336
6349
|
eventInputs.push({
|
|
6337
|
-
signalId
|
|
6350
|
+
signalId,
|
|
6338
6351
|
timestamp: row.timestamp,
|
|
6339
6352
|
symbol: row.symbol,
|
|
6340
6353
|
direction: row.direction,
|
|
@@ -6345,8 +6358,8 @@ var createNativeStrategyResearchBackend = (parameters) => {
|
|
|
6345
6358
|
});
|
|
6346
6359
|
const key = `${row.direction}:${trade.netProfit > 0}`;
|
|
6347
6360
|
const bucket = sampleBuckets.get(key) ?? [];
|
|
6348
|
-
bucket.push(
|
|
6349
|
-
sampleInputs.set(
|
|
6361
|
+
bucket.push(signalId);
|
|
6362
|
+
sampleInputs.set(signalId, row);
|
|
6350
6363
|
bucket.sort((a, b) => sha256Json(a).localeCompare(sha256Json(b)));
|
|
6351
6364
|
while (bucket.length > spec.diagnostics.samplePerSideAndOutcome)
|
|
6352
6365
|
sampleInputs.delete(bucket.pop());
|
|
@@ -69,14 +69,16 @@ const timer = setInterval(() => {
|
|
|
69
69
|
if (result.exitCode !== 0) {
|
|
70
70
|
let fd;
|
|
71
71
|
try {
|
|
72
|
-
|
|
72
|
+
const logId = result.logRequestId === undefined ? id : result.logRequestId;
|
|
73
|
+
if (typeof logId !== 'string' || !/^[a-f0-9]{64}$/.test(logId)) throw new Error('Invalid replay log identity');
|
|
74
|
+
fd = fs.openSync(path.join(root, logId) + '.log', fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
|
|
73
75
|
const size = fs.fstatSync(fd).size;
|
|
74
76
|
const tail = Buffer.alloc(Math.min(size, 65536));
|
|
75
77
|
fs.readSync(fd, tail, 0, tail.length, Math.max(0, size - tail.length));
|
|
76
78
|
// The worker redacts each line before persisting or returning it through MCP.
|
|
77
79
|
for (const line of tail.toString('utf8').split(/[\\r\\n]+/).filter(Boolean).slice(-150)) console.error('Host replay: ' + line);
|
|
78
80
|
} catch (error) {
|
|
79
|
-
console.error('Host replay log unavailable: ' + error.code);
|
|
81
|
+
console.error('Host replay: ' + result.message + '; log unavailable: ' + (error.code || error.message));
|
|
80
82
|
} finally { if (fd !== undefined) fs.closeSync(fd); }
|
|
81
83
|
}
|
|
82
84
|
clearInterval(timer);
|