@tradejs/cli 1.0.9 → 1.0.11

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.
Files changed (77) hide show
  1. package/README.md +13 -4
  2. package/dist/cli.js +29531 -13520
  3. package/dist/lib/aiPocketSearch.js +1316 -0
  4. package/dist/lib/aiTrainCharts.js +711 -0
  5. package/dist/lib/aiTrainDataset.js +40 -0
  6. package/dist/lib/aiTrainEvaluationDump.js +76 -0
  7. package/dist/lib/aiTrainMetrics.js +148 -8
  8. package/dist/lib/aiTrainOptions.js +130 -0
  9. package/dist/lib/aiTrainQuarantine.js +199 -0
  10. package/dist/lib/aiTrainResearch.js +467 -0
  11. package/dist/lib/binanceMarketContextBackfill.js +841 -0
  12. package/dist/lib/binanceMarketData.js +392 -0
  13. package/dist/lib/coinMarketCapContextBackfill.js +1126 -0
  14. package/dist/lib/continuity.js +60 -0
  15. package/dist/lib/derivativesContextBackfill.js +490 -77
  16. package/dist/lib/executionCalibration.js +705 -0
  17. package/dist/lib/marketContextPrepare.js +4441 -0
  18. package/dist/lib/paritySummary.js +101 -0
  19. package/dist/lib/runBot.js +1 -0
  20. package/dist/lib/runEnvironment.js +2259 -0
  21. package/dist/lib/runFormatting.js +1657 -0
  22. package/dist/lib/runtimeDebugEvidence.js +526 -0
  23. package/dist/lib/runtimeModeConfig.js +53 -0
  24. package/dist/lib/runtimeParity.js +88 -11
  25. package/dist/lib/runtimeParityDetails.js +751 -0
  26. package/dist/lib/runtimeRedis.js +157 -0
  27. package/dist/lib/runtimeSignalsLoader.js +40 -6
  28. package/dist/lib/runtimeSignalsStorage.js +45 -22
  29. package/dist/lib/runtimeStrategyBacktest.js +134 -0
  30. package/dist/lib/runtimeTradeSync.js +263 -0
  31. package/dist/lib/telegramReports.js +9 -0
  32. package/dist/lib/tickerUniverseCache.js +116 -0
  33. package/dist/lib/timeWindow.js +3 -3
  34. package/dist/scripts/agentRun.js +14 -8
  35. package/dist/scripts/aiExport.js +1721 -7
  36. package/dist/scripts/aiExportSelect.js +16 -4
  37. package/dist/scripts/aiPocketSearch.js +4190 -0
  38. package/dist/scripts/aiTrain.js +1866 -87
  39. package/dist/scripts/backtest.js +4534 -803
  40. package/dist/scripts/binanceMarketIngest.js +565 -0
  41. package/dist/scripts/bot.js +16 -2
  42. package/dist/scripts/candlesMigrateProvider.js +32 -22
  43. package/dist/scripts/cleanDir.js +15 -2
  44. package/dist/scripts/cleanRedis.js +15 -2
  45. package/dist/scripts/cleanTests.js +38 -2
  46. package/dist/scripts/cleanupMarketContext.js +69 -0
  47. package/dist/scripts/continuity.js +148 -67
  48. package/dist/scripts/derivativesIngest.js +14 -4
  49. package/dist/scripts/derivativesIngestCoinalyzeAll.js +14 -4
  50. package/dist/scripts/doctor.js +14 -4
  51. package/dist/scripts/executionCalibration.js +792 -0
  52. package/dist/scripts/infraCommon.js +18 -5
  53. package/dist/scripts/infraDown.js +14 -4
  54. package/dist/scripts/infraInit.js +32 -9
  55. package/dist/scripts/infraUp.js +24 -6
  56. package/dist/scripts/marketWs.js +222 -0
  57. package/dist/scripts/migration.js +15 -2
  58. package/dist/scripts/mlExport.js +1698 -10
  59. package/dist/scripts/mlExportSelect.js +16 -4
  60. package/dist/scripts/mlInspect.js +13 -3
  61. package/dist/scripts/mlTrainLatestSelect.js +14 -4
  62. package/dist/scripts/replay.js +8867 -0
  63. package/dist/scripts/replayRunner.js +8870 -0
  64. package/dist/scripts/replayRuntimeEvidence.js +868 -0
  65. package/dist/scripts/researchAuto.js +286 -59
  66. package/dist/scripts/results.js +3 -3
  67. package/dist/scripts/runtimeEvidence.js +605 -0
  68. package/dist/scripts/runtimeParity.js +6400 -1043
  69. package/dist/scripts/serverHealth.js +588 -0
  70. package/dist/scripts/signals.js +5628 -511
  71. package/dist/scripts/signalsDaemon.js +6205 -0
  72. package/dist/scripts/signalsSummary.js +833 -248
  73. package/dist/scripts/test-ml.js +25 -3
  74. package/dist/scripts/test.js +26 -1
  75. package/dist/scripts/user-add.js +15 -2
  76. package/dist/workers/testerWorker.js +130 -21
  77. package/package.json +18 -12
