@tradejs/cli 2.0.19 → 2.0.21

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.
@@ -22,9 +22,96 @@ var aiPocketSearchCli_exports = {};
22
22
  __export(aiPocketSearchCli_exports, {
23
23
  AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS: () => AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS,
24
24
  readAiPocketSearchCliOption: () => readAiPocketSearchCliOption,
25
+ resolveAiPocketCadenceProfile: () => resolveAiPocketCadenceProfile,
26
+ sealAiPocketTestPartition: () => sealAiPocketTestPartition,
27
+ splitAiPocketCoverageRowsByTimestamp: () => splitAiPocketCoverageRowsByTimestamp,
25
28
  splitAiPocketResearchRowsByTimestamp: () => splitAiPocketResearchRowsByTimestamp
26
29
  });
27
30
  module.exports = __toCommonJS(aiPocketSearchCli_exports);
31
+ var DAY_MS = 24 * 60 * 60 * 1e3;
32
+ var DEFAULT_MIN_SUPPORT = 20;
33
+ var LOW_CADENCE_EVENT_LIMIT = 200;
34
+ var LOW_CADENCE_MAX_EVENT_SHARE = 1 / 3;
35
+ var countIndependentEvents = (rows) => {
36
+ const timestampEvents = /* @__PURE__ */ new Set();
37
+ let rowsWithoutTimestamp = 0;
38
+ for (const row of rows) {
39
+ if (typeof row.timestamp === "number" && Number.isFinite(row.timestamp)) {
40
+ timestampEvents.add(row.timestamp);
41
+ } else {
42
+ rowsWithoutTimestamp += 1;
43
+ }
44
+ }
45
+ return timestampEvents.size + rowsWithoutTimestamp;
46
+ };
47
+ var getPeriodDays = (rows) => {
48
+ let minTimestamp = null;
49
+ let maxTimestamp = null;
50
+ for (const row of rows) {
51
+ const timestamp = row.timestamp;
52
+ if (typeof timestamp !== "number" || !Number.isFinite(timestamp)) {
53
+ continue;
54
+ }
55
+ minTimestamp = minTimestamp == null ? timestamp : Math.min(minTimestamp, timestamp);
56
+ maxTimestamp = maxTimestamp == null ? timestamp : Math.max(maxTimestamp, timestamp);
57
+ }
58
+ if (minTimestamp == null || maxTimestamp == null) {
59
+ return null;
60
+ }
61
+ return Math.max((maxTimestamp - minTimestamp) / DAY_MS, 1);
62
+ };
63
+ var clampInt = (value, minimum, maximum) => Math.max(minimum, Math.min(maximum, Math.ceil(value)));
64
+ var resolveAiPocketCadenceProfile = ({
65
+ trainRows,
66
+ validationRows = [],
67
+ testRows = [],
68
+ mode = "auto",
69
+ validationSplit = 0.25,
70
+ minSupport,
71
+ minEvents,
72
+ minValidationSupport,
73
+ minValidationEvents,
74
+ maxEventCountShare = 0.25,
75
+ explicitMaxEventCountShare = false
76
+ }) => {
77
+ const trainEvents = countIndependentEvents(trainRows);
78
+ const validationEvents = countIndependentEvents(validationRows);
79
+ const testEvents = countIndependentEvents(testRows);
80
+ const trainPeriodDays = getPeriodDays(trainRows);
81
+ const trainEventsPerDay = trainPeriodDays == null ? null : trainEvents / trainPeriodDays;
82
+ const sparseSample = trainEvents < LOW_CADENCE_EVENT_LIMIT;
83
+ const lowCadence = trainEventsPerDay != null && trainEventsPerDay < 0.25;
84
+ const adaptiveSample = mode === "auto" && sparseSample;
85
+ const adaptiveThresholds = adaptiveSample && (minSupport == null || minEvents == null || validationRows.length > 0 && (minValidationSupport == null || minValidationEvents == null) || !explicitMaxEventCountShare);
86
+ const resolvedMinSupport = minSupport ?? (adaptiveSample ? clampInt(trainEvents * 0.1, 3, DEFAULT_MIN_SUPPORT) : DEFAULT_MIN_SUPPORT);
87
+ const resolvedMinEvents = minEvents ?? (minSupport != null || !adaptiveSample ? Math.max(5, Math.ceil(resolvedMinSupport * 0.5)) : clampInt(resolvedMinSupport * 0.5, 3, 10));
88
+ const hasValidation = validationRows.length > 0;
89
+ const resolvedMinValidationSupport = minValidationSupport ?? (hasValidation ? Math.max(3, Math.ceil(resolvedMinSupport * validationSplit * 0.5)) : 0);
90
+ const resolvedMinValidationEvents = minValidationEvents ?? (hasValidation ? Math.max(
91
+ adaptiveSample ? 3 : 2,
92
+ Math.ceil(resolvedMinEvents * validationSplit * 0.5)
93
+ ) : 0);
94
+ const resolvedMaxEventCountShare = adaptiveSample && !explicitMaxEventCountShare ? Math.max(maxEventCountShare, LOW_CADENCE_MAX_EVENT_SHARE) : maxEventCountShare;
95
+ return {
96
+ mode,
97
+ lowCadence,
98
+ sparseSample,
99
+ adaptiveThresholds,
100
+ trainRows: trainRows.length,
101
+ trainEvents,
102
+ trainPeriodDays,
103
+ trainEventsPerDay,
104
+ validationRows: validationRows.length,
105
+ validationEvents,
106
+ testRows: testRows.length,
107
+ testEvents,
108
+ minSupport: resolvedMinSupport,
109
+ minEvents: resolvedMinEvents,
110
+ minValidationSupport: resolvedMinValidationSupport,
111
+ minValidationEvents: resolvedMinValidationEvents,
112
+ maxEventCountShare: resolvedMaxEventCountShare
113
+ };
114
+ };
28
115
  var AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS = {
29
116
  minProfitFactor: "1.2",
30
117
  minWinRate: "0",
@@ -71,6 +158,29 @@ var splitAiPocketResearchRowsByTimestamp = (rows, validationSplit, testSplit = 0
71
158
  testRows: rows.filter((row) => isIn(testTimestamps, row.timestamp))
72
159
  };
73
160
  };
