@tradejs/cli 3.1.29-beta.290 → 3.1.29-beta.292
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 +12283 -10394
- package/dist/lib/aiPocketSearch.js +37 -3
- package/dist/lib/aiTrainDataset.js +1 -0
- package/dist/lib/aiTrainEvaluationDump.js +7 -2
- package/dist/lib/aiTrainResearch.js +11 -7
- package/dist/lib/coreResearch.js +273 -119
- package/dist/lib/researchMetricSnapshot.js +74 -0
- package/dist/lib/researchVisuals.js +931 -0
- package/dist/lib/strategyRelease.js +8 -2
- package/dist/lib/strategyResearch.js +334 -199
- package/dist/lib/tickerProfiles.js +76 -0
- package/dist/scripts/aiPocketSearch.js +2959 -2610
- package/dist/scripts/aiTrain.js +29 -17
- package/dist/scripts/coreResearch.js +275 -119
- package/dist/scripts/replay.js +533 -115
- package/dist/scripts/replayRunner.js +533 -115
- package/dist/scripts/researchVisuals.js +976 -0
- package/dist/scripts/strategyRelease.js +59 -7
- package/dist/scripts/strategyResearch.js +339 -203
- package/dist/scripts/tickerProfiles.js +114 -0
- package/package.json +8 -7
|
@@ -35,6 +35,98 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
35
35
|
));
|
|
36
36
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
37
37
|
|
|
38
|
+
// src/lib/coreResearch/trace.ts
|
|
39
|
+
async function* readCoreResearchTrace(filePaths) {
|
|
40
|
+
for (const inputPath of filePaths) {
|
|
41
|
+
const filePath = import_node_path.default.resolve(inputPath);
|
|
42
|
+
const input = (0, import_node_fs.createReadStream)(filePath);
|
|
43
|
+
const lines = import_node_readline.default.createInterface({ input, crlfDelay: Infinity });
|
|
44
|
+
let index = -1;
|
|
45
|
+
try {
|
|
46
|
+
for await (const line of lines) {
|
|
47
|
+
index++;
|
|
48
|
+
if (!line.trim()) continue;
|
|
49
|
+
let event;
|
|
50
|
+
try {
|
|
51
|
+
event = JSON.parse(line);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`${filePath}:${index + 1} contains invalid trace JSON: ${String(error)}`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
yield event;
|
|
58
|
+
}
|
|
59
|
+
} finally {
|
|
60
|
+
lines.close();
|
|
61
|
+
input.destroy();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
var import_node_fs, import_node_readline, import_node_path, summarizeCoreResearchTrace;
|
|
66
|
+
var init_trace = __esm({
|
|
67
|
+
"src/lib/coreResearch/trace.ts"() {
|
|
68
|
+
"use strict";
|
|
69
|
+
import_node_fs = require("fs");
|
|
70
|
+
import_node_readline = __toESM(require("readline"));
|
|
71
|
+
import_node_path = __toESM(require("path"));
|
|
72
|
+
summarizeCoreResearchTrace = async (filePaths = []) => {
|
|
73
|
+
const events = /* @__PURE__ */ Object.create(null);
|
|
74
|
+
const skipCounts = /* @__PURE__ */ Object.create(null);
|
|
75
|
+
const exitCodes = /* @__PURE__ */ Object.create(null);
|
|
76
|
+
const policyRejections = {};
|
|
77
|
+
const policyExamples = [];
|
|
78
|
+
for await (const event of readCoreResearchTrace(filePaths)) {
|
|
79
|
+
events[event.event] = (events[event.event] ?? 0) + 1;
|
|
80
|
+
if (event.event === "skip_summary") {
|
|
81
|
+
for (const [reason, count] of Object.entries(event.skipCounts)) {
|
|
82
|
+
skipCounts[reason] = (skipCounts[reason] ?? 0) + count;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (event.event === "position_exited" && event.exitCode) {
|
|
86
|
+
exitCodes[event.exitCode] = (exitCodes[event.exitCode] ?? 0) + 1;
|
|
87
|
+
}
|
|
88
|
+
if ("researchEntryDecision" in event && event.researchEntryDecision && !event.researchEntryDecision.allowed) {
|
|
89
|
+
const reason = event.researchEntryDecision.reason;
|
|
90
|
+
policyRejections[reason] = (policyRejections[reason] ?? 0) + 1;
|
|
91
|
+
if (policyExamples.length < 20)
|
|
92
|
+
policyExamples.push({
|
|
93
|
+
timestamp: event.timestamp,
|
|
94
|
+
symbol: event.symbol,
|
|
95
|
+
decision: event.researchEntryDecision
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
events: Object.fromEntries(Object.entries(events).sort()),
|
|
101
|
+
skipCounts: Object.fromEntries(
|
|
102
|
+
Object.entries(skipCounts).sort(
|
|
103
|
+
([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
|
|
104
|
+
)
|
|
105
|
+
),
|
|
106
|
+
exitCodes: Object.fromEntries(
|
|
107
|
+
Object.entries(exitCodes).sort(
|
|
108
|
+
([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
|
|
109
|
+
)
|
|
110
|
+
),
|
|
111
|
+
...policyExamples.length ? {
|
|
112
|
+
researchPolicy: {
|
|
113
|
+
rejections: policyRejections,
|
|
114
|
+
examples: policyExamples
|
|
115
|
+
}
|
|
116
|
+
} : {}
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// src/lib/coreResearch/support.ts
|
|
123
|
+
var init_support = __esm({
|
|
124
|
+
"src/lib/coreResearch/support.ts"() {
|
|
125
|
+
"use strict";
|
|
126
|
+
init_trace();
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
38
130
|
// src/lib/coreResearch/types.ts
|
|
39
131
|
var CORE_RESEARCH_SCHEMA;
|
|
40
132
|
var init_types = __esm({
|
|
@@ -48,15 +140,16 @@ var init_types = __esm({
|
|
|
48
140
|
function assert(condition, message) {
|
|
49
141
|
if (!condition) throw new Error(`Invalid core research spec: ${message}`);
|
|
50
142
|
}
|
|
51
|
-
var import_node_crypto,
|
|
143
|
+
var import_node_crypto, import_node_fs2, import_promises, import_backtest, import_node_path2, RESEARCH_ID_RE, SHA256_RE, canonicalJson, sha256Text, sha256Json, sha256File, assertFinite, validateCoreResearchSpec, writeJsonAtomic;
|
|
52
144
|
var init_io = __esm({
|
|
53
145
|
"src/lib/coreResearch/io.ts"() {
|
|
54
146
|
"use strict";
|
|
55
147
|
import_node_crypto = require("crypto");
|
|
56
|
-
|
|
148
|
+
import_node_fs2 = require("fs");
|
|
57
149
|
import_promises = __toESM(require("fs/promises"));
|
|
58
150
|
import_backtest = require("@tradejs/core/backtest");
|
|
59
|
-
|
|
151
|
+
import_node_path2 = __toESM(require("path"));
|
|
152
|
+
init_support();
|
|
60
153
|
init_types();
|
|
61
154
|
RESEARCH_ID_RE = /^[a-z0-9][a-z0-9._-]{2,120}$/;
|
|
62
155
|
SHA256_RE = /^[a-f0-9]{64}$/;
|
|
@@ -75,10 +168,10 @@ var init_io = __esm({
|
|
|
75
168
|
sha256Text = (value) => (0, import_node_crypto.createHash)("sha256").update(value).digest("hex");
|
|
76
169
|
sha256Json = (value) => sha256Text(canonicalJson(value));
|
|
77
170
|
sha256File = async (filePath) => {
|
|
78
|
-
const
|
|
79
|
-
const input = (0,
|
|
80
|
-
for await (const chunk of input)
|
|
81
|
-
return
|
|
171
|
+
const hash2 = (0, import_node_crypto.createHash)("sha256");
|
|
172
|
+
const input = (0, import_node_fs2.createReadStream)(filePath);
|
|
173
|
+
for await (const chunk of input) hash2.update(chunk);
|
|
174
|
+
return hash2.digest("hex");
|
|
82
175
|
};
|
|
83
176
|
assertFinite = (value, name) => {
|
|
84
177
|
assert(typeof value === "number" && Number.isFinite(value), name);
|
|
@@ -312,10 +405,26 @@ var init_io = __esm({
|
|
|
312
405
|
Boolean(spec.artifacts?.ledgerPath?.trim()),
|
|
313
406
|
"artifacts.ledgerPath is required"
|
|
314
407
|
);
|
|
408
|
+
if (spec.support !== void 0) {
|
|
409
|
+
assert(
|
|
410
|
+
spec.support && typeof spec.support === "object",
|
|
411
|
+
"support must be an object"
|
|
412
|
+
);
|
|
413
|
+
for (const [key, value2] of [
|
|
414
|
+
["minimumEvents", spec.support.minimumEvents],
|
|
415
|
+
["minimumBlocks", spec.support.minimumBlocks],
|
|
416
|
+
...spec.support.minimumCommonSetups === void 0 ? [] : [["minimumCommonSetups", spec.support.minimumCommonSetups]]
|
|
417
|
+
]) {
|
|
418
|
+
assert(
|
|
419
|
+
typeof value2 === "number" && Number.isSafeInteger(value2) && value2 > 0,
|
|
420
|
+
`support.${key} must be a positive integer`
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
315
424
|
return spec;
|
|
316
425
|
};
|
|
317
426
|
writeJsonAtomic = async (filePath, value) => {
|
|
318
|
-
await import_promises.default.mkdir(
|
|
427
|
+
await import_promises.default.mkdir(import_node_path2.default.dirname(filePath), { recursive: true });
|
|
319
428
|
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
320
429
|
await import_promises.default.writeFile(temporaryPath, `${canonicalJson(value)}
|
|
321
430
|
`, "utf8");
|
|
@@ -336,30 +445,30 @@ __export(io_exports, {
|
|
|
336
445
|
verifyResearchState: () => verifyResearchState,
|
|
337
446
|
withResearchLock: () => withResearchLock
|
|
338
447
|
});
|
|
339
|
-
var import_promises2,
|
|
448
|
+
var import_promises2, import_node_path3, import_node_os, import_node_crypto3, researchDirectory, readJson, boundResearchPath, withResearchLock, verifyResearchState, verifyCompletedResearch, initializeResearchState, isRetainedDeletion;
|
|
340
449
|
var init_io2 = __esm({
|
|
341
450
|
"src/lib/strategyResearch/io.ts"() {
|
|
342
451
|
"use strict";
|
|
343
452
|
import_promises2 = __toESM(require("fs/promises"));
|
|
344
|
-
|
|
453
|
+
import_node_path3 = __toESM(require("path"));
|
|
345
454
|
import_node_os = __toESM(require("os"));
|
|
346
|
-
|
|
455
|
+
import_node_crypto3 = require("crypto");
|
|
347
456
|
init_io();
|
|
348
|
-
researchDirectory = (projectRoot, spec) =>
|
|
457
|
+
researchDirectory = (projectRoot, spec) => import_node_path3.default.join(projectRoot, "data/research/strategy", spec.core.researchId);
|
|
349
458
|
readJson = async (file) => JSON.parse(await import_promises2.default.readFile(file, "utf8"));
|
|
350
459
|
boundResearchPath = async (root, relative) => {
|
|
351
|
-
const target =
|
|
460
|
+
const target = import_node_path3.default.resolve(root, relative);
|
|
352
461
|
const realRoot = await import_promises2.default.realpath(root);
|
|
353
462
|
let ancestor = target;
|
|
354
463
|
while (true) {
|
|
355
464
|
try {
|
|
356
465
|
const real = await import_promises2.default.realpath(ancestor);
|
|
357
|
-
if (real !== realRoot && !real.startsWith(`${realRoot}${
|
|
466
|
+
if (real !== realRoot && !real.startsWith(`${realRoot}${import_node_path3.default.sep}`))
|
|
358
467
|
throw new Error(`Research path escapes Project: ${relative}`);
|
|
359
468
|
break;
|
|
360
469
|
} catch (error) {
|
|
361
470
|
if (error.code !== "ENOENT") throw error;
|
|
362
|
-
const parent =
|
|
471
|
+
const parent = import_node_path3.default.dirname(ancestor);
|
|
363
472
|
if (parent === ancestor) throw error;
|
|
364
473
|
ancestor = parent;
|
|
365
474
|
}
|
|
@@ -371,8 +480,8 @@ var init_io2 = __esm({
|
|
|
371
480
|
projectRoot,
|
|
372
481
|
"data/research/strategy/.lock"
|
|
373
482
|
);
|
|
374
|
-
await import_promises2.default.mkdir(
|
|
375
|
-
const token = (0,
|
|
483
|
+
await import_promises2.default.mkdir(import_node_path3.default.dirname(file), { recursive: true });
|
|
484
|
+
const token = (0, import_node_crypto3.randomUUID)();
|
|
376
485
|
const acquire = () => import_promises2.default.writeFile(
|
|
377
486
|
file,
|
|
378
487
|
JSON.stringify({ pid: process.pid, host: import_node_os.default.hostname(), token }),
|
|
@@ -405,7 +514,7 @@ var init_io2 = __esm({
|
|
|
405
514
|
}
|
|
406
515
|
};
|
|
407
516
|
verifyResearchState = async (directory, state) => {
|
|
408
|
-
const frozenSpec = await readJson(
|
|
517
|
+
const frozenSpec = await readJson(import_node_path3.default.join(directory, "spec.json"));
|
|
409
518
|
if (sha256Json(frozenSpec) !== state.specSha256)
|
|
410
519
|
throw new Error("Frozen research spec changed");
|
|
411
520
|
for (const [name, stage] of Object.entries(state.stages)) {
|
|
@@ -424,14 +533,14 @@ var init_io2 = __esm({
|
|
|
424
533
|
};
|
|
425
534
|
verifyCompletedResearch = async (directory, state) => {
|
|
426
535
|
await verifyResearchState(directory, state);
|
|
427
|
-
const manifest = await readJson(
|
|
536
|
+
const manifest = await readJson(import_node_path3.default.join(directory, "manifest.json"));
|
|
428
537
|
if (manifest.specSha256 !== state.specSha256)
|
|
429
538
|
throw new Error("Final manifest spec mismatch");
|
|
430
539
|
for (const [relative, expected] of Object.entries(manifest.artifacts))
|
|
431
540
|
if (await sha256File(await boundResearchPath(directory, relative)) !== expected)
|
|
432
541
|
throw new Error(`Changed final research artifact: ${relative}`);
|
|
433
|
-
const inventory = await readJson(
|
|
434
|
-
const projectRoot =
|
|
542
|
+
const inventory = await readJson(import_node_path3.default.join(directory, "retention.json"));
|
|
543
|
+
const projectRoot = import_node_path3.default.resolve(directory, "../../../..");
|
|
435
544
|
for (const artifact of inventory.research.permanentArtifacts) {
|
|
436
545
|
const file = await boundResearchPath(projectRoot, artifact.path);
|
|
437
546
|
if (await sha256File(file) !== artifact.sha256)
|
|
@@ -440,7 +549,7 @@ var init_io2 = __esm({
|
|
|
440
549
|
};
|
|
441
550
|
initializeResearchState = async (directory, spec, lineage) => {
|
|
442
551
|
await import_promises2.default.mkdir(directory, { recursive: true });
|
|
443
|
-
const file =
|
|
552
|
+
const file = import_node_path3.default.join(directory, "state.json");
|
|
444
553
|
let previous;
|
|
445
554
|
try {
|
|
446
555
|
previous = await readJson(file);
|
|
@@ -464,19 +573,19 @@ var init_io2 = __esm({
|
|
|
464
573
|
status: "running",
|
|
465
574
|
stages: {}
|
|
466
575
|
};
|
|
467
|
-
await writeJsonAtomic(
|
|
576
|
+
await writeJsonAtomic(import_node_path3.default.join(directory, "spec.json"), spec);
|
|
468
577
|
await writeJsonAtomic(file, state);
|
|
469
578
|
return state;
|
|
470
579
|
};
|
|
471
|
-
isRetainedDeletion = async (directory, file,
|
|
580
|
+
isRetainedDeletion = async (directory, file, hash2) => {
|
|
472
581
|
try {
|
|
473
|
-
const inventory = await readJson(
|
|
582
|
+
const inventory = await readJson(import_node_path3.default.join(directory, "retention.json"));
|
|
474
583
|
const index = inventory.entries.findIndex(
|
|
475
|
-
(entry) => entry.path === file && entry.sha256 ===
|
|
584
|
+
(entry) => entry.path === file && entry.sha256 === hash2
|
|
476
585
|
);
|
|
477
586
|
if (index < 0) return false;
|
|
478
|
-
const receipt = await readJson(
|
|
479
|
-
return ["applying", "completed"].includes(receipt.status) && receipt.files[0]?.path === file && receipt.files[0]?.sha256 ===
|
|
587
|
+
const receipt = await readJson(import_node_path3.default.join(directory, "retention", `${index}.json`));
|
|
588
|
+
return ["applying", "completed"].includes(receipt.status) && receipt.files[0]?.path === file && receipt.files[0]?.sha256 === hash2;
|
|
480
589
|
} catch (error) {
|
|
481
590
|
if (error.code === "ENOENT") return false;
|
|
482
591
|
throw error;
|
|
@@ -499,6 +608,7 @@ module.exports = __toCommonJS(strategyResearch_exports);
|
|
|
499
608
|
init_io();
|
|
500
609
|
|
|
501
610
|
// src/lib/aiPocketSearch/features.ts
|
|
611
|
+
var import_aiFeatures = require("@tradejs/core/aiFeatures");
|
|
502
612
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
503
613
|
var DAYS_PER_WEEK = 7;
|
|
504
614
|
var DAYS_PER_MONTH = 30.4375;
|
|
@@ -867,15 +977,25 @@ var addFlattenedFeatures = ({
|
|
|
867
977
|
value,
|
|
868
978
|
segments,
|
|
869
979
|
maxDepth,
|
|
870
|
-
shouldSkipPath = () => false
|
|
980
|
+
shouldSkipPath = () => false,
|
|
981
|
+
onFeatureExcluded
|
|
871
982
|
}) => {
|
|
872
983
|
if (!segments.length && !isPlainRecord(value)) {
|
|
873
984
|
return;
|
|
874
985
|
}
|
|
875
|
-
if (segments.length > maxDepth || isOutcomePath(segments)
|
|
986
|
+
if (segments.length > maxDepth || isOutcomePath(segments)) {
|
|
987
|
+
onFeatureExcluded?.({
|
|
988
|
+
path: segments.join("."),
|
|
989
|
+
classification: segments.length > maxDepth ? "depth-limit" : "derived-policy"
|
|
990
|
+
});
|
|
876
991
|
return;
|
|
877
992
|
}
|
|
993
|
+
if (shouldSkipPath(segments)) return;
|
|
878
994
|
if (Array.isArray(value)) {
|
|
995
|
+
onFeatureExcluded?.({
|
|
996
|
+
path: segments.join("."),
|
|
997
|
+
classification: "unprojected-array"
|
|
998
|
+
});
|
|
879
999
|
return;
|
|
880
1000
|
}
|
|
881
1001
|
if (isFeaturePrimitive(value)) {
|
|
@@ -897,7 +1017,8 @@ var addFlattenedFeatures = ({
|
|
|
897
1017
|
value: child,
|
|
898
1018
|
segments: [...segments, key],
|
|
899
1019
|
maxDepth,
|
|
900
|
-
shouldSkipPath
|
|
1020
|
+
shouldSkipPath,
|
|
1021
|
+
onFeatureExcluded
|
|
901
1022
|
});
|
|
902
1023
|
}
|
|
903
1024
|
};
|
|
@@ -1109,7 +1230,10 @@ var collectAiPocketFeatureSnapshot = ({
|
|
|
1109
1230
|
value: source,
|
|
1110
1231
|
segments: [],
|
|
1111
1232
|
maxDepth: 8,
|
|
1233
|
+
onFeatureExcluded,
|
|
1112
1234
|
shouldSkipPath: (segments) => {
|
|
1235
|
+
if (segments.join(".") === "additionalIndicators.baseContext.tickerProfile" || segments.join(".") === "additionalIndicators.tickerProfile" || segments.join(".") === "additionalIndicators.portfolioContext")
|
|
1236
|
+
return true;
|
|
1113
1237
|
if (isOperatorExcluded(segments.join("."))) {
|
|
1114
1238
|
return true;
|
|
1115
1239
|
}
|
|
@@ -1127,6 +1251,10 @@ var collectAiPocketFeatureSnapshot = ({
|
|
|
1127
1251
|
return true;
|
|
1128
1252
|
}
|
|
1129
1253
|
if (featureProfile === "compact" && isCompactFeaturePathSkipped(segments)) {
|
|
1254
|
+
onFeatureExcluded?.({
|
|
1255
|
+
path: segments.join("."),
|
|
1256
|
+
classification: "compact-profile"
|
|
1257
|
+
});
|
|
1130
1258
|
return true;
|
|
1131
1259
|
}
|
|
1132
1260
|
if (featurePolicy !== "causal-stationary") {
|
|
@@ -1161,6 +1289,21 @@ var collectAiPocketFeatureSnapshot = ({
|
|
|
1161
1289
|
delete features[key];
|
|
1162
1290
|
}
|
|
1163
1291
|
}
|
|
1292
|
+
const registered = payload.featureSnapshot;
|
|
1293
|
+
if (registered) {
|
|
1294
|
+
(0, import_aiFeatures.validateAiFeatureSnapshot)(registered);
|
|
1295
|
+
for (const definition of registered.definitions) {
|
|
1296
|
+
const path13 = `registered.${definition.id}`;
|
|
1297
|
+
if (isOperatorExcluded(path13)) continue;
|
|
1298
|
+
const value = registered.values[definition.id];
|
|
1299
|
+
const classification = definition.role === "outcome" || definition.role === "policy" ? "derived-policy" : definition.role !== "input" ? definition.role : featurePolicy === "causal-stationary" && !definition.stationary ? "raw-nonstationary" : value === null || value === "unknown" ? "data-quality" : "eligible";
|
|
1300
|
+
if (classification !== "eligible") {
|
|
1301
|
+
onFeatureExcluded?.({ path: path13, classification });
|
|
1302
|
+
continue;
|
|
1303
|
+
}
|
|
1304
|
+
features[path13] = value;
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1164
1307
|
return { features, featureCoverage };
|
|
1165
1308
|
};
|
|
1166
1309
|
var collectAiPocketFeatures = (options) => collectAiPocketFeatureSnapshot(options).features;
|
|
@@ -1779,15 +1922,15 @@ var createPocketResult = (rows, predicates, mask, validationRows, testRows, obje
|
|
|
1779
1922
|
};
|
|
1780
1923
|
};
|
|
1781
1924
|
var hashMask = (mask) => {
|
|
1782
|
-
let
|
|
1925
|
+
let hash2 = 2166136261;
|
|
1783
1926
|
for (let index = 0; index < mask.length; index += 1) {
|
|
1784
1927
|
if (mask[index] !== 1) {
|
|
1785
1928
|
continue;
|
|
1786
1929
|
}
|
|
1787
|
-
|
|
1788
|
-
|
|
1930
|
+
hash2 ^= index + 1;
|
|
1931
|
+
hash2 = Math.imul(hash2, 16777619);
|
|
1789
1932
|
}
|
|
1790
|
-
return (
|
|
1933
|
+
return (hash2 >>> 0).toString(36);
|
|
1791
1934
|
};
|
|
1792
1935
|
var isBetterRepresentativePocket = (candidate, existing, compare) => candidate.depth < existing.depth || candidate.depth === existing.depth && candidate.condition.length < existing.condition.length || candidate.depth === existing.depth && candidate.condition.length === existing.condition.length && compare(candidate, existing) < 0;
|
|
1793
1936
|
var estimateCombinationCount = (poolSize, maxDepth, maxCombinations) => {
|
|
@@ -2484,7 +2627,52 @@ var policyVariant = (spec, coreId, policy, id, jev) => {
|
|
|
2484
2627
|
|
|
2485
2628
|
// src/lib/strategyResearch/orchestrator.ts
|
|
2486
2629
|
var import_promises5 = __toESM(require("fs/promises"));
|
|
2487
|
-
|
|
2630
|
+
|
|
2631
|
+
// src/lib/researchMetricSnapshot.ts
|
|
2632
|
+
var import_node_crypto2 = require("crypto");
|
|
2633
|
+
var import_node_zlib = require("zlib");
|
|
2634
|
+
var MAX_BYTES = 256 * 1024 * 1024;
|
|
2635
|
+
var hash = (bytes) => (0, import_node_crypto2.createHash)("sha256").update(bytes).digest("hex");
|
|
2636
|
+
var validatePermanentResearchMetrics = (value) => {
|
|
2637
|
+
if (!value || typeof value !== "object") return;
|
|
2638
|
+
for (const [key, child] of Object.entries(value)) {
|
|
2639
|
+
if (["matchedPairs", "tradeRows", "allProjectedLeaves"].includes(key) && Array.isArray(child) && child.length)
|
|
2640
|
+
throw new Error(`Temporary payload ${key} is not a permanent metric`);
|
|
2641
|
+
if (key === "trades" && Array.isArray(child) && child.some(
|
|
2642
|
+
(row) => row && typeof row === "object" && ("signalId" in row || "entryTimestamp" in row)
|
|
2643
|
+
))
|
|
2644
|
+
throw new Error("Temporary trade records are not permanent metrics");
|
|
2645
|
+
validatePermanentResearchMetrics(child);
|
|
2646
|
+
}
|
|
2647
|
+
};
|
|
2648
|
+
var encodeResearchMetricSnapshot = (metrics) => {
|
|
2649
|
+
validatePermanentResearchMetrics(metrics);
|
|
2650
|
+
const bytes = Buffer.from(JSON.stringify(metrics));
|
|
2651
|
+
if (bytes.length > MAX_BYTES)
|
|
2652
|
+
throw new Error("Metric snapshot exceeds decoded size limit");
|
|
2653
|
+
return {
|
|
2654
|
+
schema: "tradejs-metric-snapshot/v1",
|
|
2655
|
+
encoding: "gzip+base64",
|
|
2656
|
+
decodedBytes: bytes.length,
|
|
2657
|
+
sha256: hash(bytes),
|
|
2658
|
+
payload: (0, import_node_zlib.gzipSync)(bytes, { level: 9 }).toString("base64")
|
|
2659
|
+
};
|
|
2660
|
+
};
|
|
2661
|
+
var decodeResearchMetricSnapshot = (snapshot) => {
|
|
2662
|
+
if (!snapshot || typeof snapshot !== "object" || !("schema" in snapshot) || snapshot.schema !== "tradejs-metric-snapshot/v1" || !("encoding" in snapshot) || snapshot.encoding !== "gzip+base64" || !("decodedBytes" in snapshot) || typeof snapshot.decodedBytes !== "number" || !Number.isSafeInteger(snapshot.decodedBytes) || snapshot.decodedBytes <= 0 || snapshot.decodedBytes > MAX_BYTES || !("sha256" in snapshot) || typeof snapshot.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(snapshot.sha256) || !("payload" in snapshot) || typeof snapshot.payload !== "string")
|
|
2663
|
+
throw new Error("Invalid metric snapshot envelope");
|
|
2664
|
+
const bytes = (0, import_node_zlib.gunzipSync)(Buffer.from(snapshot.payload, "base64"), {
|
|
2665
|
+
maxOutputLength: snapshot.decodedBytes
|
|
2666
|
+
});
|
|
2667
|
+
if (bytes.length !== snapshot.decodedBytes || hash(bytes) !== snapshot.sha256)
|
|
2668
|
+
throw new Error("Metric snapshot checksum or size mismatch");
|
|
2669
|
+
const metrics = JSON.parse(bytes.toString("utf8"));
|
|
2670
|
+
validatePermanentResearchMetrics(metrics);
|
|
2671
|
+
return metrics;
|
|
2672
|
+
};
|
|
2673
|
+
|
|
2674
|
+
// src/lib/strategyResearch/orchestrator.ts
|
|
2675
|
+
var import_node_path7 = __toESM(require("path"));
|
|
2488
2676
|
|
|
2489
2677
|
// src/lib/coreResearch/metrics.ts
|
|
2490
2678
|
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
@@ -3685,16 +3873,16 @@ var testCharts = (conclusion) => {
|
|
|
3685
3873
|
|
|
3686
3874
|
// src/lib/strategyResearch/retention.ts
|
|
3687
3875
|
var import_promises4 = __toESM(require("fs/promises"));
|
|
3688
|
-
var
|
|
3876
|
+
var import_node_path6 = __toESM(require("path"));
|
|
3689
3877
|
|
|
3690
3878
|
// src/lib/strategyRelease/researchRetention.ts
|
|
3691
3879
|
var import_promises3 = __toESM(require("fs/promises"));
|
|
3692
|
-
var
|
|
3693
|
-
var
|
|
3880
|
+
var import_node_path5 = __toESM(require("path"));
|
|
3881
|
+
var import_node_crypto4 = require("crypto");
|
|
3694
3882
|
var import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvidence");
|
|
3695
3883
|
|
|
3696
3884
|
// src/lib/strategyRelease/researchFileConsumers.ts
|
|
3697
|
-
var
|
|
3885
|
+
var import_node_path4 = __toESM(require("path"));
|
|
3698
3886
|
var import_node_child_process = require("child_process");
|
|
3699
3887
|
var import_node_util = require("util");
|
|
3700
3888
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
@@ -3725,15 +3913,15 @@ print(json.dumps(sorted(opened)))
|
|
|
3725
3913
|
`;
|
|
3726
3914
|
var assertNoResearchFileConsumers = async (files, run = runCommand) => {
|
|
3727
3915
|
const virtualPids = /* @__PURE__ */ new Set();
|
|
3728
|
-
const wanted = new Set(files.map((file) =>
|
|
3729
|
-
let commonRoot = files.length ?
|
|
3916
|
+
const wanted = new Set(files.map((file) => import_node_path4.default.resolve(file)));
|
|
3917
|
+
let commonRoot = files.length ? import_node_path4.default.dirname(files[0]) : "";
|
|
3730
3918
|
while (commonRoot && files.some((file) => {
|
|
3731
|
-
const relative =
|
|
3732
|
-
return relative === ".." || relative.startsWith(`..${
|
|
3919
|
+
const relative = import_node_path4.default.relative(commonRoot, file);
|
|
3920
|
+
return relative === ".." || relative.startsWith(`..${import_node_path4.default.sep}`) || import_node_path4.default.isAbsolute(relative);
|
|
3733
3921
|
}))
|
|
3734
|
-
commonRoot =
|
|
3922
|
+
commonRoot = import_node_path4.default.dirname(commonRoot);
|
|
3735
3923
|
const selectors = [];
|
|
3736
|
-
if (files.length > 1e3 && commonRoot !==
|
|
3924
|
+
if (files.length > 1e3 && commonRoot !== import_node_path4.default.parse(commonRoot).root) {
|
|
3737
3925
|
selectors.push(["+D", commonRoot]);
|
|
3738
3926
|
} else {
|
|
3739
3927
|
for (let offset = 0; offset < files.length; offset += 100)
|
|
@@ -3757,7 +3945,7 @@ var assertNoResearchFileConsumers = async (files, run = runCommand) => {
|
|
|
3757
3945
|
if (line.startsWith("c"))
|
|
3758
3946
|
virtual = line.startsWith("ccom.apple.Virtualization.");
|
|
3759
3947
|
if (line.startsWith("n")) {
|
|
3760
|
-
if (!wanted.has(
|
|
3948
|
+
if (!wanted.has(import_node_path4.default.resolve(line.slice(1)))) continue;
|
|
3761
3949
|
if (!virtual || !/^\d+$/.test(pid))
|
|
3762
3950
|
throw new Error(
|
|
3763
3951
|
`Research cleanup found open files: ${line.slice(1)}`
|
|
@@ -3791,9 +3979,9 @@ var assertNoResearchFileConsumers = async (files, run = runCommand) => {
|
|
|
3791
3979
|
for (const mount of container.Mounts) {
|
|
3792
3980
|
if (mount.Type !== "bind") continue;
|
|
3793
3981
|
for (const file of files) {
|
|
3794
|
-
const relative =
|
|
3795
|
-
if (relative === "" || !relative.startsWith(`..${
|
|
3796
|
-
guestFiles.add(
|
|
3982
|
+
const relative = import_node_path4.default.relative(mount.Source, file);
|
|
3983
|
+
if (relative === "" || !relative.startsWith(`..${import_node_path4.default.sep}`) && relative !== ".." && !import_node_path4.default.isAbsolute(relative)) {
|
|
3984
|
+
guestFiles.add(import_node_path4.default.posix.join(mount.Destination, relative));
|
|
3797
3985
|
covered.add(file);
|
|
3798
3986
|
}
|
|
3799
3987
|
}
|
|
@@ -3839,15 +4027,15 @@ var mapFiles = async (entries, visit) => {
|
|
|
3839
4027
|
if (worker.status === "rejected") throw worker.reason;
|
|
3840
4028
|
return results;
|
|
3841
4029
|
};
|
|
3842
|
-
var fileSha256 = async (file, bytes) => bytes <= 1024 * 1024 ? (0,
|
|
4030
|
+
var fileSha256 = async (file, bytes) => bytes <= 1024 * 1024 ? (0, import_node_crypto4.createHash)("sha256").update(await import_promises3.default.readFile(file)).digest("hex") : (0, import_strategyReleaseEvidence.strategyEvidenceFileSha256)(file);
|
|
3843
4031
|
var inside = (root, file) => {
|
|
3844
|
-
const relative =
|
|
3845
|
-
return relative !== "" && !relative.startsWith(`..${
|
|
4032
|
+
const relative = import_node_path5.default.relative(root, file);
|
|
4033
|
+
return relative !== "" && !relative.startsWith(`..${import_node_path5.default.sep}`) && relative !== ".." && !import_node_path5.default.isAbsolute(relative);
|
|
3846
4034
|
};
|
|
3847
4035
|
var applyResearchRetention = async (options) => {
|
|
3848
4036
|
const projectRoot = await import_promises3.default.realpath(options.projectRoot);
|
|
3849
|
-
const dataRoot =
|
|
3850
|
-
const resolve = (file) =>
|
|
4037
|
+
const dataRoot = import_node_path5.default.join(projectRoot, "data");
|
|
4038
|
+
const resolve = (file) => import_node_path5.default.resolve(projectRoot, file);
|
|
3851
4039
|
const receiptPath = resolve(options.receiptPath);
|
|
3852
4040
|
const journalPath = `${receiptPath}.deleted.jsonl`;
|
|
3853
4041
|
const permanent = new Set(
|
|
@@ -3883,13 +4071,27 @@ var applyResearchRetention = async (options) => {
|
|
|
3883
4071
|
const header = Buffer.alloc(4096);
|
|
3884
4072
|
const { bytesRead } = await handle.read(header, 0, header.length, 0);
|
|
3885
4073
|
const frontmatter = header.subarray(0, bytesRead).toString("utf8").match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
3886
|
-
if (!frontmatter || !/^schema:\s*tradejs-research\/
|
|
4074
|
+
if (!frontmatter || !/^schema:\s*tradejs-research\/v[12]\s*$/m.test(frontmatter[1]) || !(retired ? /^reproduction:\s*(?:complete|partial|blocked|legacy-partial)\s*$/m.test(
|
|
3887
4075
|
frontmatter[1]
|
|
3888
4076
|
) : /^reproduction:\s*complete\s*$/m.test(frontmatter[1]))) {
|
|
3889
4077
|
throw new Error(
|
|
3890
4078
|
`Research note must have reproduction: complete: ${file}`
|
|
3891
4079
|
);
|
|
3892
4080
|
}
|
|
4081
|
+
if (!retired && /^schema:\s*tradejs-research\/v2\s*$/m.test(frontmatter[1])) {
|
|
4082
|
+
if ((await import_promises3.default.stat(file)).size > 8 * 1024 * 1024)
|
|
4083
|
+
throw new Error("Compact permanent note exceeds 8 MiB");
|
|
4084
|
+
const content = await import_promises3.default.readFile(file, "utf8");
|
|
4085
|
+
const section = content.split("## Metrics snapshot (machine-readable)")[1]?.split("## Reported metrics")[0] ?? "";
|
|
4086
|
+
const blocks = [
|
|
4087
|
+
...section.matchAll(/```json\r?\n([\s\S]*?)\r?\n```/g)
|
|
4088
|
+
];
|
|
4089
|
+
if (blocks.length !== 1)
|
|
4090
|
+
throw new Error(
|
|
4091
|
+
"Complete v2 note needs one verified metric snapshot"
|
|
4092
|
+
);
|
|
4093
|
+
decodeResearchMetricSnapshot(JSON.parse(blocks[0][1]));
|
|
4094
|
+
}
|
|
3893
4095
|
} finally {
|
|
3894
4096
|
await handle.close();
|
|
3895
4097
|
}
|
|
@@ -3932,8 +4134,8 @@ var applyResearchRetention = async (options) => {
|
|
|
3932
4134
|
}))
|
|
3933
4135
|
};
|
|
3934
4136
|
if (!options.apply) return receipt;
|
|
3935
|
-
await import_promises3.default.mkdir(
|
|
3936
|
-
if (await import_promises3.default.realpath(
|
|
4137
|
+
await import_promises3.default.mkdir(import_node_path5.default.dirname(receiptPath), { recursive: true });
|
|
4138
|
+
if (await import_promises3.default.realpath(import_node_path5.default.dirname(receiptPath)) !== import_node_path5.default.dirname(receiptPath)) {
|
|
3937
4139
|
throw new Error("Cleanup receipt refuses symlinked directories");
|
|
3938
4140
|
}
|
|
3939
4141
|
const lock = await import_promises3.default.open(`${receiptPath}.lock`, "wx");
|
|
@@ -3979,17 +4181,17 @@ var createResearchRetentionInventory = async (projectRoot, directory, spec, stat
|
|
|
3979
4181
|
const permanentArtifacts = [];
|
|
3980
4182
|
for (const [role, file] of [
|
|
3981
4183
|
["note", notePath],
|
|
3982
|
-
["metrics",
|
|
3983
|
-
["candidates",
|
|
3984
|
-
["candidates",
|
|
3985
|
-
["ledger",
|
|
3986
|
-
["chart",
|
|
4184
|
+
["metrics", import_node_path6.default.join(directory, "result.json")],
|
|
4185
|
+
["candidates", import_node_path6.default.join(directory, "03-freeze-gates/result.json")],
|
|
4186
|
+
["candidates", import_node_path6.default.join(directory, "resolved-spec.json")],
|
|
4187
|
+
["ledger", import_node_path6.default.join(directory, "05-freeze-primary/result.json")],
|
|
4188
|
+
["chart", import_node_path6.default.join(directory, "report.html")],
|
|
3987
4189
|
...[
|
|
3988
4190
|
"source.patch",
|
|
3989
4191
|
"source-baseline.tar",
|
|
3990
4192
|
"source-untracked.json",
|
|
3991
4193
|
"source-jev-models.json"
|
|
3992
|
-
].map((file2) => ["source",
|
|
4194
|
+
].map((file2) => ["source", import_node_path6.default.join(directory, file2)])
|
|
3993
4195
|
]) {
|
|
3994
4196
|
try {
|
|
3995
4197
|
permanentArtifacts.push({
|
|
@@ -4003,11 +4205,11 @@ var createResearchRetentionInventory = async (projectRoot, directory, spec, stat
|
|
|
4003
4205
|
}
|
|
4004
4206
|
const files = /* @__PURE__ */ new Map();
|
|
4005
4207
|
for (const stage of Object.values(state.stages)) {
|
|
4006
|
-
for (const [relative,
|
|
4208
|
+
for (const [relative, hash2] of Object.entries(stage.artifacts)) {
|
|
4007
4209
|
const file = await boundResearchPath(directory, relative);
|
|
4008
4210
|
if (relative.endsWith("/result.json")) {
|
|
4009
4211
|
const result = await readJson(file);
|
|
4010
|
-
if (result?.dataset) files.set(file,
|
|
4212
|
+
if (result?.dataset) files.set(file, hash2);
|
|
4011
4213
|
}
|
|
4012
4214
|
if (relative.endsWith("/sources.json") || relative.endsWith("/payloads.json")) {
|
|
4013
4215
|
const sources = await readJson(file);
|
|
@@ -4024,13 +4226,13 @@ var createResearchRetentionInventory = async (projectRoot, directory, spec, stat
|
|
|
4024
4226
|
}
|
|
4025
4227
|
}
|
|
4026
4228
|
const entries = [];
|
|
4027
|
-
for (const [file,
|
|
4229
|
+
for (const [file, hash2] of files) {
|
|
4028
4230
|
const stat = await import_promises4.default.stat(file);
|
|
4029
4231
|
entries.push({
|
|
4030
4232
|
path: file,
|
|
4031
4233
|
kind: "research_payload",
|
|
4032
4234
|
researchId: spec.core.researchId,
|
|
4033
|
-
sha256:
|
|
4235
|
+
sha256: hash2,
|
|
4034
4236
|
bytes: stat.size,
|
|
4035
4237
|
createdAt: stat.birthtimeMs,
|
|
4036
4238
|
verified: true,
|
|
@@ -4049,13 +4251,13 @@ var createResearchRetentionInventory = async (projectRoot, directory, spec, stat
|
|
|
4049
4251
|
};
|
|
4050
4252
|
var applyRetentionInventory = async (projectRoot, directory) => {
|
|
4051
4253
|
const inventory = await readJson(
|
|
4052
|
-
|
|
4254
|
+
import_node_path6.default.join(directory, "retention.json")
|
|
4053
4255
|
);
|
|
4054
|
-
const receipts =
|
|
4256
|
+
const receipts = import_node_path6.default.join(directory, "retention");
|
|
4055
4257
|
await import_promises4.default.mkdir(receipts, { recursive: true });
|
|
4056
4258
|
for (let index = 0; index < inventory.entries.length; index++) {
|
|
4057
4259
|
const entry = inventory.entries[index];
|
|
4058
|
-
const receiptPath =
|
|
4260
|
+
const receiptPath = import_node_path6.default.join(receipts, `${index}.json`);
|
|
4059
4261
|
let receipt;
|
|
4060
4262
|
try {
|
|
4061
4263
|
receipt = await readJson(receiptPath);
|
|
@@ -4091,13 +4293,13 @@ var applyRetentionInventory = async (projectRoot, directory) => {
|
|
|
4091
4293
|
entries: [entry],
|
|
4092
4294
|
receiptPath,
|
|
4093
4295
|
apply: true,
|
|
4094
|
-
protectedPaths: [
|
|
4296
|
+
protectedPaths: [import_node_path6.default.join(directory, "retention.json")]
|
|
4095
4297
|
});
|
|
4096
4298
|
}
|
|
4097
4299
|
return { status: "completed", files: inventory.entries.length };
|
|
4098
4300
|
};
|
|
4099
4301
|
var finishResearchRetention = async (projectRoot, directory) => {
|
|
4100
|
-
const statusPath =
|
|
4302
|
+
const statusPath = import_node_path6.default.join(directory, "retention-status.json");
|
|
4101
4303
|
try {
|
|
4102
4304
|
const result = await applyRetentionInventory(projectRoot, directory);
|
|
4103
4305
|
await writeJsonAtomic(statusPath, result);
|
|
@@ -4124,7 +4326,7 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4124
4326
|
const directory = researchDirectory(projectRoot, spec);
|
|
4125
4327
|
const lineage = await backend.lineage();
|
|
4126
4328
|
const state = await initializeResearchState(directory, spec, lineage);
|
|
4127
|
-
const statePath =
|
|
4329
|
+
const statePath = import_node_path7.default.join(directory, "state.json");
|
|
4128
4330
|
const elapsedBefore = state.elapsedMs ?? 0;
|
|
4129
4331
|
const activeStart = Date.now();
|
|
4130
4332
|
const elapsed = () => elapsedBefore + Date.now() - activeStart;
|
|
@@ -4134,7 +4336,7 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4134
4336
|
directory,
|
|
4135
4337
|
state,
|
|
4136
4338
|
conclusion: await readJson(
|
|
4137
|
-
|
|
4339
|
+
import_node_path7.default.join(directory, "result.json")
|
|
4138
4340
|
)
|
|
4139
4341
|
};
|
|
4140
4342
|
}
|
|
@@ -4144,13 +4346,13 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4144
4346
|
return writeJsonAtomic(statePath, state);
|
|
4145
4347
|
};
|
|
4146
4348
|
const event = async (stage2, status) => import_promises5.default.appendFile(
|
|
4147
|
-
|
|
4349
|
+
import_node_path7.default.join(directory, "events.jsonl"),
|
|
4148
4350
|
`${JSON.stringify({ at: (/* @__PURE__ */ new Date()).toISOString(), stage: stage2, status, specSha256: state.specSha256 })}
|
|
4149
4351
|
`
|
|
4150
4352
|
);
|
|
4151
4353
|
const stage = async (name, work) => {
|
|
4152
4354
|
const stageDirectory = await boundResearchPath(directory, name);
|
|
4153
|
-
const resultPath =
|
|
4355
|
+
const resultPath = import_node_path7.default.join(stageDirectory, "result.json");
|
|
4154
4356
|
if (state.stages[name]?.status === "completed")
|
|
4155
4357
|
return readJson(resultPath);
|
|
4156
4358
|
if (elapsed() > spec.budget.maxMinutes * 6e4)
|
|
@@ -4169,7 +4371,7 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4169
4371
|
await save();
|
|
4170
4372
|
await event(name, "started");
|
|
4171
4373
|
await import_promises5.default.writeFile(
|
|
4172
|
-
|
|
4374
|
+
import_node_path7.default.join(directory, "report.html"),
|
|
4173
4375
|
buildStrategyResearchReport({ spec, state, diagnostics })
|
|
4174
4376
|
);
|
|
4175
4377
|
try {
|
|
@@ -4177,7 +4379,7 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4177
4379
|
await writeJsonAtomic(resultPath, result);
|
|
4178
4380
|
const artifacts = {};
|
|
4179
4381
|
for (const file of await import_promises5.default.readdir(stageDirectory)) {
|
|
4180
|
-
const full =
|
|
4382
|
+
const full = import_node_path7.default.join(stageDirectory, file);
|
|
4181
4383
|
if ((await import_promises5.default.stat(full)).isFile())
|
|
4182
4384
|
artifacts[`${name}/${file}`] = await sha256File(full);
|
|
4183
4385
|
}
|
|
@@ -4189,7 +4391,7 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4189
4391
|
"source-jev-models.json"
|
|
4190
4392
|
]) {
|
|
4191
4393
|
try {
|
|
4192
|
-
artifacts[file] = await sha256File(
|
|
4394
|
+
artifacts[file] = await sha256File(import_node_path7.default.join(directory, file));
|
|
4193
4395
|
} catch (error) {
|
|
4194
4396
|
if (error.code !== "ENOENT")
|
|
4195
4397
|
throw error;
|
|
@@ -4222,14 +4424,14 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4222
4424
|
let previousRunId;
|
|
4223
4425
|
try {
|
|
4224
4426
|
previousRunId = (await readJson(
|
|
4225
|
-
|
|
4427
|
+
import_node_path7.default.join(folder, "native-run.json")
|
|
4226
4428
|
)).runId;
|
|
4227
4429
|
} catch (error) {
|
|
4228
4430
|
if (error.code !== "ENOENT") throw error;
|
|
4229
4431
|
}
|
|
4230
4432
|
if (!previousRunId) {
|
|
4231
4433
|
try {
|
|
4232
|
-
if ((await import_promises5.default.stat(
|
|
4434
|
+
if ((await import_promises5.default.stat(import_node_path7.default.join(folder, "backtest.log"))).size)
|
|
4233
4435
|
throw new Error(
|
|
4234
4436
|
"Previous native attempt has no recorded run identity; inspect its log before another launch"
|
|
4235
4437
|
);
|
|
@@ -4250,7 +4452,7 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4250
4452
|
},
|
|
4251
4453
|
variant,
|
|
4252
4454
|
folder,
|
|
4253
|
-
async (runId) => writeJsonAtomic(
|
|
4455
|
+
async (runId) => writeJsonAtomic(import_node_path7.default.join(folder, "native-run.json"), { runId }),
|
|
4254
4456
|
previousRunId,
|
|
4255
4457
|
confirmation
|
|
4256
4458
|
);
|
|
@@ -4265,15 +4467,15 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4265
4467
|
try {
|
|
4266
4468
|
const inventory = await stage("01-preflight", async () => {
|
|
4267
4469
|
const prior = [];
|
|
4268
|
-
const root =
|
|
4470
|
+
const root = import_node_path7.default.dirname(directory);
|
|
4269
4471
|
for (const id of await import_promises5.default.readdir(root)) {
|
|
4270
4472
|
if (id === spec.core.researchId || id.startsWith(".")) continue;
|
|
4271
4473
|
try {
|
|
4272
4474
|
const previous = await readJson(
|
|
4273
|
-
|
|
4475
|
+
import_node_path7.default.join(root, id, "spec.json")
|
|
4274
4476
|
);
|
|
4275
4477
|
const previousState = await readJson(
|
|
4276
|
-
|
|
4478
|
+
import_node_path7.default.join(root, id, "state.json")
|
|
4277
4479
|
);
|
|
4278
4480
|
const overlap = previous.core.strategy === spec.core.strategy && previous.core.window.start < spec.core.window.end && previous.core.window.end > (spec.testSince || spec.core.window.start);
|
|
4279
4481
|
if (overlap && previousState.holdoutOpenedAt)
|
|
@@ -4299,7 +4501,7 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4299
4501
|
let previous;
|
|
4300
4502
|
try {
|
|
4301
4503
|
previous = (await readJson(
|
|
4302
|
-
|
|
4504
|
+
import_node_path7.default.join(folder, "native-run.json")
|
|
4303
4505
|
)).runId;
|
|
4304
4506
|
} catch (error) {
|
|
4305
4507
|
if (error.code !== "ENOENT")
|
|
@@ -4309,7 +4511,7 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4309
4511
|
spec,
|
|
4310
4512
|
control2,
|
|
4311
4513
|
folder,
|
|
4312
|
-
async (runId) => writeJsonAtomic(
|
|
4514
|
+
async (runId) => writeJsonAtomic(import_node_path7.default.join(folder, "native-run.json"), {
|
|
4313
4515
|
runId
|
|
4314
4516
|
}),
|
|
4315
4517
|
previous
|
|
@@ -4323,7 +4525,7 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4323
4525
|
);
|
|
4324
4526
|
spec = { ...spec, testSince: partition.testSince };
|
|
4325
4527
|
}
|
|
4326
|
-
await writeJsonAtomic(
|
|
4528
|
+
await writeJsonAtomic(import_node_path7.default.join(directory, "resolved-spec.json"), spec);
|
|
4327
4529
|
let gates = [];
|
|
4328
4530
|
let primaryId = null;
|
|
4329
4531
|
let frozenSelection;
|
|
@@ -4382,10 +4584,10 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4382
4584
|
core: { ...spec.core, researchId: spec.frozenFrom }
|
|
4383
4585
|
});
|
|
4384
4586
|
const parentState = await readJson(
|
|
4385
|
-
|
|
4587
|
+
import_node_path7.default.join(parentDirectory, "state.json")
|
|
4386
4588
|
);
|
|
4387
4589
|
const parentSpec = await readJson(
|
|
4388
|
-
|
|
4590
|
+
import_node_path7.default.join(parentDirectory, "spec.json")
|
|
4389
4591
|
);
|
|
4390
4592
|
const { verifyCompletedResearch: verifyCompletedResearch2 } = await Promise.resolve().then(() => (init_io2(), io_exports));
|
|
4391
4593
|
await verifyCompletedResearch2(parentDirectory, parentState);
|
|
@@ -4412,10 +4614,10 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4412
4614
|
"Frozen revalidation requires complete, unchanged rules/runtime and a strictly new period"
|
|
4413
4615
|
);
|
|
4414
4616
|
gates = await readJson(
|
|
4415
|
-
|
|
4617
|
+
import_node_path7.default.join(parentDirectory, "03-freeze-gates/result.json")
|
|
4416
4618
|
);
|
|
4417
4619
|
frozenSelection = await readJson(
|
|
4418
|
-
|
|
4620
|
+
import_node_path7.default.join(parentDirectory, "05-freeze-primary/result.json")
|
|
4419
4621
|
);
|
|
4420
4622
|
if (frozenSelection.schema !== "tradejs-research-selection/v1" || !Array.isArray(frozenSelection.compositions))
|
|
4421
4623
|
throw new Error(
|
|
@@ -4544,7 +4746,7 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4544
4746
|
`06-raw-metrics-${variant.id}`,
|
|
4545
4747
|
async (folder) => {
|
|
4546
4748
|
const resolved = spec.frozenFrom ? (await run(spec, variant, `06-raw-revalidation-${variant.id}`)).variant : (await readJson(
|
|
4547
|
-
|
|
4749
|
+
import_node_path7.default.join(directory, `02-raw-${variant.id}`, "result.json")
|
|
4548
4750
|
)).variant;
|
|
4549
4751
|
const dataset = await backend.loadDataset(
|
|
4550
4752
|
spec,
|
|
@@ -4721,7 +4923,7 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4721
4923
|
for (const name of Object.keys(state.stages)) {
|
|
4722
4924
|
try {
|
|
4723
4925
|
const result = await readJson(
|
|
4724
|
-
|
|
4926
|
+
import_node_path7.default.join(directory, name, "result.json")
|
|
4725
4927
|
);
|
|
4726
4928
|
if (result?.variant)
|
|
4727
4929
|
executions.push({ stage: name, ...result.variant });
|
|
@@ -4729,7 +4931,7 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4729
4931
|
if (error.code !== "ENOENT") throw error;
|
|
4730
4932
|
}
|
|
4731
4933
|
}
|
|
4732
|
-
await writeJsonAtomic(
|
|
4934
|
+
await writeJsonAtomic(import_node_path7.default.join(directory, "result.json"), {
|
|
4733
4935
|
...conclusion,
|
|
4734
4936
|
executions,
|
|
4735
4937
|
rawCore: Object.entries(rawReports).map(([id, windows]) => ({
|
|
@@ -4743,7 +4945,7 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4743
4945
|
delete state.error;
|
|
4744
4946
|
await save();
|
|
4745
4947
|
await import_promises5.default.writeFile(
|
|
4746
|
-
|
|
4948
|
+
import_node_path7.default.join(directory, "report.html"),
|
|
4747
4949
|
buildStrategyResearchReport({
|
|
4748
4950
|
spec,
|
|
4749
4951
|
state,
|
|
@@ -4774,7 +4976,7 @@ var runStrategyResearch = async (parameters) => {
|
|
|
4774
4976
|
)
|
|
4775
4977
|
};
|
|
4776
4978
|
const note = `---
|
|
4777
|
-
schema: tradejs-research/
|
|
4979
|
+
schema: tradejs-research/v2
|
|
4778
4980
|
strategy: '${spec.core.strategy}'
|
|
4779
4981
|
date: '${spec.core.createdAt.slice(0, 10)}'
|
|
4780
4982
|
kind: 'backtest'
|
|
@@ -4807,7 +5009,7 @@ ${JSON.stringify(spec, null, 2)}
|
|
|
4807
5009
|
## Metrics snapshot (machine-readable)
|
|
4808
5010
|
|
|
4809
5011
|
\`\`\`json
|
|
4810
|
-
${JSON.stringify(compact
|
|
5012
|
+
${JSON.stringify(encodeResearchMetricSnapshot(compact))}
|
|
4811
5013
|
\`\`\`
|
|
4812
5014
|
|
|
4813
5015
|
## Reported metrics
|
|
@@ -4826,8 +5028,12 @@ ${directory}/report.html
|
|
|
4826
5028
|
|
|
4827
5029
|
${conclusion.nextAction} Current-snapshot universe; no deployment authorization. Temporary payloads are deleted only after the permanent record is verified; new trade-level questions require a frozen rerun.
|
|
4828
5030
|
`;
|
|
5031
|
+
if (Buffer.byteLength(note) > 8 * 1024 * 1024)
|
|
5032
|
+
throw new Error(
|
|
5033
|
+
"Compact permanent note exceeds 8 MiB; archive source separately, never truncate metrics"
|
|
5034
|
+
);
|
|
4829
5035
|
await import_promises5.default.writeFile(
|
|
4830
|
-
|
|
5036
|
+
import_node_path7.default.join(
|
|
4831
5037
|
noteDirectory,
|
|
4832
5038
|
`${spec.core.createdAt.slice(0, 10)}-${spec.core.researchId}.md`
|
|
4833
5039
|
),
|
|
@@ -4836,7 +5042,7 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
|
|
|
4836
5042
|
).catch(async (error) => {
|
|
4837
5043
|
if (error.code !== "EEXIST") throw error;
|
|
4838
5044
|
const existing = await import_promises5.default.readFile(
|
|
4839
|
-
|
|
5045
|
+
import_node_path7.default.join(
|
|
4840
5046
|
noteDirectory,
|
|
4841
5047
|
`${spec.core.createdAt.slice(0, 10)}-${spec.core.researchId}.md`
|
|
4842
5048
|
),
|
|
@@ -4848,19 +5054,19 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
|
|
|
4848
5054
|
);
|
|
4849
5055
|
});
|
|
4850
5056
|
await writeJsonAtomic(
|
|
4851
|
-
|
|
5057
|
+
import_node_path7.default.join(directory, "retention.json"),
|
|
4852
5058
|
await createResearchRetentionInventory(
|
|
4853
5059
|
projectRoot,
|
|
4854
5060
|
directory,
|
|
4855
5061
|
spec,
|
|
4856
5062
|
state,
|
|
4857
|
-
|
|
5063
|
+
import_node_path7.default.join(
|
|
4858
5064
|
noteDirectory,
|
|
4859
5065
|
`${spec.core.createdAt.slice(0, 10)}-${spec.core.researchId}.md`
|
|
4860
5066
|
)
|
|
4861
5067
|
)
|
|
4862
5068
|
);
|
|
4863
|
-
await writeJsonAtomic(
|
|
5069
|
+
await writeJsonAtomic(import_node_path7.default.join(directory, "manifest.json"), {
|
|
4864
5070
|
schema: "tradejs-strategy-research-manifest/v1",
|
|
4865
5071
|
specSha256: state.specSha256,
|
|
4866
5072
|
artifacts: Object.fromEntries(
|
|
@@ -4874,7 +5080,7 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
|
|
|
4874
5080
|
"retention.json"
|
|
4875
5081
|
].map(async (file) => [
|
|
4876
5082
|
file,
|
|
4877
|
-
await sha256File(
|
|
5083
|
+
await sha256File(import_node_path7.default.join(directory, file))
|
|
4878
5084
|
])
|
|
4879
5085
|
)
|
|
4880
5086
|
)
|
|
@@ -4887,7 +5093,7 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
|
|
|
4887
5093
|
);
|
|
4888
5094
|
}
|
|
4889
5095
|
parameters.onProgress?.(
|
|
4890
|
-
`\u0418\u0442\u043E\u0433: ${conclusion.status}. \u041E\u0442\u0447\u0451\u0442: ${
|
|
5096
|
+
`\u0418\u0442\u043E\u0433: ${conclusion.status}. \u041E\u0442\u0447\u0451\u0442: ${import_node_path7.default.join(directory, "report.html")}`
|
|
4891
5097
|
);
|
|
4892
5098
|
return { directory, state, conclusion };
|
|
4893
5099
|
} catch (error) {
|
|
@@ -4908,9 +5114,9 @@ ${conclusion.nextAction} Current-snapshot universe; no deployment authorization.
|
|
|
4908
5114
|
frozenGates: 0
|
|
4909
5115
|
}
|
|
4910
5116
|
};
|
|
4911
|
-
await writeJsonAtomic(
|
|
5117
|
+
await writeJsonAtomic(import_node_path7.default.join(directory, "result.json"), conclusion);
|
|
4912
5118
|
await import_promises5.default.writeFile(
|
|
4913
|
-
|
|
5119
|
+
import_node_path7.default.join(directory, "report.html"),
|
|
4914
5120
|
buildStrategyResearchReport({ spec, state, conclusion, diagnostics })
|
|
4915
5121
|
);
|
|
4916
5122
|
throw error;
|
|
@@ -4933,10 +5139,10 @@ var import_registry = require("@tradejs/node/registry");
|
|
|
4933
5139
|
var import_connectors = require("@tradejs/node/connectors");
|
|
4934
5140
|
|
|
4935
5141
|
// src/lib/coreResearch/dataset.ts
|
|
4936
|
-
var
|
|
4937
|
-
var
|
|
4938
|
-
var
|
|
4939
|
-
var
|
|
5142
|
+
var import_node_crypto5 = require("crypto");
|
|
5143
|
+
var import_node_fs3 = require("fs");
|
|
5144
|
+
var import_node_path8 = __toESM(require("path"));
|
|
5145
|
+
var import_node_readline2 = __toESM(require("readline"));
|
|
4940
5146
|
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
4941
5147
|
var finiteNumber = (value, field) => {
|
|
4942
5148
|
const numeric = Number(value);
|
|
@@ -5146,11 +5352,11 @@ var readCoreResearchVariant = async (variant) => {
|
|
|
5146
5352
|
const seen = /* @__PURE__ */ new Map();
|
|
5147
5353
|
let duplicateRowsDropped = 0;
|
|
5148
5354
|
for (const inputPath of variant.files) {
|
|
5149
|
-
const filePath =
|
|
5150
|
-
const input = (0,
|
|
5151
|
-
const
|
|
5152
|
-
input.on("data", (chunk) =>
|
|
5153
|
-
const lines =
|
|
5355
|
+
const filePath = import_node_path8.default.resolve(inputPath);
|
|
5356
|
+
const input = (0, import_node_fs3.createReadStream)(filePath);
|
|
5357
|
+
const hash2 = (0, import_node_crypto5.createHash)("sha256");
|
|
5358
|
+
input.on("data", (chunk) => hash2.update(chunk));
|
|
5359
|
+
const lines = import_node_readline2.default.createInterface({ input, crlfDelay: Infinity });
|
|
5154
5360
|
const fileTrades = [];
|
|
5155
5361
|
let lineNumber = 0;
|
|
5156
5362
|
let rows = 0;
|
|
@@ -5199,7 +5405,7 @@ var readCoreResearchVariant = async (variant) => {
|
|
|
5199
5405
|
trades.push(trade);
|
|
5200
5406
|
fileTrades.push(trade);
|
|
5201
5407
|
}
|
|
5202
|
-
const fileSha2562 =
|
|
5408
|
+
const fileSha2562 = hash2.digest("hex");
|
|
5203
5409
|
for (const trade of fileTrades) trade.sourceSha256 = fileSha2562;
|
|
5204
5410
|
sources.push({
|
|
5205
5411
|
path: filePath,
|
|
@@ -5219,7 +5425,7 @@ var readCoreResearchVariant = async (variant) => {
|
|
|
5219
5425
|
};
|
|
5220
5426
|
|
|
5221
5427
|
// src/lib/backtest/checkpoint.ts
|
|
5222
|
-
var
|
|
5428
|
+
var import_node_crypto6 = require("crypto");
|
|
5223
5429
|
var import_constants = require("@tradejs/core/constants");
|
|
5224
5430
|
var import_backtest2 = require("@tradejs/core/backtest");
|
|
5225
5431
|
var import_redis = require("@tradejs/infra/redis");
|
|
@@ -5419,6 +5625,7 @@ var extractSignalFromAiDatasetRow = (row) => {
|
|
|
5419
5625
|
const { payload } = row;
|
|
5420
5626
|
return {
|
|
5421
5627
|
...payload.signal,
|
|
5628
|
+
aiFeatureReplay: { snapshot: payload.featureSnapshot ?? null },
|
|
5422
5629
|
strategy: payload.signal.strategy,
|
|
5423
5630
|
figures: payload.figures ?? {},
|
|
5424
5631
|
indicators: payload.indicators ?? {},
|
|
@@ -5432,9 +5639,9 @@ init_io2();
|
|
|
5432
5639
|
|
|
5433
5640
|
// src/lib/aiTrainResearch.ts
|
|
5434
5641
|
var import_node_child_process2 = require("child_process");
|
|
5435
|
-
var
|
|
5642
|
+
var import_node_crypto8 = require("crypto");
|
|
5436
5643
|
var import_promises7 = __toESM(require("fs/promises"));
|
|
5437
|
-
var
|
|
5644
|
+
var import_node_path10 = __toESM(require("path"));
|
|
5438
5645
|
|
|
5439
5646
|
// src/lib/aiTrainMetrics.ts
|
|
5440
5647
|
var DAY_MS3 = 24 * 60 * 60 * 1e3;
|
|
@@ -5444,10 +5651,10 @@ var import_strategies = require("@tradejs/node/strategies");
|
|
|
5444
5651
|
var import_types2 = require("@tradejs/types");
|
|
5445
5652
|
|
|
5446
5653
|
// src/lib/strategyGateFingerprint.ts
|
|
5447
|
-
var
|
|
5654
|
+
var import_node_crypto7 = require("crypto");
|
|
5448
5655
|
var import_node_module = require("module");
|
|
5449
5656
|
var import_promises6 = __toESM(require("fs/promises"));
|
|
5450
|
-
var
|
|
5657
|
+
var import_node_path9 = __toESM(require("path"));
|
|
5451
5658
|
|
|
5452
5659
|
// src/lib/aiTrainResearch.ts
|
|
5453
5660
|
var DAY_MS4 = 24 * 60 * 60 * 1e3;
|
|
@@ -5457,6 +5664,8 @@ var RESEARCH_CONTEXT_ENV_KEYS = [
|
|
|
5457
5664
|
"INTERVAL",
|
|
5458
5665
|
"DERIVATIVES_CONTEXT_ENABLED",
|
|
5459
5666
|
"DERIVATIVES_CONTEXT_TARGET_ENABLED",
|
|
5667
|
+
"TICKER_PROFILES_PROVIDER",
|
|
5668
|
+
"TICKER_PROFILES_UNIVERSE",
|
|
5460
5669
|
"DERIVATIVES_CONTEXT_LOOKBACK_HOURS",
|
|
5461
5670
|
"DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS",
|
|
5462
5671
|
"DERIVATIVES_CONTEXT_EXCHANGE_PRIORITY",
|
|
@@ -5486,7 +5695,7 @@ var normalizeForStableJson = (value) => {
|
|
|
5486
5695
|
}
|
|
5487
5696
|
return value;
|
|
5488
5697
|
};
|
|
5489
|
-
var fingerprintResearchValue = (value) => (0,
|
|
5698
|
+
var fingerprintResearchValue = (value) => (0, import_node_crypto8.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
|
|
5490
5699
|
var fingerprintResearchContextEnvironment = (env = process.env) => fingerprintResearchValue(
|
|
5491
5700
|
Object.fromEntries(
|
|
5492
5701
|
RESEARCH_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
|
|
@@ -5496,81 +5705,7 @@ var fingerprintResearchContextEnvironment = (env = process.env) => fingerprintRe
|
|
|
5496
5705
|
// src/lib/strategyResearch/native.ts
|
|
5497
5706
|
var import_data = require("@tradejs/core/data");
|
|
5498
5707
|
var import_coreResearch = require("@tradejs/infra/coreResearch");
|
|
5499
|
-
|
|
5500
|
-
// src/lib/coreResearch/trace.ts
|
|
5501
|
-
var import_node_fs3 = require("fs");
|
|
5502
|
-
var import_node_readline2 = __toESM(require("readline"));
|
|
5503
|
-
var import_node_path10 = __toESM(require("path"));
|
|
5504
|
-
var summarizeCoreResearchTrace = async (filePaths = []) => {
|
|
5505
|
-
const events = /* @__PURE__ */ Object.create(null);
|
|
5506
|
-
const skipCounts = /* @__PURE__ */ Object.create(null);
|
|
5507
|
-
const exitCodes = /* @__PURE__ */ Object.create(null);
|
|
5508
|
-
const policyRejections = {};
|
|
5509
|
-
const policyExamples = [];
|
|
5510
|
-
for (const inputPath of filePaths) {
|
|
5511
|
-
const filePath = import_node_path10.default.resolve(inputPath);
|
|
5512
|
-
const input = (0, import_node_fs3.createReadStream)(filePath);
|
|
5513
|
-
const lines = import_node_readline2.default.createInterface({ input, crlfDelay: Infinity });
|
|
5514
|
-
let index = -1;
|
|
5515
|
-
try {
|
|
5516
|
-
for await (const line of lines) {
|
|
5517
|
-
index++;
|
|
5518
|
-
if (!line.trim()) continue;
|
|
5519
|
-
let event;
|
|
5520
|
-
try {
|
|
5521
|
-
event = JSON.parse(line);
|
|
5522
|
-
} catch (error) {
|
|
5523
|
-
throw new Error(
|
|
5524
|
-
`${filePath}:${index + 1} contains invalid trace JSON: ${String(error)}`
|
|
5525
|
-
);
|
|
5526
|
-
}
|
|
5527
|
-
events[event.event] = (events[event.event] ?? 0) + 1;
|
|
5528
|
-
if (event.event === "skip_summary") {
|
|
5529
|
-
for (const [reason, count] of Object.entries(event.skipCounts)) {
|
|
5530
|
-
skipCounts[reason] = (skipCounts[reason] ?? 0) + count;
|
|
5531
|
-
}
|
|
5532
|
-
}
|
|
5533
|
-
if (event.event === "position_exited" && event.exitCode) {
|
|
5534
|
-
exitCodes[event.exitCode] = (exitCodes[event.exitCode] ?? 0) + 1;
|
|
5535
|
-
}
|
|
5536
|
-
if ("researchEntryDecision" in event && event.researchEntryDecision && !event.researchEntryDecision.allowed) {
|
|
5537
|
-
const reason = event.researchEntryDecision.reason;
|
|
5538
|
-
policyRejections[reason] = (policyRejections[reason] ?? 0) + 1;
|
|
5539
|
-
if (policyExamples.length < 20)
|
|
5540
|
-
policyExamples.push({
|
|
5541
|
-
timestamp: event.timestamp,
|
|
5542
|
-
symbol: event.symbol,
|
|
5543
|
-
decision: event.researchEntryDecision
|
|
5544
|
-
});
|
|
5545
|
-
}
|
|
5546
|
-
}
|
|
5547
|
-
} finally {
|
|
5548
|
-
lines.close();
|
|
5549
|
-
input.destroy();
|
|
5550
|
-
}
|
|
5551
|
-
}
|
|
5552
|
-
return {
|
|
5553
|
-
events: Object.fromEntries(Object.entries(events).sort()),
|
|
5554
|
-
skipCounts: Object.fromEntries(
|
|
5555
|
-
Object.entries(skipCounts).sort(
|
|
5556
|
-
([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
|
|
5557
|
-
)
|
|
5558
|
-
),
|
|
5559
|
-
exitCodes: Object.fromEntries(
|
|
5560
|
-
Object.entries(exitCodes).sort(
|
|
5561
|
-
([leftKey, leftValue], [rightKey, rightValue]) => rightValue - leftValue || (leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0)
|
|
5562
|
-
)
|
|
5563
|
-
),
|
|
5564
|
-
...policyExamples.length ? {
|
|
5565
|
-
researchPolicy: {
|
|
5566
|
-
rejections: policyRejections,
|
|
5567
|
-
examples: policyExamples
|
|
5568
|
-
}
|
|
5569
|
-
} : {}
|
|
5570
|
-
};
|
|
5571
|
-
};
|
|
5572
|
-
|
|
5573
|
-
// src/lib/strategyResearch/native.ts
|
|
5708
|
+
init_trace();
|
|
5574
5709
|
var import_jev4 = require("@tradejs/core/jev");
|
|
5575
5710
|
|
|
5576
5711
|
// src/lib/backtest/jevOptions.ts
|