@@ -0,0 +1,40 @@
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/lib/aiTrainDataset.ts
21
+ var aiTrainDataset_exports = {};
22
+ __export(aiTrainDataset_exports, {
23
+ extractSignalFromAiDatasetRow: () => extractSignalFromAiDatasetRow
24
+ });
25
+ module.exports = __toCommonJS(aiTrainDataset_exports);
26
+ var extractSignalFromAiDatasetRow = (row) => {
27
+ const { payload } = row;
28
+ return {
29
+ ...payload.signal,
30
+ strategy: payload.signal.strategy,
31
+ figures: payload.figures ?? {},
32
+ indicators: payload.indicators ?? {},
33
+ additionalIndicators: payload.additionalIndicators ?? {},
34
+ prices: payload.signal.prices
35
+ };
36
+ };
37
+ // Annotate the CommonJS export names for ESM import in node:
38
+ 0 && (module.exports = {
39
+ extractSignalFromAiDatasetRow
40
+ });
@@ -0,0 +1,76 @@
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/lib/aiTrainEvaluationDump.ts
21
+ var aiTrainEvaluationDump_exports = {};
22
+ __export(aiTrainEvaluationDump_exports, {
23
+ buildAiTrainEvaluationFeatureSnapshot: () => buildAiTrainEvaluationFeatureSnapshot
24
+ });
25
+ module.exports = __toCommonJS(aiTrainEvaluationDump_exports);
26
+ var BASE_CONTEXT_FEATURE_SECTIONS = [
27
+ "regime",
28
+ "structure",
29
+ "participation",
30
+ "relative",
31
+ "derivatives",
32
+ "mtf",
33
+ "gateFeatures"
34
+ ];
35
+ var asRecord = (value) => typeof value === "object" && value != null && !Array.isArray(value) ? value : null;
36
+ var collectStrategyGateFeatures = (baseContext) => {
37
+ const entries = Object.entries(baseContext).filter(
38
+ ([key, value]) => key !== "gateFeatures" && key.endsWith("GateFeatures") && asRecord(value) != null
39
+ );
40
+ return entries.length > 0 ? Object.fromEntries(entries) : void 0;
41
+ };
42
+ var pickBaseContextFeatureSections = (baseContext) => Object.fromEntries(
43
+ BASE_CONTEXT_FEATURE_SECTIONS.map((section) => [
44
+ section,
45
+ baseContext[section] ?? null
46
+ ])
47
+ );
48
+ var buildAiTrainEvaluationFeatureSnapshot = ({
49
+ additionalIndicators,
50
+ mode
51
+ }) => {
52
+ if (mode === "none") {
53
+ return void 0;
54
+ }
55
+ const additional = asRecord(additionalIndicators);
56
+ const baseContext = asRecord(additional?.baseContext);
57
+ if (!baseContext) {
58
+ return {
59
+ baseContextAvailable: false
60
+ };
61
+ }
62
+ const strategyGateFeatures = collectStrategyGateFeatures(baseContext);
63
+ const snapshot = {
64
+ baseContextAvailable: true,
65
+ gateFeatures: baseContext.gateFeatures ?? null,
66
+ ...strategyGateFeatures ? { strategyGateFeatures } : {}
67
+ };
68
+ if (mode === "baseContext") {
69
+ snapshot.baseContext = pickBaseContextFeatureSections(baseContext);
70
+ }
71
+ return snapshot;
72
+ };
73
+ // Annotate the CommonJS export names for ESM import in node:
74
+ 0 && (module.exports = {
75
+ buildAiTrainEvaluationFeatureSnapshot
76
+ });
@@ -21,7 +21,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var aiTrainMetrics_exports = {};
22
22
  __export(aiTrainMetrics_exports, {
23
23
  summarizeAiTrainEvaluations: () => summarizeAiTrainEvaluations,
24
- summarizeAiTrainEvaluationsByDirection: () => summarizeAiTrainEvaluationsByDirection
24
+ summarizeAiTrainEvaluationsByDirection: () => summarizeAiTrainEvaluationsByDirection,
25
+ summarizeAiTrainEvaluationsByMonth: () => summarizeAiTrainEvaluationsByMonth,
26
+ summarizeAiTrainEvaluationsByQualityThreshold: () => summarizeAiTrainEvaluationsByQualityThreshold
25
27
  });