161
+ var splitAiPocketCoverageRowsByTimestamp = (rows, family, validationSplit, testSplit = 0) => splitAiPocketResearchRowsByTimestamp(
162
+ rows.filter((row) => row.featureCoverage?.[family] === true),
163
+ validationSplit,
164
+ testSplit
165
+ );
166
+ var sealAiPocketTestPartition = (split, sealed) => {
167
+ const finiteTimestamps = split.testRows.map((row) => row.timestamp).filter((timestamp) => Number.isFinite(timestamp));
168
+ return {
169
+ discoveryRows: [
170
+ ...split.trainRows,
171
+ ...split.validationRows,
172
+ ...sealed ? [] : split.testRows
173
+ ],
174
+ searchTestRows: sealed ? [] : split.testRows,
175
+ evidence: {
176
+ sealed,
177
+ rows: split.testRows.length,
178
+ events: countIndependentEvents(split.testRows),
179
+ startTimestamp: finiteTimestamps.length ? Math.min(...finiteTimestamps) : null,
180
+ endTimestamp: finiteTimestamps.length ? Math.max(...finiteTimestamps) : null
181
+ }
182
+ };
183
+ };
74
184
  var readAiPocketSearchCliOption = ({
75
185
  argv,
76
186
  longName,
@@ -92,5 +202,8 @@ var readAiPocketSearchCliOption = ({
92
202
  0 && (module.exports = {
93
203
  AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS,
94
204
  readAiPocketSearchCliOption,
205
+ resolveAiPocketCadenceProfile,
206
+ sealAiPocketTestPartition,
207
+ splitAiPocketCoverageRowsByTimestamp,
95
208
  splitAiPocketResearchRowsByTimestamp
96
209
  });
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/lib/aiTrainCharts.ts
21
21
  var aiTrainCharts_exports = {};
22
22
  __export(aiTrainCharts_exports, {
23
+ buildAiChartPersistenceReceipt: () => buildAiChartPersistenceReceipt,
23
24
  buildAiChartSnapshot: () => buildAiChartSnapshot
24
25
  });
25
26
  module.exports = __toCommonJS(aiTrainCharts_exports);
@@ -303,7 +304,10 @@ var getDirectionSortKey = (direction) => {
303
304
  return 2;
304
305
  };
305
306
  var summarizeAiTrainEvaluationsByDirection = (evaluations) => {
306
- const grouped = /* @__PURE__ */ new Map();
307
+ const grouped = /* @__PURE__ */ new Map([
308
+ ["LONG", []],
309
+ ["SHORT", []]
310
+ ]);
307
311
  for (const evaluation of evaluations) {
308
312
  const direction = typeof evaluation.direction === "string" && evaluation.direction.trim() ? evaluation.direction : "UNKNOWN";
309
313
  const bucket = grouped.get(direction) ?? [];
@@ -752,7 +756,16 @@ var buildAiChartSnapshot = (params) => {
752
756
  strategies: cards
753
757
  };
754
758
  };
759
+ var buildAiChartPersistenceReceipt = (params) => ({
760
+ persisted: true,
761
+ mode: params.snapshot.mode,
762
+ datasetId: params.datasetId,
763
+ generatedAt: params.snapshot.generatedAt,
764
+ cardIds: params.snapshot.strategies.map((card) => card.cardId),
765
+ ttlMs: params.ttlMs
766
+ });
755
767
  // Annotate the CommonJS export names for ESM import in node:
756
768
  0 && (module.exports = {
769
+ buildAiChartPersistenceReceipt,
757
770
  buildAiChartSnapshot
758
771
  });
@@ -314,7 +314,10 @@ var isDirectionMatchAtThreshold = (evaluation, threshold) => {
314
314
  return directionMatches === true && evaluation.quality != null && evaluation.quality >= threshold;
315
315
  };
316
316
  var summarizeAiTrainEvaluationsByDirection = (evaluations) => {
317
- const grouped = /* @__PURE__ */ new Map();
317
+ const grouped = /* @__PURE__ */ new Map([
318
+ ["LONG", []],
319
+ ["SHORT", []]
320
+ ]);
318
321
  for (const evaluation of evaluations) {
319
322
  const direction = typeof evaluation.direction === "string" && evaluation.direction.trim() ? evaluation.direction : "UNKNOWN";
320
323
  const bucket = grouped.get(direction) ?? [];
@@ -39,9 +39,9 @@ __export(aiTrainResearch_exports, {
39
39
  });
40
40
  module.exports = __toCommonJS(aiTrainResearch_exports);
41
41
  var import_node_child_process = require("child_process");
42
- var import_node_crypto = require("crypto");
43
- var import_promises = __toESM(require("fs/promises"));
44
- var import_node_path = __toESM(require("path"));
42
+ var import_node_crypto2 = require("crypto");
43
+ var import_promises2 = __toESM(require("fs/promises"));
44
+ var import_node_path2 = __toESM(require("path"));
45
45
 
46
46
  // src/lib/aiTrainMetrics.ts
47
47
  var divideOrNull = (num, denom) => {
@@ -312,10 +312,82 @@ var summarizeAiTrainEvaluations = (evaluations) => {
312
312
  )
313
313
  };
314
314
  };
315
+ var getDirectionSortKey = (direction) => {
316
+ if (direction === "LONG") {
317
+ return 0;
318
+ }
319
+ if (direction === "SHORT") {
320
+ return 1;
321
+ }
322
+ return 2;
323
+ };
324
+ var summarizeAiTrainEvaluationsByDirection = (evaluations) => {
325
+ const grouped = /* @__PURE__ */ new Map([
326
+ ["LONG", []],
327
+ ["SHORT", []]
328
+ ]);
329
+ for (const evaluation of evaluations) {
330
+ const direction = typeof evaluation.direction === "string" && evaluation.direction.trim() ? evaluation.direction : "UNKNOWN";
331
+ const bucket = grouped.get(direction) ?? [];
332
+ bucket.push(evaluation);
333
+ grouped.set(direction, bucket);
334
+ }
335
+ return [...grouped.entries()].sort(
336
+ ([leftDirection], [rightDirection]) => getDirectionSortKey(leftDirection) - getDirectionSortKey(rightDirection) || leftDirection.localeCompare(rightDirection)
337
+ ).map(([direction, directionEvaluations]) => ({
338
+ direction,
339
+ summary: summarizeAiTrainEvaluations(directionEvaluations)
340
+ }));
341
+ };
315
342
 
316
343
  // src/lib/aiTrainResearch.ts
317
344
  var import_strategies = require("@tradejs/node/strategies");
318
345
  var import_types = require("@tradejs/types");
346
+
347
+ // src/lib/strategyGateFingerprint.ts
348
+ var import_node_crypto = require("crypto");
349
+ var import_promises = __toESM(require("fs/promises"));
350
+ var import_node_path = __toESM(require("path"));
351
+ var gateFingerprintRelativePaths = (strategyName) => [
352
+ `packages/strategies/src/${strategyName}/adapters/ai.ts`,
353
+ `packages/strategies/src/${strategyName}/guardrails.ts`,
354
+ `packages/strategies/src/${strategyName}/pockets.ts`,
355
+ `packages/strategies/src/${strategyName}/config.ts`,
356
+ "packages/strategies/src/shared/localAiGate.ts",
357
+ "packages/node/src/ai.ts",
358
+ "packages/core/src/utils/strategyHelpers/signalBuilders.ts"
359
+ ];
360
+ var resolveStrategyGateFingerprint = async ({
361
+ projectRoot,
362
+ strategyName,
363
+ gitSha
364
+ }) => {
365
+ const candidates = await Promise.all(
366
+ gateFingerprintRelativePaths(strategyName).map(async (relativePath) => {
367
+ const content = await import_promises.default.readFile(import_node_path.default.join(projectRoot, relativePath)).catch(() => null);
368
+ return content == null ? null : { relativePath, content };
369
+ })
370
+ );
371
+ const sourceEntries = candidates.filter(
372
+ (entry) => entry != null
373
+ );
374
+ const hash = (0, import_node_crypto.createHash)("sha256");
375
+ hash.update(strategyName);
376
+ if (sourceEntries.length) {
377
+ for (const entry of sourceEntries) {
378
+ hash.update(entry.relativePath);
379
+ hash.update(entry.content);
380
+ }
381
+ } else {
382
+ hash.update(gitSha ?? "unknown-git-sha");
383
+ }
384
+ return {
385
+ gateFingerprint: hash.digest("hex").slice(0, 16),
386
+ gateFingerprintFiles: sourceEntries.map((entry) => entry.relativePath)
387
+ };
388
+ };
389
+
390
+ // src/lib/aiTrainResearch.ts
319
391
  var DAY_MS2 = 24 * 60 * 60 * 1e3;
320
392
  var RESEARCH_CONTEXT_ENV_KEYS = [
321
393
  "AI_MODE",
@@ -355,9 +427,9 @@ var writeAiTrainResearchSnapshot = async ({
355
427
  outputPath,
356
428
  result
357
429
  }) => {
358
- const resolvedPath = import_node_path.default.resolve(outputPath);
359
- await import_promises.default.mkdir(import_node_path.default.dirname(resolvedPath), { recursive: true });
360
- await import_promises.default.writeFile(
430
+ const resolvedPath = import_node_path2.default.resolve(outputPath);
431
+ await import_promises2.default.mkdir(import_node_path2.default.dirname(resolvedPath), { recursive: true });
432
+ await import_promises2.default.writeFile(
361
433
  resolvedPath,
362
434
  `${JSON.stringify(result, null, 2)}
363
435
  `,
@@ -376,7 +448,7 @@ var normalizeForStableJson = (value) => {
376
448
  }
377
449
  return value;
378
450
  };
379
- var fingerprintResearchValue = (value) => (0, import_node_crypto.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
451
+ var fingerprintResearchValue = (value) => (0, import_node_crypto2.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
380
452
  var getTimestampRange = (evaluations) => {
381
453
  let minTimestamp = null;
382
454
  let maxTimestamp = null;
@@ -441,17 +513,11 @@ var summarizeAiTrainTerminalWindows = (evaluations, windowDays) => {
441
513
  selected: selected.length,
442
514
  approvedPerCalendarDay: outcome.approved / coverageDays,
443
515
  outcome,
516
+ byDirection: summarizeAiTrainEvaluationsByDirection(selected),
444
517
  topRejectReasons: summarizeAiTrainRejectReasons(selected)
445
518
  };
446
519
  });
447
520
  };
448
- var readOptionalFile = async (filePath) => {
449
- try {
450
- return await import_promises.default.readFile(filePath);
451
- } catch {
452
- return null;
453
- }
454
- };
455
521
  var getGitLineage = (projectRoot) => {
456
522
  try {
457
523
  const gitSha = (0, import_node_child_process.execFileSync)("git", ["rev-parse", "HEAD"], {
@@ -473,53 +539,20 @@ var getGitLineage = (projectRoot) => {
473
539
  return { gitSha: null, gitDirty: null };
474
540
  }
475
541
  };
476
- var resolveGateFingerprint = async (projectRoot, strategyName, gitSha) => {
477
- const relativeCandidates = [
478
- `packages/strategies/src/${strategyName}/adapters/ai.ts`,
479
- `packages/strategies/src/${strategyName}/guardrails.ts`,
480
- `packages/strategies/src/${strategyName}/pockets.ts`,
481
- `packages/strategies/src/${strategyName}/config.ts`,
482
- "packages/strategies/src/shared/localAiGate.ts",
483
- "packages/node/src/ai.ts"
484
- ];
485
- const optionalSourceEntries = await Promise.all(
486
- relativeCandidates.map(async (relativePath) => {
487
- const content = await readOptionalFile(
488
- import_node_path.default.join(projectRoot, relativePath)
489
- );
490
- return content == null ? null : { relativePath, content };
491
- })
492
- );
493
- const sourceEntries = [];
494
- for (const entry of optionalSourceEntries) {
495
- if (entry != null) {
496
- sourceEntries.push(entry);
497
- }
498
- }
499
- const hash = (0, import_node_crypto.createHash)("sha256");
500
- hash.update(strategyName);
501
- if (sourceEntries.length) {
502
- for (const entry of sourceEntries) {
503
- hash.update(entry.relativePath);
504
- hash.update(entry.content);
505
- }
506
- } else {
507
- hash.update(gitSha ?? "unknown-git-sha");
508
- }
509
- return {
510
- gateFingerprint: hash.digest("hex").slice(0, 16),
511
- gateFingerprintFiles: sourceEntries.map((entry) => entry.relativePath)
512
- };
513
- };
514
542
  var buildAiTrainLineage = async ({
515
543
  projectRoot,
516
544
  strategyName,
517
545
  configIds,
518
546
  runContext,
547
+ sourceSha256s = [],
519
548
  env = process.env
520
549
  }) => {
521
550
  const { gitSha, gitDirty } = getGitLineage(projectRoot);
522
- const gate = await resolveGateFingerprint(projectRoot, strategyName, gitSha);
551
+ const gate = await resolveStrategyGateFingerprint({
552
+ projectRoot,
553
+ strategyName,
554
+ gitSha
555
+ });
523
556
  const normalizedConfigIds = [...new Set(configIds.filter(Boolean))].sort();
524
557
  const hyperliquidPerpUniverseFingerprint = typeof import_strategies.getHyperliquidPerpUniverseSnapshot === "function" ? (0, import_strategies.getHyperliquidPerpUniverseSnapshot)().fingerprint : null;
525
558
  const hyperliquidWhaleRegistryFingerprint = typeof import_strategies.getHyperliquidWhaleRegistrySnapshot === "function" ? (0, import_strategies.getHyperliquidWhaleRegistrySnapshot)().fingerprint : null;
@@ -539,7 +572,8 @@ var buildAiTrainLineage = async ({
539
572
  configIdsFingerprint: fingerprintResearchValue(normalizedConfigIds),
540
573
  configIds: normalizedConfigIds,
541
574
  contextFingerprint: fingerprintResearchValue(context),
542
- context
575
+ context,
576
+ sourceSha256s: [...new Set(sourceSha256s.filter(Boolean))].sort()
543
577
  };
544
578
  };
545
579
  // Annotate the CommonJS export names for ESM import in node: