@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.
- package/README.md +13 -4
- package/dist/cli.js +29531 -13520
- package/dist/lib/aiPocketSearch.js +1316 -0
- package/dist/lib/aiTrainCharts.js +711 -0
- package/dist/lib/aiTrainDataset.js +40 -0
- package/dist/lib/aiTrainEvaluationDump.js +76 -0
- package/dist/lib/aiTrainMetrics.js +148 -8
- package/dist/lib/aiTrainOptions.js +130 -0
- package/dist/lib/aiTrainQuarantine.js +199 -0
- package/dist/lib/aiTrainResearch.js +467 -0
- package/dist/lib/binanceMarketContextBackfill.js +841 -0
- package/dist/lib/binanceMarketData.js +392 -0
- package/dist/lib/coinMarketCapContextBackfill.js +1126 -0
- package/dist/lib/continuity.js +60 -0
- package/dist/lib/derivativesContextBackfill.js +490 -77
- package/dist/lib/executionCalibration.js +705 -0
- package/dist/lib/marketContextPrepare.js +4441 -0
- package/dist/lib/paritySummary.js +101 -0
- package/dist/lib/runBot.js +1 -0
- package/dist/lib/runEnvironment.js +2259 -0
- package/dist/lib/runFormatting.js +1657 -0
- package/dist/lib/runtimeDebugEvidence.js +526 -0
- package/dist/lib/runtimeModeConfig.js +53 -0
- package/dist/lib/runtimeParity.js +88 -11
- package/dist/lib/runtimeParityDetails.js +751 -0
- package/dist/lib/runtimeRedis.js +157 -0
- package/dist/lib/runtimeSignalsLoader.js +40 -6
- package/dist/lib/runtimeSignalsStorage.js +45 -22
- package/dist/lib/runtimeStrategyBacktest.js +134 -0
- package/dist/lib/runtimeTradeSync.js +263 -0
- package/dist/lib/telegramReports.js +9 -0
- package/dist/lib/tickerUniverseCache.js +116 -0
- package/dist/lib/timeWindow.js +3 -3
- package/dist/scripts/agentRun.js +14 -8
- package/dist/scripts/aiExport.js +1721 -7
- package/dist/scripts/aiExportSelect.js +16 -4
- package/dist/scripts/aiPocketSearch.js +4190 -0
- package/dist/scripts/aiTrain.js +1866 -87
- package/dist/scripts/backtest.js +4534 -803
- package/dist/scripts/binanceMarketIngest.js +565 -0
- package/dist/scripts/bot.js +16 -2
- package/dist/scripts/candlesMigrateProvider.js +32 -22
- package/dist/scripts/cleanDir.js +15 -2
- package/dist/scripts/cleanRedis.js +15 -2
- package/dist/scripts/cleanTests.js +38 -2
- package/dist/scripts/cleanupMarketContext.js +69 -0
- package/dist/scripts/continuity.js +148 -67
- package/dist/scripts/derivativesIngest.js +14 -4
- package/dist/scripts/derivativesIngestCoinalyzeAll.js +14 -4
- package/dist/scripts/doctor.js +14 -4
- package/dist/scripts/executionCalibration.js +792 -0
- package/dist/scripts/infraCommon.js +18 -5
- package/dist/scripts/infraDown.js +14 -4
- package/dist/scripts/infraInit.js +32 -9
- package/dist/scripts/infraUp.js +24 -6
- package/dist/scripts/marketWs.js +222 -0
- package/dist/scripts/migration.js +15 -2
- package/dist/scripts/mlExport.js +1698 -10
- package/dist/scripts/mlExportSelect.js +16 -4
- package/dist/scripts/mlInspect.js +13 -3
- package/dist/scripts/mlTrainLatestSelect.js +14 -4
- package/dist/scripts/replay.js +8867 -0
- package/dist/scripts/replayRunner.js +8870 -0
- package/dist/scripts/replayRuntimeEvidence.js +868 -0
- package/dist/scripts/researchAuto.js +286 -59
- package/dist/scripts/results.js +3 -3
- package/dist/scripts/runtimeEvidence.js +605 -0
- package/dist/scripts/runtimeParity.js +6400 -1043
- package/dist/scripts/serverHealth.js +588 -0
- package/dist/scripts/signals.js +5628 -511
- package/dist/scripts/signalsDaemon.js +6205 -0
- package/dist/scripts/signalsSummary.js +833 -248
- package/dist/scripts/test-ml.js +25 -3
- package/dist/scripts/test.js +26 -1
- package/dist/scripts/user-add.js +15 -2
- package/dist/workers/testerWorker.js +130 -21
- package/package.json +18 -12
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/lib/aiTrainResearch.ts
|
|
31
|
+
var aiTrainResearch_exports = {};
|
|
32
|
+
__export(aiTrainResearch_exports, {
|
|
33
|
+
buildAiTrainLineage: () => buildAiTrainLineage,
|
|
34
|
+
fingerprintResearchValue: () => fingerprintResearchValue,
|
|
35
|
+
summarizeAiTrainCoverage: () => summarizeAiTrainCoverage,
|
|
36
|
+
summarizeAiTrainRejectReasons: () => summarizeAiTrainRejectReasons,
|
|
37
|
+
summarizeAiTrainTerminalWindows: () => summarizeAiTrainTerminalWindows
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(aiTrainResearch_exports);
|
|
40
|
+
var import_node_child_process = require("child_process");
|
|
41
|
+
var import_node_crypto = require("crypto");
|
|
42
|
+
var import_promises = __toESM(require("fs/promises"));
|
|
43
|
+
var import_node_path = __toESM(require("path"));
|
|
44
|
+
|
|
45
|
+
// src/lib/aiTrainMetrics.ts
|
|
46
|
+
var divideOrNull = (num, denom) => {
|
|
47
|
+
if (denom <= 0) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return num / denom;
|
|
51
|
+
};
|
|
52
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
53
|
+
var DAYS_PER_WEEK = 7;
|
|
54
|
+
var DAYS_PER_MONTH = 30.4375;
|
|
55
|
+
var getEvaluationPeriodDays = (evaluations) => {
|
|
56
|
+
let minTimestamp = null;
|
|
57
|
+
let maxTimestamp = null;
|
|
58
|
+
for (const evaluation of evaluations) {
|
|
59
|
+
const timestamp = typeof evaluation.timestamp === "number" && Number.isFinite(evaluation.timestamp) ? evaluation.timestamp : null;
|
|
60
|
+
if (timestamp == null) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (minTimestamp == null || timestamp < minTimestamp) {
|
|
64
|
+
minTimestamp = timestamp;
|
|
65
|
+
}
|
|
66
|
+
if (maxTimestamp == null || timestamp > maxTimestamp) {
|
|
67
|
+
maxTimestamp = timestamp;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (minTimestamp == null || maxTimestamp == null) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
return Math.max((maxTimestamp - minTimestamp) / DAY_MS, 1);
|
|
74
|
+
};
|
|
75
|
+
var qualitySortKey = (quality) => quality == null ? Number.POSITIVE_INFINITY : quality;
|
|
76
|
+
var emptyRiskSummary = () => ({
|
|
77
|
+
trades: 0,
|
|
78
|
+
totalProfit: 0,
|
|
79
|
+
grossProfit: 0,
|
|
80
|
+
grossLoss: 0,
|
|
81
|
+
profitFactor: null,
|
|
82
|
+
payoffRatio: null,
|
|
83
|
+
avgWin: null,
|
|
84
|
+
avgLoss: null,
|
|
85
|
+
largestWin: null,
|
|
86
|
+
largestLoss: null,
|
|
87
|
+
winRate: null,
|
|
88
|
+
maxDrawdown: 0,
|
|
89
|
+
maxDrawdownPctOfGrossProfit: null,
|
|
90
|
+
maxDrawdownPctOfTotalProfit: null,
|
|
91
|
+
recoveryFactor: null,
|
|
92
|
+
ulcerIndex: null,
|
|
93
|
+
maxConsecutiveWins: 0,
|
|
94
|
+
maxConsecutiveLosses: 0
|
|
95
|
+
});
|
|
96
|
+
var summarizeApprovedRisk = (evaluations) => {
|
|
97
|
+
const approvedEvaluations = evaluations.filter((evaluation) => evaluation.aiApproved).sort((left, right) => {
|
|
98
|
+
const leftTimestamp = typeof left.timestamp === "number" && Number.isFinite(left.timestamp) ? left.timestamp : Number.POSITIVE_INFINITY;
|
|
99
|
+
const rightTimestamp = typeof right.timestamp === "number" && Number.isFinite(right.timestamp) ? right.timestamp : Number.POSITIVE_INFINITY;
|
|
100
|
+
return leftTimestamp - rightTimestamp;
|
|
101
|
+
});
|
|
102
|
+
if (!approvedEvaluations.length) {
|
|
103
|
+
return emptyRiskSummary();
|
|
104
|
+
}
|
|
105
|
+
let grossProfit = 0;
|
|
106
|
+
let grossLoss = 0;
|
|
107
|
+
let wins = 0;
|
|
108
|
+
let losses = 0;
|
|
109
|
+
let largestWin = null;
|
|
110
|
+
let largestLoss = null;
|
|
111
|
+
let equity = 0;
|
|
112
|
+
let peakEquity = 0;
|
|
113
|
+
let maxDrawdown = 0;
|
|
114
|
+
let drawdownSquares = 0;
|
|
115
|
+
let currentWinStreak = 0;
|
|
116
|
+
let currentLossStreak = 0;
|
|
117
|
+
let maxConsecutiveWins = 0;
|
|
118
|
+
let maxConsecutiveLosses = 0;
|
|
119
|
+
for (const evaluation of approvedEvaluations) {
|
|
120
|
+
const profit = evaluation.profit;
|
|
121
|
+
if (profit > 0) {
|
|
122
|
+
grossProfit += profit;
|
|
123
|
+
wins += 1;
|
|
124
|
+
largestWin = largestWin == null ? profit : Math.max(largestWin, profit);
|
|
125
|
+
currentWinStreak += 1;
|
|
126
|
+
currentLossStreak = 0;
|
|
127
|
+
} else if (profit < 0) {
|
|
128
|
+
grossLoss += Math.abs(profit);
|
|
129
|
+
losses += 1;
|
|
130
|
+
largestLoss = largestLoss == null ? profit : Math.min(largestLoss, profit);
|
|
131
|
+
currentLossStreak += 1;
|
|
132
|
+
currentWinStreak = 0;
|
|
133
|
+
} else {
|
|
134
|
+
currentWinStreak = 0;
|
|
135
|
+
currentLossStreak = 0;
|
|
136
|
+
}
|
|
137
|
+
maxConsecutiveWins = Math.max(maxConsecutiveWins, currentWinStreak);
|
|
138
|
+
maxConsecutiveLosses = Math.max(maxConsecutiveLosses, currentLossStreak);
|
|
139
|
+
equity += profit;
|
|
140
|
+
peakEquity = Math.max(peakEquity, equity);
|
|
141
|
+
const drawdown = Math.max(0, peakEquity - equity);
|
|
142
|
+
maxDrawdown = Math.max(maxDrawdown, drawdown);
|
|
143
|
+
drawdownSquares += drawdown * drawdown;
|
|
144
|
+
}
|
|
145
|
+
const totalProfit = grossProfit - grossLoss;
|
|
146
|
+
const avgWin = divideOrNull(grossProfit, wins);
|
|
147
|
+
const avgLoss = divideOrNull(grossLoss, losses);
|
|
148
|
+
return {
|
|
149
|
+
trades: approvedEvaluations.length,
|
|
150
|
+
totalProfit,
|
|
151
|
+
grossProfit,
|
|
152
|
+
grossLoss,
|
|
153
|
+
profitFactor: grossLoss > 0 ? grossProfit / grossLoss : null,
|
|
154
|
+
payoffRatio: avgWin != null && avgLoss != null && avgLoss > 0 ? avgWin / avgLoss : null,
|
|
155
|
+
avgWin,
|
|
156
|
+
avgLoss,
|
|
157
|
+
largestWin,
|
|
158
|
+
largestLoss,
|
|
159
|
+
winRate: divideOrNull(wins, approvedEvaluations.length),
|
|
160
|
+
maxDrawdown,
|
|
161
|
+
maxDrawdownPctOfGrossProfit: grossProfit > 0 ? maxDrawdown / grossProfit : null,
|
|
162
|
+
maxDrawdownPctOfTotalProfit: totalProfit > 0 ? maxDrawdown / totalProfit : null,
|
|
163
|
+
recoveryFactor: maxDrawdown > 0 ? totalProfit / maxDrawdown : null,
|
|
164
|
+
ulcerIndex: Math.sqrt(drawdownSquares / approvedEvaluations.length),
|
|
165
|
+
maxConsecutiveWins,
|
|
166
|
+
maxConsecutiveLosses
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
var summarizeAiTrainEvaluations = (evaluations) => {
|
|
170
|
+
let approved = 0;
|
|
171
|
+
let rejected = 0;
|
|
172
|
+
let correct = 0;
|
|
173
|
+
let incorrect = 0;
|
|
174
|
+
let profitable = 0;
|
|
175
|
+
let unprofitable = 0;
|
|
176
|
+
let flat = 0;
|
|
177
|
+
let truePositive = 0;
|
|
178
|
+
let falsePositive = 0;
|
|
179
|
+
let trueNegative = 0;
|
|
180
|
+
let falseNegative = 0;
|
|
181
|
+
let totalProfitAll = 0;
|
|
182
|
+
let totalProfitApproved = 0;
|
|
183
|
+
const bucketMap = /* @__PURE__ */ new Map();
|
|
184
|
+
for (const evaluation of evaluations) {
|
|
185
|
+
const { profit, profitableTrade, aiApproved, quality } = evaluation;
|
|
186
|
+
totalProfitAll += profit;
|
|
187
|
+
if (profitableTrade) {
|
|
188
|
+
profitable += 1;
|
|
189
|
+
} else if (profit < 0) {
|
|
190
|
+
unprofitable += 1;
|
|
191
|
+
} else {
|
|
192
|
+
flat += 1;
|
|
193
|
+
}
|
|
194
|
+
if (aiApproved) {
|
|
195
|
+
approved += 1;
|
|
196
|
+
totalProfitApproved += profit;
|
|
197
|
+
} else {
|
|
198
|
+
rejected += 1;
|
|
199
|
+
}
|
|
200
|
+
const isCorrect = aiApproved === profitableTrade;
|
|
201
|
+
if (isCorrect) {
|
|
202
|
+
correct += 1;
|
|
203
|
+
} else {
|
|
204
|
+
incorrect += 1;
|
|
205
|
+
}
|
|
206
|
+
if (aiApproved && profitableTrade) {
|
|
207
|
+
truePositive += 1;
|
|
208
|
+
} else if (aiApproved && !profitableTrade) {
|
|
209
|
+
falsePositive += 1;
|
|
210
|
+
} else if (!aiApproved && profitableTrade) {
|
|
211
|
+
falseNegative += 1;
|
|
212
|
+
} else {
|
|
213
|
+
trueNegative += 1;
|
|
214
|
+
}
|
|
215
|
+
const bucket = bucketMap.get(quality) ?? {
|
|
216
|
+
quality,
|
|
217
|
+
count: 0,
|
|
218
|
+
approved: 0,
|
|
219
|
+
profitable: 0,
|
|
220
|
+
totalProfit: 0
|
|
221
|
+
};
|
|
222
|
+
bucket.count += 1;
|
|
223
|
+
bucket.totalProfit += profit;
|
|
224
|
+
if (aiApproved) {
|
|
225
|
+
bucket.approved += 1;
|
|
226
|
+
}
|
|
227
|
+
if (profitableTrade) {
|
|
228
|
+
bucket.profitable += 1;
|
|
229
|
+
}
|
|
230
|
+
bucketMap.set(quality, bucket);
|
|
231
|
+
}
|
|
232
|
+
const avgProfitAll = divideOrNull(totalProfitAll, evaluations.length);
|
|
233
|
+
const avgProfitApproved = divideOrNull(totalProfitApproved, approved);
|
|
234
|
+
const periodDays = getEvaluationPeriodDays(evaluations);
|
|
235
|
+
const avgProfitApprovedPerDay = periodDays == null ? null : divideOrNull(totalProfitApproved, periodDays);
|
|
236
|
+
const avgApprovedTradesPerDay = periodDays == null ? null : divideOrNull(approved, periodDays);
|
|
237
|
+
const avgProfitApprovedPerMonth = avgProfitApprovedPerDay == null ? null : avgProfitApprovedPerDay * DAYS_PER_MONTH;
|
|
238
|
+
const avgApprovedTradesPerWeek = avgApprovedTradesPerDay == null ? null : avgApprovedTradesPerDay * DAYS_PER_WEEK;
|
|
239
|
+
const expectancyDelta = avgProfitAll == null || avgProfitApproved == null ? null : avgProfitApproved - avgProfitAll;
|
|
240
|
+
return {
|
|
241
|
+
approved,
|
|
242
|
+
rejected,
|
|
243
|
+
correct,
|
|
244
|
+
incorrect,
|
|
245
|
+
truePositive,
|
|
246
|
+
falsePositive,
|
|
247
|
+
trueNegative,
|
|
248
|
+
falseNegative,
|
|
249
|
+
profitable,
|
|
250
|
+
unprofitable,
|
|
251
|
+
flat,
|
|
252
|
+
precisionApproved: divideOrNull(truePositive, approved),
|
|
253
|
+
recallWinners: divideOrNull(truePositive, profitable),
|
|
254
|
+
avgProfitAll,
|
|
255
|
+
avgProfitApproved,
|
|
256
|
+
avgProfitApprovedPerDay,
|
|
257
|
+
avgProfitApprovedPerMonth,
|
|
258
|
+
avgApprovedTradesPerDay,
|
|
259
|
+
avgApprovedTradesPerWeek,
|
|
260
|
+
expectancyDelta,
|
|
261
|
+
approvedRisk: summarizeApprovedRisk(evaluations),
|
|
262
|
+
qualityBuckets: [...bucketMap.values()].sort(
|
|
263
|
+
(a, b) => qualitySortKey(a.quality) - qualitySortKey(b.quality)
|
|
264
|
+
)
|
|
265
|
+
};
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// src/lib/aiTrainResearch.ts
|
|
269
|
+
var DAY_MS2 = 24 * 60 * 60 * 1e3;
|
|
270
|
+
var RESEARCH_CONTEXT_ENV_KEYS = [
|
|
271
|
+
"AI_MODE",
|
|
272
|
+
"MIN_AI_QUALITY",
|
|
273
|
+
"INTERVAL",
|
|
274
|
+
"DERIVATIVES_CONTEXT_ENABLED",
|
|
275
|
+
"DERIVATIVES_CONTEXT_TARGET_ENABLED",
|
|
276
|
+
"DERIVATIVES_CONTEXT_INTERVALS",
|
|
277
|
+
"DERIVATIVES_CONTEXT_LOOKBACK_HOURS",
|
|
278
|
+
"DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS",
|
|
279
|
+
"DERIVATIVES_CONTEXT_EXCHANGE_PRIORITY",
|
|
280
|
+
"COINMARKETCAP_CONTEXT_ENABLED",
|
|
281
|
+
"COINMARKETCAP_CONTEXT_MAX_AGE_MS",
|
|
282
|
+
"COINMARKETCAP_CONTEXT_EXCHANGE_LIQUIDITY_ENABLED",
|
|
283
|
+
"COINMARKETCAP_CONTEXT_EXCHANGE_SLUGS",
|
|
284
|
+
"COINMARKETCAP_CONTEXT_FEAR_GREED_ENABLED",
|
|
285
|
+
"COINMARKETCAP_CONTEXT_HISTORICAL_ACCESS_MONTHS",
|
|
286
|
+
"COINMARKETCAP_CONTEXT_BACKFILL_ENABLED",
|
|
287
|
+
"COINMARKETCAP_CONTEXT_BACKFILL_MAX_DAYS",
|
|
288
|
+
"COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS"
|
|
289
|
+
];
|
|
290
|
+
var normalizeForStableJson = (value) => {
|
|
291
|
+
if (Array.isArray(value)) {
|
|
292
|
+
return value.map(normalizeForStableJson);
|
|
293
|
+
}
|
|
294
|
+
if (value && typeof value === "object") {
|
|
295
|
+
return Object.fromEntries(
|
|
296
|
+
Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalizeForStableJson(entry)])
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
return value;
|
|
300
|
+
};
|
|
301
|
+
var fingerprintResearchValue = (value) => (0, import_node_crypto.createHash)("sha256").update(JSON.stringify(normalizeForStableJson(value))).digest("hex").slice(0, 16);
|
|
302
|
+
var getTimestampRange = (evaluations) => {
|
|
303
|
+
let minTimestamp = null;
|
|
304
|
+
let maxTimestamp = null;
|
|
305
|
+
for (const evaluation of evaluations) {
|
|
306
|
+
const timestamp = evaluation.timestamp;
|
|
307
|
+
if (typeof timestamp !== "number" || !Number.isFinite(timestamp)) {
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
minTimestamp = minTimestamp == null ? timestamp : Math.min(minTimestamp, timestamp);
|
|
311
|
+
maxTimestamp = maxTimestamp == null ? timestamp : Math.max(maxTimestamp, timestamp);
|
|
312
|
+
}
|
|
313
|
+
return { minTimestamp, maxTimestamp };
|
|
314
|
+
};
|
|
315
|
+
var summarizeAiTrainCoverage = (evaluations, now = Date.now()) => {
|
|
316
|
+
const { minTimestamp, maxTimestamp } = getTimestampRange(evaluations);
|
|
317
|
+
const timestamped = evaluations.filter(
|
|
318
|
+
(evaluation) => typeof evaluation.timestamp === "number" && Number.isFinite(evaluation.timestamp)
|
|
319
|
+
).length;
|
|
320
|
+
return {
|
|
321
|
+
timestamped,
|
|
322
|
+
missingTimestamps: evaluations.length - timestamped,
|
|
323
|
+
minTimestamp,
|
|
324
|
+
maxTimestamp,
|
|
325
|
+
spanDays: minTimestamp == null || maxTimestamp == null ? null : (maxTimestamp - minTimestamp) / DAY_MS2,
|
|
326
|
+
dataLagDays: maxTimestamp == null ? null : Math.max(0, (now - maxTimestamp) / DAY_MS2)
|
|
327
|
+
};
|
|
328
|
+
};
|
|
329
|
+
var summarizeAiTrainRejectReasons = (evaluations, limit = 5) => {
|
|
330
|
+
const counts = /* @__PURE__ */ new Map();
|
|
331
|
+
for (const evaluation of evaluations) {
|
|
332
|
+
if (evaluation.aiApproved || !evaluation.rejectReason) {
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
for (const reason of evaluation.rejectReason.split(";").map((part) => part.trim()).filter(Boolean)) {
|
|
336
|
+
counts.set(reason, (counts.get(reason) ?? 0) + 1);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return [...counts.entries()].sort(
|
|
340
|
+
(left, right) => right[1] - left[1] || left[0].localeCompare(right[0])
|
|
341
|
+
).slice(0, limit).map(([reason, count]) => ({ reason, count }));
|
|
342
|
+
};
|
|
343
|
+
var summarizeAiTrainTerminalWindows = (evaluations, windowDays) => {
|
|
344
|
+
const { minTimestamp, maxTimestamp } = getTimestampRange(evaluations);
|
|
345
|
+
if (minTimestamp == null || maxTimestamp == null) {
|
|
346
|
+
return [];
|
|
347
|
+
}
|
|
348
|
+
return windowDays.map((days) => {
|
|
349
|
+
const requestedSince = maxTimestamp - days * DAY_MS2;
|
|
350
|
+
const since = Math.max(minTimestamp, requestedSince);
|
|
351
|
+
const selected = evaluations.filter(
|
|
352
|
+
(evaluation) => typeof evaluation.timestamp === "number" && Number.isFinite(evaluation.timestamp) && evaluation.timestamp >= requestedSince && evaluation.timestamp <= maxTimestamp
|
|
353
|
+
);
|
|
354
|
+
const coverageDays = Math.max(1, (maxTimestamp - since) / DAY_MS2);
|
|
355
|
+
const outcome = summarizeAiTrainEvaluations(selected);
|
|
356
|
+
return {
|
|
357
|
+
label: `last${days}d`,
|
|
358
|
+
days,
|
|
359
|
+
complete: minTimestamp <= requestedSince,
|
|
360
|
+
since,
|
|
361
|
+
until: maxTimestamp,
|
|
362
|
+
coverageDays,
|
|
363
|
+
selected: selected.length,
|
|
364
|
+
approvedPerCalendarDay: outcome.approved / coverageDays,
|
|
365
|
+
outcome,
|
|
366
|
+
topRejectReasons: summarizeAiTrainRejectReasons(selected)
|
|
367
|
+
};
|
|
368
|
+
});
|
|
369
|
+
};
|
|
370
|
+
var readOptionalFile = async (filePath) => {
|
|
371
|
+
try {
|
|
372
|
+
return await import_promises.default.readFile(filePath);
|
|
373
|
+
} catch {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
var getGitLineage = (projectRoot) => {
|
|
378
|
+
try {
|
|
379
|
+
const gitSha = (0, import_node_child_process.execFileSync)("git", ["rev-parse", "HEAD"], {
|
|
380
|
+
cwd: projectRoot,
|
|
381
|
+
encoding: "utf8",
|
|
382
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
383
|
+
}).trim();
|
|
384
|
+
const status = (0, import_node_child_process.execFileSync)(
|
|
385
|
+
"git",
|
|
386
|
+
["status", "--porcelain", "--untracked-files=no"],
|
|
387
|
+
{
|
|
388
|
+
cwd: projectRoot,
|
|
389
|
+
encoding: "utf8",
|
|
390
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
391
|
+
}
|
|
392
|
+
).trim();
|
|
393
|
+
return { gitSha: gitSha || null, gitDirty: status.length > 0 };
|
|
394
|
+
} catch {
|
|
395
|
+
return { gitSha: null, gitDirty: null };
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
var resolveGateFingerprint = async (projectRoot, strategyName, gitSha) => {
|
|
399
|
+
const relativeCandidates = [
|
|
400
|
+
`packages/strategies/src/${strategyName}/adapters/ai.ts`,
|
|
401
|
+
`packages/strategies/src/${strategyName}/guardrails.ts`,
|
|
402
|
+
`packages/strategies/src/${strategyName}/config.ts`,
|
|
403
|
+
"packages/node/src/ai.ts"
|
|
404
|
+
];
|
|
405
|
+
const optionalSourceEntries = await Promise.all(
|
|
406
|
+
relativeCandidates.map(async (relativePath) => {
|
|
407
|
+
const content = await readOptionalFile(
|
|
408
|
+
import_node_path.default.join(projectRoot, relativePath)
|
|
409
|
+
);
|
|
410
|
+
return content == null ? null : { relativePath, content };
|
|
411
|
+
})
|
|
412
|
+
);
|
|
413
|
+
const sourceEntries = [];
|
|
414
|
+
for (const entry of optionalSourceEntries) {
|
|
415
|
+
if (entry != null) {
|
|
416
|
+
sourceEntries.push(entry);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
420
|
+
hash.update(strategyName);
|
|
421
|
+
if (sourceEntries.length) {
|
|
422
|
+
for (const entry of sourceEntries) {
|
|
423
|
+
hash.update(entry.relativePath);
|
|
424
|
+
hash.update(entry.content);
|
|
425
|
+
}
|
|
426
|
+
} else {
|
|
427
|
+
hash.update(gitSha ?? "unknown-git-sha");
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
gateFingerprint: hash.digest("hex").slice(0, 16),
|
|
431
|
+
gateFingerprintFiles: sourceEntries.map((entry) => entry.relativePath)
|
|
432
|
+
};
|
|
433
|
+
};
|
|
434
|
+
var buildAiTrainLineage = async ({
|
|
435
|
+
projectRoot,
|
|
436
|
+
strategyName,
|
|
437
|
+
configIds,
|
|
438
|
+
runContext,
|
|
439
|
+
env = process.env
|
|
440
|
+
}) => {
|
|
441
|
+
const { gitSha, gitDirty } = getGitLineage(projectRoot);
|
|
442
|
+
const gate = await resolveGateFingerprint(projectRoot, strategyName, gitSha);
|
|
443
|
+
const normalizedConfigIds = [...new Set(configIds.filter(Boolean))].sort();
|
|
444
|
+
const context = {
|
|
445
|
+
...Object.fromEntries(
|
|
446
|
+
RESEARCH_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
|
|
447
|
+
),
|
|
448
|
+
...runContext
|
|
449
|
+
};
|
|
450
|
+
return {
|
|
451
|
+
gitSha,
|
|
452
|
+
gitDirty,
|
|
453
|
+
...gate,
|
|
454
|
+
configIdsFingerprint: fingerprintResearchValue(normalizedConfigIds),
|
|
455
|
+
configIds: normalizedConfigIds,
|
|
456
|
+
contextFingerprint: fingerprintResearchValue(context),
|
|
457
|
+
context
|
|
458
|
+
};
|
|
459
|
+
};
|
|
460
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
461
|
+
0 && (module.exports = {
|
|
462
|
+
buildAiTrainLineage,
|
|
463
|
+
fingerprintResearchValue,
|
|
464
|
+
summarizeAiTrainCoverage,
|
|
465
|
+
summarizeAiTrainRejectReasons,
|
|
466
|
+
summarizeAiTrainTerminalWindows
|
|
467
|
+
});
|