26
28
  module.exports = __toCommonJS(aiTrainMetrics_exports);
27
29
  var divideOrNull = (num, denom) => {
@@ -34,17 +36,119 @@ var DAY_MS = 24 * 60 * 60 * 1e3;
34
36
  var DAYS_PER_WEEK = 7;
35
37
  var DAYS_PER_MONTH = 30.4375;
36
38
  var getEvaluationPeriodDays = (evaluations) => {
37
- const timestamps = evaluations.map(
38
- (evaluation) => typeof evaluation.timestamp === "number" && Number.isFinite(evaluation.timestamp) ? evaluation.timestamp : null
39
- ).filter((timestamp) => timestamp != null);
40
- if (!timestamps.length) {
39
+ let minTimestamp = null;
40
+ let maxTimestamp = null;
41
+ for (const evaluation of evaluations) {
42
+ const timestamp = typeof evaluation.timestamp === "number" && Number.isFinite(evaluation.timestamp) ? evaluation.timestamp : null;
43
+ if (timestamp == null) {
44
+ continue;
45
+ }
46
+ if (minTimestamp == null || timestamp < minTimestamp) {
47
+ minTimestamp = timestamp;
48
+ }
49
+ if (maxTimestamp == null || timestamp > maxTimestamp) {
50
+ maxTimestamp = timestamp;
51
+ }
52
+ }
53
+ if (minTimestamp == null || maxTimestamp == null) {
41
54
  return null;
42
55
  }
43
- const minTimestamp = Math.min(...timestamps);
44
- const maxTimestamp = Math.max(...timestamps);
45
56
  return Math.max((maxTimestamp - minTimestamp) / DAY_MS, 1);
46
57
  };
47
58
  var qualitySortKey = (quality) => quality == null ? Number.POSITIVE_INFINITY : quality;
59
+ var emptyRiskSummary = () => ({
60
+ trades: 0,
61
+ totalProfit: 0,
62
+ grossProfit: 0,
63
+ grossLoss: 0,
64
+ profitFactor: null,
65
+ payoffRatio: null,
66
+ avgWin: null,
67
+ avgLoss: null,
68
+ largestWin: null,
69
+ largestLoss: null,
70
+ winRate: null,
71
+ maxDrawdown: 0,
72
+ maxDrawdownPctOfGrossProfit: null,
73
+ maxDrawdownPctOfTotalProfit: null,
74
+ recoveryFactor: null,
75
+ ulcerIndex: null,
76
+ maxConsecutiveWins: 0,
77
+ maxConsecutiveLosses: 0
78
+ });
79
+ var summarizeApprovedRisk = (evaluations) => {
80
+ const approvedEvaluations = evaluations.filter((evaluation) => evaluation.aiApproved).sort((left, right) => {
81
+ const leftTimestamp = typeof left.timestamp === "number" && Number.isFinite(left.timestamp) ? left.timestamp : Number.POSITIVE_INFINITY;
82
+ const rightTimestamp = typeof right.timestamp === "number" && Number.isFinite(right.timestamp) ? right.timestamp : Number.POSITIVE_INFINITY;
83
+ return leftTimestamp - rightTimestamp;
84
+ });
85
+ if (!approvedEvaluations.length) {
86
+ return emptyRiskSummary();
87
+ }
88
+ let grossProfit = 0;
89
+ let grossLoss = 0;
90
+ let wins = 0;
91
+ let losses = 0;
92
+ let largestWin = null;
93
+ let largestLoss = null;
94
+ let equity = 0;
95
+ let peakEquity = 0;
96
+ let maxDrawdown = 0;
97
+ let drawdownSquares = 0;
98
+ let currentWinStreak = 0;
99
+ let currentLossStreak = 0;
100
+ let maxConsecutiveWins = 0;
101
+ let maxConsecutiveLosses = 0;
102
+ for (const evaluation of approvedEvaluations) {
103
+ const profit = evaluation.profit;
104
+ if (profit > 0) {
105
+ grossProfit += profit;
106
+ wins += 1;
107
+ largestWin = largestWin == null ? profit : Math.max(largestWin, profit);
108
+ currentWinStreak += 1;
109
+ currentLossStreak = 0;
110
+ } else if (profit < 0) {
111
+ grossLoss += Math.abs(profit);
112
+ losses += 1;
113
+ largestLoss = largestLoss == null ? profit : Math.min(largestLoss, profit);
114
+ currentLossStreak += 1;
115
+ currentWinStreak = 0;
116
+ } else {
117
+ currentWinStreak = 0;
118
+ currentLossStreak = 0;
119
+ }
120
+ maxConsecutiveWins = Math.max(maxConsecutiveWins, currentWinStreak);
121
+ maxConsecutiveLosses = Math.max(maxConsecutiveLosses, currentLossStreak);
122
+ equity += profit;
123
+ peakEquity = Math.max(peakEquity, equity);
124
+ const drawdown = Math.max(0, peakEquity - equity);
125
+ maxDrawdown = Math.max(maxDrawdown, drawdown);
126
+ drawdownSquares += drawdown * drawdown;
127
+ }
128
+ const totalProfit = grossProfit - grossLoss;
129
+ const avgWin = divideOrNull(grossProfit, wins);
130
+ const avgLoss = divideOrNull(grossLoss, losses);
131
+ return {
132
+ trades: approvedEvaluations.length,
133
+ totalProfit,
134
+ grossProfit,
135
+ grossLoss,
136
+ profitFactor: grossLoss > 0 ? grossProfit / grossLoss : null,
137
+ payoffRatio: avgWin != null && avgLoss != null && avgLoss > 0 ? avgWin / avgLoss : null,
138
+ avgWin,
139
+ avgLoss,
140
+ largestWin,
141
+ largestLoss,
142
+ winRate: divideOrNull(wins, approvedEvaluations.length),
143
+ maxDrawdown,
144
+ maxDrawdownPctOfGrossProfit: grossProfit > 0 ? maxDrawdown / grossProfit : null,
145
+ maxDrawdownPctOfTotalProfit: totalProfit > 0 ? maxDrawdown / totalProfit : null,
146
+ recoveryFactor: maxDrawdown > 0 ? totalProfit / maxDrawdown : null,
147
+ ulcerIndex: Math.sqrt(drawdownSquares / approvedEvaluations.length),
148
+ maxConsecutiveWins,
149
+ maxConsecutiveLosses
150
+ };
151
+ };
48
152
  var summarizeAiTrainEvaluations = (evaluations) => {
49
153
  let approved = 0;
50
154
  let rejected = 0;
@@ -137,6 +241,7 @@ var summarizeAiTrainEvaluations = (evaluations) => {
137
241
  avgApprovedTradesPerDay,
138
242
  avgApprovedTradesPerWeek,
139
243
  expectancyDelta,
244
+ approvedRisk: summarizeApprovedRisk(evaluations),
140
245
  qualityBuckets: [...bucketMap.values()].sort(
141
246
  (a, b) => qualitySortKey(a.quality) - qualitySortKey(b.quality)
142
247
  )
@@ -151,6 +256,16 @@ var getDirectionSortKey = (direction) => {
151
256
  }
152
257
  return 2;
153
258
  };
259
+ var getMonthKey = (timestamp) => {
260
+ if (typeof timestamp !== "number" || !Number.isFinite(timestamp)) {
261
+ return "UNKNOWN";
262
+ }
263
+ return new Date(timestamp).toISOString().slice(0, 7);
264
+ };
265
+ var isDirectionMatchAtThreshold = (evaluation, threshold) => {
266
+ const directionMatches = typeof evaluation.modelDirectionMatches === "boolean" ? evaluation.modelDirectionMatches : evaluation.aiApproved;
267
+ return directionMatches === true && evaluation.quality != null && evaluation.quality >= threshold;
268
+ };
154
269
  var summarizeAiTrainEvaluationsByDirection = (evaluations) => {
155
270
  const grouped = /* @__PURE__ */ new Map();
156
271
  for (const evaluation of evaluations) {
@@ -166,8 +281,33 @@ var summarizeAiTrainEvaluationsByDirection = (evaluations) => {
166
281
  summary: summarizeAiTrainEvaluations(directionEvaluations)
167
282
  }));
168
283
  };
284
+ var summarizeAiTrainEvaluationsByMonth = (evaluations) => {
285
+ const grouped = /* @__PURE__ */ new Map();
286
+ for (const evaluation of evaluations) {
287
+ const month = getMonthKey(evaluation.timestamp);
288
+ const bucket = grouped.get(month) ?? [];
289
+ bucket.push(evaluation);
290
+ grouped.set(month, bucket);
291
+ }
292
+ return [...grouped.entries()].sort(([leftMonth], [rightMonth]) => leftMonth.localeCompare(rightMonth)).map(([month, monthEvaluations]) => ({
293
+ month,
294
+ summary: summarizeAiTrainEvaluations(monthEvaluations)
295
+ }));
296
+ };
297
+ var summarizeAiTrainEvaluationsByQualityThreshold = (evaluations, thresholds = [3, 4, 5]) => thresholds.map((threshold) => ({
298
+ threshold,
299
+ label: `q${threshold}+`,
300
+ summary: summarizeAiTrainEvaluations(
301
+ evaluations.map((evaluation) => ({
302
+ ...evaluation,
303
+ aiApproved: isDirectionMatchAtThreshold(evaluation, threshold)
304
+ }))
305
+ )
306
+ }));
169
307
  // Annotate the CommonJS export names for ESM import in node:
170
308
  0 && (module.exports = {
171
309
  summarizeAiTrainEvaluations,
172
- summarizeAiTrainEvaluationsByDirection
310
+ summarizeAiTrainEvaluationsByDirection,
311
+ summarizeAiTrainEvaluationsByMonth,
312
+ summarizeAiTrainEvaluationsByQualityThreshold
173
313
  });
@@ -0,0 +1,130 @@
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/lib/aiTrainOptions.ts
21
+ var aiTrainOptions_exports = {};
22
+ __export(aiTrainOptions_exports, {
23
+ hasCliOption: () => hasCliOption,
24
+ parseDumpFeatureMode: () => parseDumpFeatureMode,
25
+ parseQualityThresholds: () => parseQualityThresholds,
26
+ parseTerminalWindowDays: () => parseTerminalWindowDays,
27
+ parseTimestampFilter: () => parseTimestampFilter,
28
+ parseTrailingPeriodMs: () => parseTrailingPeriodMs,
29
+ resolveAiTrainRecentLimit: () => resolveAiTrainRecentLimit
30
+ });
31
+ module.exports = __toCommonJS(aiTrainOptions_exports);
32
+ var parseTimestampFilter = (value) => {
33
+ if (value == null || String(value).trim() === "") {
34
+ return null;
35
+ }
36
+ const text = String(value).trim();
37
+ const numeric = Number(text);
38
+ if (Number.isFinite(numeric)) {
39
+ return numeric;
40
+ }
41
+ const parsed = Date.parse(text);
42
+ if (Number.isFinite(parsed)) {
43
+ return parsed;
44
+ }
45
+ throw new Error(
46
+ `Invalid timestamp filter "${text}". Use an ISO date or epoch milliseconds.`
47
+ );
48
+ };
49
+ var parseTrailingPeriodMs = (value) => {
50
+ if (value == null || String(value).trim() === "") {
51
+ return null;
52
+ }
53
+ const text = String(value).trim().toLowerCase();
54
+ const match = text.match(
55
+ /^(?:last)?(\d+)(d|day|days|w|week|weeks|m|month|months|y|year|years)$/
56
+ );
57
+ if (!match) {
58
+ throw new Error(
59
+ `Invalid --period value "${String(value)}". Use values like last365d, 90d, 12w, or 1y.`
60
+ );
61
+ }
62
+ const amount = Number(match[1]);
63
+ const unit = match[2];
64
+ const dayMs = 24 * 60 * 60 * 1e3;
65
+ if (unit.startsWith("w")) {
66
+ return amount * 7 * dayMs;
67
+ }
68
+ if (unit.startsWith("m")) {
69
+ return amount * 30.4375 * dayMs;
70
+ }
71
+ if (unit.startsWith("y")) {
72
+ return amount * 365 * dayMs;
73
+ }
74
+ return amount * dayMs;
75
+ };
76
+ var parseQualityThresholds = (value) => {
77
+ const raw = String(value ?? "3,4,5").split(",").map((part) => Number(part.trim())).filter((part) => Number.isFinite(part)).map((part) => Math.trunc(part)).filter((part) => part > 0);
78
+ return [...new Set(raw)].sort((left, right) => left - right);
79
+ };
80
+ var parseTerminalWindowDays = (value) => {
81
+ const raw = String(value ?? "90,30,7").split(",").map((part) => Number(part.trim())).filter((part) => Number.isFinite(part)).map((part) => Math.trunc(part)).filter((part) => part > 0);
82
+ return [...new Set(raw)].sort((left, right) => right - left);
83
+ };
84
+ var parseDumpFeatureMode = (value) => {
85
+ const text = String(value ?? "none").trim().toLowerCase();
86
+ if (!text || text === "none" || text === "false" || text === "0") {
87
+ return "none";
88
+ }
89
+ if (text === "gatefeatures" || text === "gate-features") {
90
+ return "gateFeatures";
91
+ }
92
+ if (text === "basecontext" || text === "base-context") {
93
+ return "baseContext";
94
+ }
95
+ throw new Error(
96
+ `Invalid --dumpFeatures value "${String(value)}". Use none, gateFeatures, or baseContext.`
97
+ );
98
+ };
99
+ var hasCliOption = ({
100
+ argv,
101
+ longName,
102
+ shortName
103
+ }) => argv.some(
104
+ (arg) => arg === `--${longName}` || arg.startsWith(`--${longName}=`) || arg === `-${shortName}` || arg.startsWith(`-${shortName}=`)
105
+ );
106
+ var resolveAiTrainRecentLimit = ({
107
+ argv,
108
+ recentValue,
109
+ hasDateFilter,
110
+ defaultRecent = 50
111
+ }) => {
112
+ const hasExplicitRecent = hasCliOption({
113
+ argv,
114
+ longName: "recent",
115
+ shortName: "n"
116
+ });
117
+ const selectedRecent = hasDateFilter && !hasExplicitRecent ? 0 : recentValue;
118
+ const parsed = Number(selectedRecent);
119
+ return Number.isFinite(parsed) ? Math.max(0, Math.trunc(parsed)) : defaultRecent;
120
+ };
121
+ // Annotate the CommonJS export names for ESM import in node:
122
+ 0 && (module.exports = {
123
+ hasCliOption,
124
+ parseDumpFeatureMode,
125
+ parseQualityThresholds,
126
+ parseTerminalWindowDays,
127
+ parseTimestampFilter,
128
+ parseTrailingPeriodMs,
129
+ resolveAiTrainRecentLimit
130
+ });
@@ -0,0 +1,199 @@
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/lib/aiTrainQuarantine.ts
21
+ var aiTrainQuarantine_exports = {};
22
+ __export(aiTrainQuarantine_exports, {
23
+ applyAiTrainSymbolQuarantine: () => applyAiTrainSymbolQuarantine,
24
+ summarizeAiTrainDuplicateSignals: () => summarizeAiTrainDuplicateSignals
25
+ });
26
+ module.exports = __toCommonJS(aiTrainQuarantine_exports);
27
+ var import_crypto = require("crypto");
28
+ var DAY_MS = 24 * 60 * 60 * 1e3;
29
+ var normalizeKeyPart = (value, fallback) => {
30
+ if (typeof value !== "string") {
31
+ return fallback;
32
+ }
33
+ const trimmed = value.trim();
34
+ return trimmed || fallback;
35
+ };
36
+ var getProfitFactor = ({
37
+ grossProfit,
38
+ grossLoss
39
+ }) => {
40
+ if (grossLoss <= 0) {
41
+ return grossProfit > 0 ? null : 0;
42
+ }
43
+ return grossProfit / grossLoss;
44
+ };
45
+ var createEmptyState = () => ({
46
+ quarantineUntil: null,
47
+ approvedLosses: 0,
48
+ grossProfit: 0,
49
+ grossLoss: 0
50
+ });
51
+ var applyAiTrainSymbolQuarantine = (evaluations, options) => {
52
+ const summary = {
53
+ enabled: options.enabled,
54
+ minApprovedLosses: options.minApprovedLosses,
55
+ minProfitFactor: options.minProfitFactor,
56
+ cooldownDays: options.cooldownDays,
57
+ blocked: 0,
58
+ events: []
59
+ };
60
+ if (!options.enabled) {
61
+ return {
62
+ evaluations: evaluations.map((evaluation) => ({
63
+ ...evaluation,
64
+ rawAiApproved: evaluation.aiApproved
65
+ })),
66
+ summary
67
+ };
68
+ }
69
+ const cooldownMs = Math.max(0, options.cooldownDays) * DAY_MS;
70
+ const minApprovedLosses = Math.max(1, Math.trunc(options.minApprovedLosses));
71
+ const minProfitFactor = Math.max(0, options.minProfitFactor);
72
+ const states = /* @__PURE__ */ new Map();
73
+ const ordered = evaluations.map((evaluation, index) => ({ evaluation, index })).sort((left, right) => {
74
+ const leftTimestamp = typeof left.evaluation.timestamp === "number" && Number.isFinite(left.evaluation.timestamp) ? left.evaluation.timestamp : Number.POSITIVE_INFINITY;
75
+ const rightTimestamp = typeof right.evaluation.timestamp === "number" && Number.isFinite(right.evaluation.timestamp) ? right.evaluation.timestamp : Number.POSITIVE_INFINITY;
76
+ return leftTimestamp - rightTimestamp || left.index - right.index;
77
+ });
78
+ const result = evaluations.map((evaluation) => ({
79
+ ...evaluation,
80
+ rawAiApproved: evaluation.aiApproved
81
+ }));
82
+ for (const { evaluation, index } of ordered) {
83
+ const timestamp = typeof evaluation.timestamp === "number" && Number.isFinite(evaluation.timestamp) ? evaluation.timestamp : null;
84
+ const strategy = normalizeKeyPart(evaluation.strategy, "unknown");
85
+ const symbol = normalizeKeyPart(evaluation.symbol, "unknown");
86
+ const key = `${strategy}:${symbol}`;
87
+ const state = states.get(key) ?? createEmptyState();
88
+ states.set(key, state);
89
+ if (timestamp != null && state.quarantineUntil != null && timestamp >= state.quarantineUntil) {
90
+ state.quarantineUntil = null;
91
+ state.approvedLosses = 0;
92
+ state.grossProfit = 0;
93
+ state.grossLoss = 0;
94
+ }
95
+ if (evaluation.aiApproved && timestamp != null && state.quarantineUntil != null && timestamp < state.quarantineUntil) {
96
+ result[index] = {
97
+ ...result[index],
98
+ aiApproved: false
99
+ };
100
+ summary.blocked += 1;
101
+ continue;
102
+ }
103
+ if (!evaluation.aiApproved) {
104
+ continue;
105
+ }
106
+ if (evaluation.profit > 0) {
107
+ state.grossProfit += evaluation.profit;
108
+ } else if (evaluation.profit < 0) {
109
+ state.grossLoss += Math.abs(evaluation.profit);
110
+ state.approvedLosses += 1;
111
+ }
112
+ const profitFactor = getProfitFactor(state);
113
+ if (timestamp != null && cooldownMs > 0 && state.approvedLosses >= minApprovedLosses && profitFactor != null && profitFactor < minProfitFactor) {
114
+ state.quarantineUntil = timestamp + cooldownMs;
115
+ summary.events.push({
116
+ strategy,
117
+ symbol,
118
+ startedAt: timestamp,
119
+ until: state.quarantineUntil,
120
+ approvedLosses: state.approvedLosses,
121
+ profitFactor,
122
+ grossProfit: state.grossProfit,
123
+ grossLoss: state.grossLoss
124
+ });
125
+ }
126
+ }
127
+ return {
128
+ evaluations: result,
129
+ summary
130
+ };
131
+ };
132
+ var stableStringify = (value) => {
133
+ if (value == null || typeof value !== "object") {
134
+ return JSON.stringify(value);
135
+ }
136
+ if (Array.isArray(value)) {
137
+ return `[${value.map(stableStringify).join(",")}]`;
138
+ }
139
+ const record = value;
140
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`).join(",")}}`;
141
+ };
142
+ var hashContext = (row) => {
143
+ const payload = row.payload;
144
+ const signal = payload?.signal;
145
+ const additional = payload?.additionalIndicators && typeof payload.additionalIndicators === "object" && !Array.isArray(payload.additionalIndicators) ? payload.additionalIndicators : {};
146
+ const context = {
147
+ prices: signal?.prices ?? null,
148
+ trendShiftContext: additional.trendShiftContext ?? null,
149
+ marketContext: additional.marketContext ?? null
150
+ };
151
+ return (0, import_crypto.createHash)("sha1").update(stableStringify(context)).digest("hex");
152
+ };
153
+ var summarizeAiTrainDuplicateSignals = (rows) => {
154
+ const groups = /* @__PURE__ */ new Map();
155
+ for (const row of rows) {
156
+ const strategy = normalizeKeyPart(row.strategyName, "unknown");
157
+ const symbol = normalizeKeyPart(row.symbol, "unknown");
158
+ const direction = normalizeKeyPart(row.direction, "UNKNOWN");
159
+ const timestamp = Number(row.timestamp);
160
+ const profit = Number(row.profit);
161
+ const key = [
162
+ strategy,
163
+ symbol,
164
+ direction,
165
+ Number.isFinite(timestamp) ? timestamp : "no_timestamp",
166
+ Number.isFinite(profit) ? profit : "no_profit",
167
+ hashContext(row)
168
+ ].join("|");
169
+ const group = groups.get(key) ?? {
170
+ key,
171
+ strategy,
172
+ symbol,
173
+ direction,
174
+ timestamp: Number.isFinite(timestamp) ? timestamp : 0,
175
+ count: 0,
176
+ totalProfit: 0
177
+ };
178
+ group.count += 1;
179
+ group.totalProfit += Number.isFinite(profit) ? profit : 0;
180
+ groups.set(key, group);
181
+ }
182
+ const duplicateGroups = [...groups.values()].filter((group) => group.count > 1).sort(
183
+ (left, right) => left.totalProfit - right.totalProfit || right.count - left.count || left.key.localeCompare(right.key)
184
+ );
185
+ return {
186
+ groups: duplicateGroups.length,
187
+ rows: duplicateGroups.reduce((total, group) => total + group.count, 0),
188
+ maxGroupSize: duplicateGroups.reduce(
189
+ (max, group) => Math.max(max, group.count),
190
+ 0
191
+ ),
192
+ worstGroups: duplicateGroups.slice(0, 10)
193
+ };
194
+ };
195
+ // Annotate the CommonJS export names for ESM import in node:
196
+ 0 && (module.exports = {
197
+ applyAiTrainSymbolQuarantine,
198
+ summarizeAiTrainDuplicateSignals
199
+ });