@tradejs/cli 2.0.21 → 3.0.0
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 +242 -69
- package/dist/lib/aiTrainOptions.js +36 -0
- package/dist/lib/coreResearch.js +73 -8
- package/dist/lib/strategyRelease.js +71 -14
- package/dist/scripts/agentRun.js +22 -23
- package/dist/scripts/aiPocketSearch.js +2 -1
- package/dist/scripts/aiTrain.js +64 -13
- package/dist/scripts/coreResearch.js +73 -8
- package/dist/scripts/runtimeScorecard.js +35 -2
- package/dist/scripts/strategyRelease.js +71 -14
- package/package.json +9 -9
package/dist/cli.js
CHANGED
|
@@ -241,7 +241,7 @@ var agentRun_exports = {};
|
|
|
241
241
|
__export(agentRun_exports, {
|
|
242
242
|
main: () => main
|
|
243
243
|
});
|
|
244
|
-
var import_args, import_chalk, import_promises, import_node_os, import_node_path, import_node_child_process, import_constants, import_redis, import_userSettings, flags, projectRoot, AGENT_SYSTEM_PROMPT, trimTextTail, escapeHtml, isOpenRouterEndpoint, resolveChatCompletionsUrl, runCommand, runCommandOrThrow, listFilesRecursive, collectStrategyContextFiles, renderFileContext, buildPrompt, extractAssistantContent, requestAgentDiff, applyDiff, getChangedFilesFromGit, runValidationSuite, buildAgentTelegramReport, loadRun, saveAgentRun, resolveGithubRepository, createGithubPullRequest, createWorktree, cleanupWorktree, main;
|
|
244
|
+
var import_args, import_chalk, import_promises, import_node_os, import_node_path, import_node_child_process, import_constants, import_aiEndpoints, import_redis, import_userSettings, flags, projectRoot, AGENT_SYSTEM_PROMPT, trimTextTail, escapeHtml, isOpenRouterEndpoint, resolveChatCompletionsUrl, runCommand, runCommandOrThrow, listFilesRecursive, collectStrategyContextFiles, renderFileContext, buildPrompt, extractAssistantContent, requestAgentDiff, applyDiff, getChangedFilesFromGit, runValidationSuite, buildAgentTelegramReport, loadRun, saveAgentRun, resolveGithubRepository, createGithubPullRequest, createWorktree, cleanupWorktree, main;
|
|
245
245
|
var init_agentRun = __esm({
|
|
246
246
|
"src/scripts/agentRun.ts"() {
|
|
247
247
|
"use strict";
|
|
@@ -252,6 +252,7 @@ var init_agentRun = __esm({
|
|
|
252
252
|
import_node_path = __toESM(require("path"));
|
|
253
253
|
import_node_child_process = require("child_process");
|
|
254
254
|
import_constants = require("@tradejs/core/constants");
|
|
255
|
+
import_aiEndpoints = require("@tradejs/core/aiEndpoints");
|
|
255
256
|
import_redis = require("@tradejs/infra/redis");
|
|
256
257
|
import_userSettings = require("@tradejs/infra/userSettings");
|
|
257
258
|
init_telegramReports();
|
|
@@ -439,35 +440,33 @@ Rules:
|
|
|
439
440
|
};
|
|
440
441
|
requestAgentDiff = async (userName2, messages) => {
|
|
441
442
|
const settings = await (0, import_userSettings.getUserSettings)(userName2);
|
|
442
|
-
|
|
443
|
+
const aiApiEndpoint = (0, import_aiEndpoints.normalizeAiEndpoint)(settings.AI_API_ENDPOINT);
|
|
444
|
+
if (!settings.AI_API_KEY || !aiApiEndpoint) {
|
|
443
445
|
throw new Error(`AI settings are incomplete for user ${userName2}`);
|
|
444
446
|
}
|
|
445
|
-
if (!isOpenRouterEndpoint(
|
|
447
|
+
if (!isOpenRouterEndpoint(aiApiEndpoint)) {
|
|
446
448
|
throw new Error(
|
|
447
|
-
`Research agent requires OpenRouter endpoint, got: ${
|
|
449
|
+
`Research agent requires OpenRouter endpoint, got: ${aiApiEndpoint}`
|
|
448
450
|
);
|
|
449
451
|
}
|
|
450
|
-
const response = await fetch(
|
|
451
|
-
|
|
452
|
-
{
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
452
|
+
const response = await fetch(resolveChatCompletionsUrl(aiApiEndpoint), {
|
|
453
|
+
method: "POST",
|
|
454
|
+
headers: {
|
|
455
|
+
Authorization: `Bearer ${settings.AI_API_KEY}`,
|
|
456
|
+
"Content-Type": "application/json",
|
|
457
|
+
"HTTP-Referer": "https://tradejs.dev",
|
|
458
|
+
"X-Title": "TradeJS Research Agent"
|
|
459
|
+
},
|
|
460
|
+
body: JSON.stringify({
|
|
461
|
+
model: RESEARCH_AGENT_MODEL,
|
|
462
|
+
messages,
|
|
463
|
+
reasoning: {
|
|
464
|
+
effort: RESEARCH_AGENT_REASONING_EFFORT,
|
|
465
|
+
exclude: true
|
|
459
466
|
},
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
reasoning: {
|
|
464
|
-
effort: RESEARCH_AGENT_REASONING_EFFORT,
|
|
465
|
-
exclude: true
|
|
466
|
-
},
|
|
467
|
-
temperature: 0.2
|
|
468
|
-
})
|
|
469
|
-
}
|
|
470
|
-
);
|
|
467
|
+
temperature: 0.2
|
|
468
|
+
})
|
|
469
|
+
});
|
|
471
470
|
if (!response.ok) {
|
|
472
471
|
throw new Error(
|
|
473
472
|
`OpenRouter request failed (${response.status}): ${await response.text()}`
|
|
@@ -1857,7 +1856,7 @@ var init_aiTrainMetrics = __esm({
|
|
|
1857
1856
|
});
|
|
1858
1857
|
|
|
1859
1858
|
// src/lib/aiTrainOptions.ts
|
|
1860
|
-
var parseTimestampFilter, parseTrailingPeriodMs, parseQualityThresholds, parseTerminalWindowDays, parseDumpFeatureMode, hasCliOption, resolveAiTrainRecentLimit;
|
|
1859
|
+
var parseTimestampFilter, parseTrailingPeriodMs, parseQualityThresholds, parseTerminalWindowDays, parseAiTrainDirectionPolicy, assertAiTrainDirectionPolicy, parseDumpFeatureMode, hasCliOption, resolveAiTrainRecentLimit;
|
|
1861
1860
|
var init_aiTrainOptions = __esm({
|
|
1862
1861
|
"src/lib/aiTrainOptions.ts"() {
|
|
1863
1862
|
"use strict";
|
|
@@ -1913,6 +1912,38 @@ var init_aiTrainOptions = __esm({
|
|
|
1913
1912
|
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);
|
|
1914
1913
|
return [...new Set(raw)].sort((left, right) => right - left);
|
|
1915
1914
|
};
|
|
1915
|
+
parseAiTrainDirectionPolicy = (value) => {
|
|
1916
|
+
const normalized = String(value ?? "both").trim().toLowerCase().replaceAll("-", "_");
|
|
1917
|
+
if (normalized === "both" || normalized === "long_only" || normalized === "short_only" || normalized === "direction_aware") {
|
|
1918
|
+
return normalized;
|
|
1919
|
+
}
|
|
1920
|
+
throw new Error(
|
|
1921
|
+
`Invalid --directionPolicy value "${String(value)}". Use both, long_only, short_only, or direction_aware.`
|
|
1922
|
+
);
|
|
1923
|
+
};
|
|
1924
|
+
assertAiTrainDirectionPolicy = ({
|
|
1925
|
+
directionPolicy,
|
|
1926
|
+
byDirection,
|
|
1927
|
+
terminalWindows
|
|
1928
|
+
}) => {
|
|
1929
|
+
const suppressedDirection = directionPolicy === "long_only" ? "SHORT" : directionPolicy === "short_only" ? "LONG" : null;
|
|
1930
|
+
if (!suppressedDirection) return;
|
|
1931
|
+
for (const scope of [{ label: "full", byDirection }, ...terminalWindows]) {
|
|
1932
|
+
const summary = scope.byDirection.find(
|
|
1933
|
+
(entry) => entry.direction === suppressedDirection
|
|
1934
|
+
);
|
|
1935
|
+
if (!summary) {
|
|
1936
|
+
throw new Error(
|
|
1937
|
+
`${scope.label} ${suppressedDirection} direction summary is required for ${directionPolicy}`
|
|
1938
|
+
);
|
|
1939
|
+
}
|
|
1940
|
+
if (summary.summary.approved !== 0) {
|
|
1941
|
+
throw new Error(
|
|
1942
|
+
`${scope.label} has ${summary.summary.approved} ${suppressedDirection} approved rows under ${directionPolicy}`
|
|
1943
|
+
);
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
};
|
|
1916
1947
|
parseDumpFeatureMode = (value) => {
|
|
1917
1948
|
const text = String(value ?? "none").trim().toLowerCase();
|
|
1918
1949
|
if (!text || text === "none" || text === "false" || text === "0") {
|
|
@@ -4333,7 +4364,7 @@ var aiPocketSearch_exports = {};
|
|
|
4333
4364
|
__export(aiPocketSearch_exports, {
|
|
4334
4365
|
main: () => main3
|
|
4335
4366
|
});
|
|
4336
|
-
var import_args3, import_chalk4, import_promises4, import_path2, import_progress2, import_ai3, import_ai4, ListIt2, flags3, createListIt2, createTable2, formatRatio, formatProfit, formatNumber2, colorizeRatio, colorizeProfit, colorizeNumber, toReportTimestamp, markdownPlainTable, buildPendingMarkdownReport, normalizeInt, normalizePositiveInt, normalizeNonNegativeNumber, normalizeRatio, normalizeUnitRatio, normalizeFeatureProfile, normalizeFeaturePolicy, normalizeCoverageMode, normalizeCadenceMode, normalizeObjective, normalizeQuality, isAiApproval, listMergedFiles, deriveStrategyNameFromFile, getMergedGroupId, resolveMarkdownReportPath, sortDatasetPartPaths, resolveMergedDatasetFiles, getDatasetRowTimestamp, findMaxSelectedTimestamp, getDeterministicModelCandidate, resolveScopeRows, printSection, buildSummaryRows, buildPocketRows, buildQualityRows, tickProgressBarTo, searchPhaseLabels, hasCliOption2, main3;
|
|
4367
|
+
var import_args3, import_chalk4, import_promises4, import_path2, import_progress2, import_ai3, import_ai4, import_registry, ListIt2, flags3, createListIt2, createTable2, formatRatio, formatProfit, formatNumber2, colorizeRatio, colorizeProfit, colorizeNumber, toReportTimestamp, markdownPlainTable, buildPendingMarkdownReport, normalizeInt, normalizePositiveInt, normalizeNonNegativeNumber, normalizeRatio, normalizeUnitRatio, normalizeFeatureProfile, normalizeFeaturePolicy, normalizeCoverageMode, normalizeCadenceMode, normalizeObjective, normalizeQuality, isAiApproval, listMergedFiles, deriveStrategyNameFromFile, getMergedGroupId, resolveMarkdownReportPath, sortDatasetPartPaths, resolveMergedDatasetFiles, getDatasetRowTimestamp, findMaxSelectedTimestamp, getDeterministicModelCandidate, resolveScopeRows, printSection, buildSummaryRows, buildPocketRows, buildQualityRows, tickProgressBarTo, searchPhaseLabels, hasCliOption2, main3;
|
|
4337
4368
|
var init_aiPocketSearch2 = __esm({
|
|
4338
4369
|
"src/scripts/aiPocketSearch.ts"() {
|
|
4339
4370
|
"use strict";
|
|
@@ -4344,6 +4375,7 @@ var init_aiPocketSearch2 = __esm({
|
|
|
4344
4375
|
import_progress2 = __toESM(require("progress"));
|
|
4345
4376
|
import_ai3 = require("@tradejs/infra/ai");
|
|
4346
4377
|
import_ai4 = require("@tradejs/node/ai");
|
|
4378
|
+
import_registry = require("@tradejs/node/registry");
|
|
4347
4379
|
init_aiTrainDataset();
|
|
4348
4380
|
init_aiTrainMetrics();
|
|
4349
4381
|
init_aiTrainOptions();
|
|
@@ -5057,7 +5089,7 @@ var init_aiPocketSearch2 = __esm({
|
|
|
5057
5089
|
const explicitReportFile = String(flags3.reportFile || "").trim();
|
|
5058
5090
|
const generatedAt = Date.now();
|
|
5059
5091
|
const excludedFeaturePaths = /* @__PURE__ */ new Map();
|
|
5060
|
-
await (0,
|
|
5092
|
+
await (0, import_registry.ensureStrategyPluginsLoaded)();
|
|
5061
5093
|
const filePaths = await resolveMergedDatasetFiles({
|
|
5062
5094
|
outDir,
|
|
5063
5095
|
strategyName,
|
|
@@ -6856,7 +6888,7 @@ var aiTrain_exports = {};
|
|
|
6856
6888
|
__export(aiTrain_exports, {
|
|
6857
6889
|
main: () => main4
|
|
6858
6890
|
});
|
|
6859
|
-
var import_args4, import_chalk5, import_promises7, import_path3, import_progress3, import_constants3, import_ai5, import_strategyReleaseEvidence, import_redis3, import_ai6, import_constants4, ListIt3, flags4, createListIt3, createTable3, formatRatio3, formatProfit2, formatMetricNumber, colorizeRatio2, colorizePercent, colorizeProfit2, colorizeMetricNumber, colorizeQuality, normalizeInt2, normalizePositiveInt2, normalizeNonNegativeNumber2, normalizeQuality2, isProfitableTrade, isAiApproval2, listMergedFiles2, deriveStrategyNameFromFile2, getMergedGroupId2, sortDatasetPartPaths2, resolveMergedDatasetFiles2, resolvePromptRunContext, getDeterministicGateContext, resolveDeterministicGateEvaluation, summarizeDeterministicGateEvaluations, printSection2, persistAiChartSnapshot, resolveDatasetFiles, getDatasetRowTimestamp2, findMaxSelectedTimestamp2, main4;
|
|
6891
|
+
var import_args4, import_chalk5, import_promises7, import_path3, import_progress3, import_constants3, import_ai5, import_strategyReleaseEvidence, import_redis3, import_ai6, import_registry2, import_constants4, ListIt3, flags4, createListIt3, createTable3, formatRatio3, formatProfit2, formatMetricNumber, colorizeRatio2, colorizePercent, colorizeProfit2, colorizeMetricNumber, colorizeQuality, normalizeInt2, normalizePositiveInt2, normalizeNonNegativeNumber2, normalizeQuality2, isProfitableTrade, isAiApproval2, listMergedFiles2, deriveStrategyNameFromFile2, getMergedGroupId2, sortDatasetPartPaths2, resolveMergedDatasetFiles2, resolvePromptRunContext, getDeterministicGateContext, resolveDeterministicGateEvaluation, summarizeDeterministicGateEvaluations, printSection2, persistAiChartSnapshot, resolveDatasetFiles, getDatasetRowTimestamp2, findMaxSelectedTimestamp2, main4;
|
|
6860
6892
|
var init_aiTrain = __esm({
|
|
6861
6893
|
"src/scripts/aiTrain.ts"() {
|
|
6862
6894
|
"use strict";
|
|
@@ -6870,6 +6902,7 @@ var init_aiTrain = __esm({
|
|
|
6870
6902
|
import_strategyReleaseEvidence = require("@tradejs/infra/strategyReleaseEvidence");
|
|
6871
6903
|
import_redis3 = require("@tradejs/infra/redis");
|
|
6872
6904
|
import_ai6 = require("@tradejs/node/ai");
|
|
6905
|
+
import_registry2 = require("@tradejs/node/registry");
|
|
6873
6906
|
import_constants4 = require("@tradejs/node/constants");
|
|
6874
6907
|
init_aiTrainCharts();
|
|
6875
6908
|
init_aiTrainMetrics();
|
|
@@ -6911,6 +6944,11 @@ var init_aiTrain = __esm({
|
|
|
6911
6944
|
"Minimum AI quality required to approve entry",
|
|
6912
6945
|
4
|
|
6913
6946
|
);
|
|
6947
|
+
import_args4.default.option(
|
|
6948
|
+
["Y", "directionPolicy"],
|
|
6949
|
+
"Evidence label for the enforced gate policy: both, long_only, short_only, or direction_aware",
|
|
6950
|
+
"both"
|
|
6951
|
+
);
|
|
6914
6952
|
import_args4.default.option(
|
|
6915
6953
|
["l", "localOnly"],
|
|
6916
6954
|
"Replay deterministic adapter gate without AI provider calls",
|
|
@@ -7301,6 +7339,7 @@ var init_aiTrain = __esm({
|
|
|
7301
7339
|
});
|
|
7302
7340
|
const qualityThresholds = parseQualityThresholds(flags4.qualityThresholds);
|
|
7303
7341
|
const terminalWindowDays = parseTerminalWindowDays(flags4.terminalWindows);
|
|
7342
|
+
const directionPolicy = parseAiTrainDirectionPolicy(flags4.directionPolicy);
|
|
7304
7343
|
const dumpEvaluationsPath = String(flags4.dumpEvaluations || "").trim();
|
|
7305
7344
|
const dumpFeatureMode = parseDumpFeatureMode(flags4.dumpFeatures);
|
|
7306
7345
|
if (dumpFeatureMode !== "none" && !dumpEvaluationsPath) {
|
|
@@ -7322,7 +7361,7 @@ var init_aiTrain = __esm({
|
|
|
7322
7361
|
const userName2 = String(flags4.user || "root").trim() || "root";
|
|
7323
7362
|
const model = String(flags4.model || import_ai6.DEFAULT_AI_MODEL).trim() || import_ai6.DEFAULT_AI_MODEL;
|
|
7324
7363
|
const parallel = normalizePositiveInt2(flags4.parallel, import_constants4.AI_CONCURRENCY_LIMIT);
|
|
7325
|
-
await (0,
|
|
7364
|
+
await (0, import_registry2.ensureStrategyPluginsLoaded)();
|
|
7326
7365
|
const filePaths = await resolveDatasetFiles();
|
|
7327
7366
|
const datasetId = getMergedGroupId2(filePaths[0] || "")?.mergeId;
|
|
7328
7367
|
const { totalRows, selectedRows } = await (0, import_ai5.countAiDatasetRows)({
|
|
@@ -7515,18 +7554,29 @@ var init_aiTrain = __esm({
|
|
|
7515
7554
|
finalEvaluations,
|
|
7516
7555
|
terminalWindowDays
|
|
7517
7556
|
);
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
configIds: finalEvaluations.map((evaluation) => evaluation.configId),
|
|
7523
|
-
sourceSha256s: await Promise.all(filePaths.map(import_strategyReleaseEvidence.strategyEvidenceFileSha256)),
|
|
7524
|
-
runContext: {
|
|
7525
|
-
mode: localOnly ? "local-deterministic" : "llm",
|
|
7526
|
-
model: localOnly ? "local-deterministic" : model,
|
|
7527
|
-
minQuality
|
|
7528
|
-
}
|
|
7557
|
+
assertAiTrainDirectionPolicy({
|
|
7558
|
+
directionPolicy,
|
|
7559
|
+
byDirection: directionSummaries,
|
|
7560
|
+
terminalWindows
|
|
7529
7561
|
});
|
|
7562
|
+
const topRejectReasons = summarizeAiTrainRejectReasons(finalEvaluations);
|
|
7563
|
+
const lineage = {
|
|
7564
|
+
...await buildAiTrainLineage({
|
|
7565
|
+
projectRoot: String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd(),
|
|
7566
|
+
strategyName,
|
|
7567
|
+
configIds: finalEvaluations.map((evaluation) => evaluation.configId),
|
|
7568
|
+
sourceSha256s: await Promise.all(
|
|
7569
|
+
filePaths.map(import_strategyReleaseEvidence.strategyEvidenceFileSha256)
|
|
7570
|
+
),
|
|
7571
|
+
runContext: {
|
|
7572
|
+
mode: localOnly ? "local-deterministic" : "llm",
|
|
7573
|
+
model: localOnly ? "local-deterministic" : model,
|
|
7574
|
+
minQuality,
|
|
7575
|
+
directionPolicy
|
|
7576
|
+
}
|
|
7577
|
+
}),
|
|
7578
|
+
directionPolicy
|
|
7579
|
+
};
|
|
7530
7580
|
const evaluated = summary.correct + summary.incorrect;
|
|
7531
7581
|
const result = {
|
|
7532
7582
|
run: {
|
|
@@ -7548,7 +7598,8 @@ var init_aiTrain = __esm({
|
|
|
7548
7598
|
model: localOnly ? "local-deterministic" : model,
|
|
7549
7599
|
parallel: concurrency,
|
|
7550
7600
|
dumpFeatures: dumpFeatureMode,
|
|
7551
|
-
terminalWindows: terminalWindowDays
|
|
7601
|
+
terminalWindows: terminalWindowDays,
|
|
7602
|
+
directionPolicy
|
|
7552
7603
|
},
|
|
7553
7604
|
outcome: summary,
|
|
7554
7605
|
byDirection: directionSummaries,
|
|
@@ -11495,13 +11546,13 @@ var init_hyperliquidWhaleBackfill = __esm({
|
|
|
11495
11546
|
});
|
|
11496
11547
|
|
|
11497
11548
|
// src/lib/marketContextPrepare.ts
|
|
11498
|
-
var import_chalk9, import_marketContext3,
|
|
11549
|
+
var import_chalk9, import_marketContext3, import_registry3, uniqueReasons, shouldPrepareDerivativesContextForRun, shouldPrepareBinanceMarketContextForRun, shouldPrepareCoinMarketCapContextForRun, shouldPrepareHyperliquidWhaleContextForRun, resolveCoreContextSources, resolveMarketContextRunRequirements, resolveCoinMarketCapBackfillForMode, buildCoinMarketCapBackfillParams, buildBinanceMarketBackfillParams, buildDerivativesBackfillParams, prepareMarketContextForRun;
|
|
11499
11550
|
var init_marketContextPrepare = __esm({
|
|
11500
11551
|
"src/lib/marketContextPrepare.ts"() {
|
|
11501
11552
|
"use strict";
|
|
11502
11553
|
import_chalk9 = __toESM(require("chalk"));
|
|
11503
11554
|
import_marketContext3 = require("@tradejs/infra/timescale/marketContext");
|
|
11504
|
-
|
|
11555
|
+
import_registry3 = require("@tradejs/node/registry");
|
|
11505
11556
|
init_binanceMarketContextBackfill();
|
|
11506
11557
|
init_derivativesContextBackfill();
|
|
11507
11558
|
init_coinMarketCapContextBackfill();
|
|
@@ -11579,11 +11630,11 @@ var init_marketContextPrepare = __esm({
|
|
|
11579
11630
|
resolveCoreContextSources = async (params) => {
|
|
11580
11631
|
const strategyNames = [...new Set(params.strategyNames ?? [])];
|
|
11581
11632
|
if (!strategyNames.length) return [];
|
|
11582
|
-
await (0,
|
|
11633
|
+
await (0, import_registry3.ensureStrategyPluginsLoaded)(params.projectRoot);
|
|
11583
11634
|
return [
|
|
11584
11635
|
...new Set(
|
|
11585
11636
|
strategyNames.flatMap(
|
|
11586
|
-
(strategyName) => (0,
|
|
11637
|
+
(strategyName) => (0, import_registry3.getStrategyManifest)(strategyName, params.projectRoot)?.contextRequirements?.core ?? []
|
|
11587
11638
|
)
|
|
11588
11639
|
)
|
|
11589
11640
|
];
|
|
@@ -21566,7 +21617,7 @@ var init_comparison = __esm({
|
|
|
21566
21617
|
});
|
|
21567
21618
|
|
|
21568
21619
|
// src/lib/coreResearch/dataset.ts
|
|
21569
|
-
var import_node_crypto8, import_node_fs4, import_node_path10, import_node_readline, asRecord3, finiteNumber4, normalizeDirection, findStrategySetupIdentity, classifyBreadth, classifyDerivatives, resolveCoreResearchRegime, normalizeCompletedTrade, completedTradeIdentity, compareText, compareTrades, readCoreResearchVariant;
|
|
21620
|
+
var import_node_crypto8, import_node_fs4, import_node_path10, import_node_readline, asRecord3, finiteNumber4, normalizeDirection, findStrategySetupIdentity, classifyBreadth, classifyDerivatives, resolveCoreResearchRegime, normalizeCompletedTrade, completedTradeIdentity, positionCycleIdentity, sumBy, weightedPrice, aggregatePositionCycles, compareText, compareTrades, readCoreResearchVariant;
|
|
21570
21621
|
var init_dataset = __esm({
|
|
21571
21622
|
"src/lib/coreResearch/dataset.ts"() {
|
|
21572
21623
|
"use strict";
|
|
@@ -21687,6 +21738,7 @@ var init_dataset = __esm({
|
|
|
21687
21738
|
runId: row.backtestRunId ?? null,
|
|
21688
21739
|
configId: row.configId?.trim() || "<missing-config-id>",
|
|
21689
21740
|
signalId,
|
|
21741
|
+
positionCycleId: typeof result.positionCycleId === "string" && result.positionCycleId.trim() ? result.positionCycleId.trim() : null,
|
|
21690
21742
|
setupIdentity,
|
|
21691
21743
|
setupIdentitySource: explicitIdentity ? "research.setupIdentity" : strategyIdentity ? "strategy-context" : "signal-time-fallback",
|
|
21692
21744
|
strategy: row.strategyName,
|
|
@@ -21710,6 +21762,69 @@ var init_dataset = __esm({
|
|
|
21710
21762
|
};
|
|
21711
21763
|
};
|
|
21712
21764
|
completedTradeIdentity = (trade) => [trade.runId ?? "", trade.configId, trade.signalId, trade.symbol].join("|");
|
|
21765
|
+
positionCycleIdentity = (trade) => trade.positionCycleId ? [
|
|
21766
|
+
trade.runId ?? "",
|
|
21767
|
+
trade.configId,
|
|
21768
|
+
trade.strategy,
|
|
21769
|
+
trade.symbol,
|
|
21770
|
+
trade.direction,
|
|
21771
|
+
trade.positionCycleId
|
|
21772
|
+
].join("|") : null;
|
|
21773
|
+
sumBy = (trades, select) => trades.reduce((sum3, trade) => sum3 + select(trade), 0);
|
|
21774
|
+
weightedPrice = (trades, select) => {
|
|
21775
|
+
const withPrice = trades.filter((trade) => select(trade) != null);
|
|
21776
|
+
const qty = sumBy(withPrice, (trade) => trade.qty);
|
|
21777
|
+
if (qty <= 0) return null;
|
|
21778
|
+
return sumBy(withPrice, (trade) => Number(select(trade)) * trade.qty) / qty;
|
|
21779
|
+
};
|
|
21780
|
+
aggregatePositionCycles = (trades) => {
|
|
21781
|
+
const standalone = [];
|
|
21782
|
+
const cycles = /* @__PURE__ */ new Map();
|
|
21783
|
+
for (const trade of trades) {
|
|
21784
|
+
const identity = positionCycleIdentity(trade);
|
|
21785
|
+
if (!identity) {
|
|
21786
|
+
standalone.push(trade);
|
|
21787
|
+
continue;
|
|
21788
|
+
}
|
|
21789
|
+
const cycle = cycles.get(identity) ?? [];
|
|
21790
|
+
cycle.push(trade);
|
|
21791
|
+
cycles.set(identity, cycle);
|
|
21792
|
+
}
|
|
21793
|
+
for (const [identity, cycle] of cycles) {
|
|
21794
|
+
const primaryRows = cycle.filter(
|
|
21795
|
+
(trade) => trade.signalId === trade.positionCycleId
|
|
21796
|
+
);
|
|
21797
|
+
if (primaryRows.length !== 1) {
|
|
21798
|
+
throw new Error(
|
|
21799
|
+
`Position cycle ${identity} must contain exactly one opening row; found ${primaryRows.length}`
|
|
21800
|
+
);
|
|
21801
|
+
}
|
|
21802
|
+
const primary = primaryRows[0];
|
|
21803
|
+
if (cycle.some(
|
|
21804
|
+
(trade) => trade.exitTimestamp !== primary.exitTimestamp || trade.exitReason !== primary.exitReason
|
|
21805
|
+
)) {
|
|
21806
|
+
throw new Error(
|
|
21807
|
+
`Position cycle ${identity} contains inconsistent final exits`
|
|
21808
|
+
);
|
|
21809
|
+
}
|
|
21810
|
+
if (cycle.length === 1) {
|
|
21811
|
+
standalone.push(primary);
|
|
21812
|
+
continue;
|
|
21813
|
+
}
|
|
21814
|
+
standalone.push({
|
|
21815
|
+
...primary,
|
|
21816
|
+
entryTimestamp: Math.min(...cycle.map((trade) => trade.entryTimestamp)),
|
|
21817
|
+
entryPrice: weightedPrice(cycle, (trade) => trade.entryPrice) ?? 0,
|
|
21818
|
+
exitPrice: weightedPrice(cycle, (trade) => trade.exitPrice),
|
|
21819
|
+
qty: sumBy(cycle, (trade) => trade.qty),
|
|
21820
|
+
netProfit: sumBy(cycle, (trade) => trade.netProfit),
|
|
21821
|
+
grossProfit: sumBy(cycle, (trade) => trade.grossProfit),
|
|
21822
|
+
totalFee: sumBy(cycle, (trade) => trade.totalFee),
|
|
21823
|
+
totalSlippageCost: sumBy(cycle, (trade) => trade.totalSlippageCost)
|
|
21824
|
+
});
|
|
21825
|
+
}
|
|
21826
|
+
return standalone;
|
|
21827
|
+
};
|
|
21713
21828
|
compareText = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
21714
21829
|
compareTrades = (left, right) => left.exitTimestamp - right.exitTimestamp || compareText(left.symbol, right.symbol) || compareText(left.direction, right.direction) || compareText(left.signalId, right.signalId) || compareText(left.sourceFile, right.sourceFile) || left.sourceLine - right.sourceLine;
|
|
21715
21830
|
readCoreResearchVariant = async (variant) => {
|
|
@@ -21786,7 +21901,7 @@ var init_dataset = __esm({
|
|
|
21786
21901
|
variant,
|
|
21787
21902
|
files: sources,
|
|
21788
21903
|
duplicateRowsDropped,
|
|
21789
|
-
trades: trades.sort(compareTrades)
|
|
21904
|
+
trades: aggregatePositionCycles(trades).sort(compareTrades)
|
|
21790
21905
|
};
|
|
21791
21906
|
};
|
|
21792
21907
|
}
|
|
@@ -22293,13 +22408,17 @@ var init_orchestrator = __esm({
|
|
|
22293
22408
|
const loaded = await Promise.all(
|
|
22294
22409
|
analysisSpec.variants.map(readCoreResearchVariant)
|
|
22295
22410
|
);
|
|
22411
|
+
const periodDays = (spec.window.end - spec.window.start) / DAY_MS11;
|
|
22296
22412
|
const windowed = loaded.map((entry) => ({
|
|
22297
22413
|
...entry,
|
|
22414
|
+
reconciliationMetrics: summarizeCoreResearchCohorts(
|
|
22415
|
+
entry.trades,
|
|
22416
|
+
periodDays
|
|
22417
|
+
).ALL,
|
|
22298
22418
|
trades: entry.trades.filter(
|
|
22299
22419
|
(trade) => trade.exitTimestamp >= spec.window.start && trade.exitTimestamp < spec.window.end
|
|
22300
22420
|
)
|
|
22301
22421
|
}));
|
|
22302
|
-
const periodDays = (spec.window.end - spec.window.start) / DAY_MS11;
|
|
22303
22422
|
const analyses = await Promise.all(
|
|
22304
22423
|
windowed.map(async (entry) => ({
|
|
22305
22424
|
variant: entry.variant,
|
|
@@ -22357,12 +22476,9 @@ var init_orchestrator = __esm({
|
|
|
22357
22476
|
reconciliation: await reconcileCoreResearchVariant({
|
|
22358
22477
|
variant: entry.variant,
|
|
22359
22478
|
spec,
|
|
22360
|
-
|
|
22361
|
-
|
|
22362
|
-
|
|
22363
|
-
start: spec.window.start,
|
|
22364
|
-
end: spec.window.end
|
|
22365
|
-
}).cohorts.ALL
|
|
22479
|
+
// Redis result.stat includes the runner's terminal close at window.end.
|
|
22480
|
+
// Keep half-open research metrics, but reconcile the complete run export.
|
|
22481
|
+
exportMetrics: entry.reconciliationMetrics
|
|
22366
22482
|
}),
|
|
22367
22483
|
supplemental: {
|
|
22368
22484
|
coldStart: Object.fromEntries(
|
|
@@ -25948,9 +26064,10 @@ async function collectReleaseEvidenceReferences(references) {
|
|
|
25948
26064
|
if (reference.kind === "ai_gate") {
|
|
25949
26065
|
const outcome = nested(artifact.outcome);
|
|
25950
26066
|
const approvedRisk = nested(outcome?.approvedRisk);
|
|
25951
|
-
const
|
|
25952
|
-
|
|
25953
|
-
|
|
26067
|
+
const run = nested(artifact.run);
|
|
26068
|
+
const research = nested(artifact.research);
|
|
26069
|
+
const researchLineage = nested(research?.lineage);
|
|
26070
|
+
const terminalWindows = array(research?.terminalWindows).map(nested).filter((entry) => entry != null);
|
|
25954
26071
|
const hasLongShortDirections = (value) => {
|
|
25955
26072
|
const directions = new Map(
|
|
25956
26073
|
array(value).map(nested).filter(
|
|
@@ -25964,6 +26081,22 @@ async function collectReleaseEvidenceReferences(references) {
|
|
|
25964
26081
|
const directionalEvidenceComplete = hasLongShortDirections(artifact.byDirection) && terminalWindows.every(
|
|
25965
26082
|
(window) => hasLongShortDirections(window.byDirection)
|
|
25966
26083
|
);
|
|
26084
|
+
const directionPolicy = finiteDirectionPolicy(researchLineage?.directionPolicy) ?? "both";
|
|
26085
|
+
const runDirectionPolicy = finiteDirectionPolicy(run?.directionPolicy) ?? "both";
|
|
26086
|
+
const suppressedDirection = directionPolicy === "long_only" ? "SHORT" : directionPolicy === "short_only" ? "LONG" : null;
|
|
26087
|
+
const approvedForDirection = (value, direction) => {
|
|
26088
|
+
const entry = array(value).map(nested).find((candidate) => candidate?.direction === direction);
|
|
26089
|
+
return Number(nested(entry?.summary)?.approved ?? Number.NaN);
|
|
26090
|
+
};
|
|
26091
|
+
const directionPolicyValid = runDirectionPolicy === directionPolicy && (suppressedDirection == null || approvedForDirection(
|
|
26092
|
+
artifact.byDirection,
|
|
26093
|
+
suppressedDirection
|
|
26094
|
+
) === 0 && terminalWindows.every(
|
|
26095
|
+
(window) => approvedForDirection(
|
|
26096
|
+
window.byDirection,
|
|
26097
|
+
suppressedDirection
|
|
26098
|
+
) === 0
|
|
26099
|
+
));
|
|
25967
26100
|
const terminalsPass = terminalWindows.length > 0 && terminalWindows.every((window) => {
|
|
25968
26101
|
const terminalOutcome = nested(window.outcome);
|
|
25969
26102
|
return window.complete === true && Number(terminalOutcome?.approved ?? 0) > 0 && Number(
|
|
@@ -25971,7 +26104,7 @@ async function collectReleaseEvidenceReferences(references) {
|
|
|
25971
26104
|
) > 0;
|
|
25972
26105
|
});
|
|
25973
26106
|
return {
|
|
25974
|
-
aiGateAddsValue: Number(outcome?.expectancyDelta ?? Number.NEGATIVE_INFINITY) > 0 && Number(approvedRisk?.totalProfit ?? Number.NEGATIVE_INFINITY) > 0 && Number(approvedRisk?.profitFactor ?? Number.NEGATIVE_INFINITY) > 1 && directionalEvidenceComplete && terminalsPass
|
|
26107
|
+
aiGateAddsValue: Number(outcome?.expectancyDelta ?? Number.NEGATIVE_INFINITY) > 0 && Number(approvedRisk?.totalProfit ?? Number.NEGATIVE_INFINITY) > 0 && Number(approvedRisk?.profitFactor ?? Number.NEGATIVE_INFINITY) > 1 && directionalEvidenceComplete && directionPolicyValid && terminalsPass
|
|
25975
26108
|
};
|
|
25976
26109
|
}
|
|
25977
26110
|
if (reference.kind === "runtime_parity") {
|
|
@@ -26006,7 +26139,7 @@ async function collectReleaseEvidenceReferences(references) {
|
|
|
26006
26139
|
})
|
|
26007
26140
|
);
|
|
26008
26141
|
}
|
|
26009
|
-
var import_node_crypto12, import_promises25, import_node_path20, import_types9, import_strategyReleaseEvidence3, SHA256_RE2, LINEAGE_FINGERPRINT_RE, DAY_MS12, REQUIRED_LONG_WINDOWS, REQUIRED_STABLE_TERMINAL_WINDOWS, MIN_RECENT_DIRECTION_REPAIR_TRADES, safeSegment3, compactTimestamp2, verifyFullPeriodChartArtifact, hasExactRuntimeTarget, deriveStrategyReleaseResearchDecision, canonicalStrategyReleaseJson, strategyReleaseSha256, assertFingerprint, assertLineageFingerprint, releaseReasons, deriveReleaseGates, verifyEvidenceComposition, createStrategyReleaseManifest, createStrategyReleaseEnvelope, verifyStrategyReleaseEnvelope, verifyStrategyEvidenceMarkerEnvelope, releaseMarkers, writeJsonAtomic2, publishStrategyRelease, buildStrategyLiveDiagnosis, buildStrategyLiveDiagnosisFromScorecard, realizedDrawdown, lowerBoundExitTimestamp, percentile2, buildStrategyMonitoringProfile, publishStrategyLiveDiagnosis, planStrategyEvidenceRetention, finiteString3, finiteNumber6, uniqueStrings, incompleteEvidenceLineage, extractEvidenceLineage, verifyCompletedCoreResearchBundle;
|
|
26142
|
+
var import_node_crypto12, import_promises25, import_node_path20, import_types9, import_strategyReleaseEvidence3, SHA256_RE2, LINEAGE_FINGERPRINT_RE, DAY_MS12, REQUIRED_LONG_WINDOWS, REQUIRED_STABLE_TERMINAL_WINDOWS, MIN_RECENT_DIRECTION_REPAIR_TRADES, safeSegment3, compactTimestamp2, verifyFullPeriodChartArtifact, hasExactRuntimeTarget, deriveStrategyReleaseResearchDecision, canonicalStrategyReleaseJson, strategyReleaseSha256, assertFingerprint, assertLineageFingerprint, releaseReasons, deriveReleaseGates, verifyEvidenceComposition, createStrategyReleaseManifest, createStrategyReleaseEnvelope, verifyStrategyReleaseEnvelope, verifyStrategyEvidenceMarkerEnvelope, releaseMarkers, writeJsonAtomic2, publishStrategyRelease, buildStrategyLiveDiagnosis, buildStrategyLiveDiagnosisFromScorecard, realizedDrawdown, lowerBoundExitTimestamp, percentile2, buildStrategyMonitoringProfile, publishStrategyLiveDiagnosis, planStrategyEvidenceRetention, finiteString3, finiteNumber6, finiteDirectionPolicy, uniqueStrings, incompleteEvidenceLineage, extractEvidenceLineage, verifyCompletedCoreResearchBundle;
|
|
26010
26143
|
var init_strategyRelease = __esm({
|
|
26011
26144
|
"src/lib/strategyRelease.ts"() {
|
|
26012
26145
|
"use strict";
|
|
@@ -26044,6 +26177,12 @@ var init_strategyRelease = __esm({
|
|
|
26044
26177
|
);
|
|
26045
26178
|
deriveStrategyReleaseResearchDecision = async (input) => {
|
|
26046
26179
|
const blockers = [];
|
|
26180
|
+
const directionPolicy = input.directionPolicy ?? "both";
|
|
26181
|
+
const activeDirections = directionPolicy === "long_only" ? ["long"] : directionPolicy === "short_only" ? ["short"] : ["long", "short"];
|
|
26182
|
+
const suppressedDirection = directionPolicy === "long_only" ? "short" : directionPolicy === "short_only" ? "long" : null;
|
|
26183
|
+
const hasHistoricalEdge = (entry) => entry.pnl > 0 && entry.profitFactor >= 1 && activeDirections.every(
|
|
26184
|
+
(direction) => entry[direction].pnl > 0 && entry[direction].profitFactor >= 1
|
|
26185
|
+
) && (suppressedDirection == null || entry[suppressedDirection].trades === 0 && entry[suppressedDirection].pnl === 0);
|
|
26047
26186
|
const byDays = new Map(
|
|
26048
26187
|
input.historicalWindows.map((entry) => [entry.days, entry])
|
|
26049
26188
|
);
|
|
@@ -26056,17 +26195,13 @@ var init_strategyRelease = __esm({
|
|
|
26056
26195
|
const longWindows = REQUIRED_LONG_WINDOWS.map(
|
|
26057
26196
|
(days) => byDays.get(days)
|
|
26058
26197
|
).filter((entry) => Boolean(entry));
|
|
26059
|
-
if (longWindows.some(
|
|
26060
|
-
(entry) => entry.pnl <= 0 || entry.profitFactor < 1 || entry.long.pnl <= 0 || entry.long.profitFactor < 1 || entry.short.pnl <= 0 || entry.short.profitFactor < 1
|
|
26061
|
-
)) {
|
|
26198
|
+
if (longWindows.some((entry) => !hasHistoricalEdge(entry))) {
|
|
26062
26199
|
blockers.push("HISTORICAL_EDGE_FAILED");
|
|
26063
26200
|
}
|
|
26064
26201
|
const stableTerminalWindows = REQUIRED_STABLE_TERMINAL_WINDOWS.map(
|
|
26065
26202
|
(days) => byDays.get(days)
|
|
26066
26203
|
).filter((entry) => Boolean(entry));
|
|
26067
|
-
if (stableTerminalWindows.some(
|
|
26068
|
-
(entry) => entry.pnl <= 0 || entry.profitFactor < 1 || entry.long.pnl <= 0 || entry.long.profitFactor < 1 || entry.short.pnl <= 0 || entry.short.profitFactor < 1
|
|
26069
|
-
)) {
|
|
26204
|
+
if (stableTerminalWindows.some((entry) => !hasHistoricalEdge(entry))) {
|
|
26070
26205
|
blockers.push("HISTORICAL_EDGE_FAILED");
|
|
26071
26206
|
}
|
|
26072
26207
|
if (blockers.length) {
|
|
@@ -26078,7 +26213,7 @@ var init_strategyRelease = __esm({
|
|
|
26078
26213
|
maxLossValue: null,
|
|
26079
26214
|
requiresRuntimeBinding: false,
|
|
26080
26215
|
blockers,
|
|
26081
|
-
summary:
|
|
26216
|
+
summary: `The final ${directionPolicy} composition does not have complete positive evidence for ALL and every active direction on the frozen 3y/4y/max-window matrix.`
|
|
26082
26217
|
};
|
|
26083
26218
|
}
|
|
26084
26219
|
const repairAllowed = Boolean(
|
|
@@ -26202,6 +26337,7 @@ var init_strategyRelease = __esm({
|
|
|
26202
26337
|
};
|
|
26203
26338
|
};
|
|
26204
26339
|
verifyEvidenceComposition = (strategy, composition, evidence) => {
|
|
26340
|
+
const directionPolicy = composition.directionPolicy ?? "both";
|
|
26205
26341
|
const requireEqual = (actual, expected, label, artifactId) => assert2(
|
|
26206
26342
|
actual === expected,
|
|
26207
26343
|
`evidence ${artifactId} ${label} does not match the frozen composition`
|
|
@@ -26272,6 +26408,14 @@ var init_strategyRelease = __esm({
|
|
|
26272
26408
|
"gateContextFingerprint",
|
|
26273
26409
|
reference.artifactId
|
|
26274
26410
|
);
|
|
26411
|
+
if (directionPolicy !== "both" || lineage?.directionPolicy != null) {
|
|
26412
|
+
requireEqual(
|
|
26413
|
+
lineage?.directionPolicy,
|
|
26414
|
+
directionPolicy,
|
|
26415
|
+
"directionPolicy",
|
|
26416
|
+
reference.artifactId
|
|
26417
|
+
);
|
|
26418
|
+
}
|
|
26275
26419
|
assert2(
|
|
26276
26420
|
lineage?.sourceSha256s.includes(composition.coreExportSha256),
|
|
26277
26421
|
`evidence ${reference.artifactId} does not use the frozen core export`
|
|
@@ -26316,6 +26460,14 @@ var init_strategyRelease = __esm({
|
|
|
26316
26460
|
"MAX_LOSS_VALUE",
|
|
26317
26461
|
reference.artifactId
|
|
26318
26462
|
);
|
|
26463
|
+
if (directionPolicy !== "both" || lineage?.directionPolicy != null) {
|
|
26464
|
+
requireEqual(
|
|
26465
|
+
lineage?.directionPolicy,
|
|
26466
|
+
directionPolicy,
|
|
26467
|
+
"directionPolicy",
|
|
26468
|
+
reference.artifactId
|
|
26469
|
+
);
|
|
26470
|
+
}
|
|
26319
26471
|
continue;
|
|
26320
26472
|
}
|
|
26321
26473
|
if (reference.kind === "execution_calibration") {
|
|
@@ -26356,6 +26508,14 @@ var init_strategyRelease = __esm({
|
|
|
26356
26508
|
"MAX_LOSS_VALUE",
|
|
26357
26509
|
reference.artifactId
|
|
26358
26510
|
);
|
|
26511
|
+
if (directionPolicy !== "both" || lineage?.directionPolicy != null) {
|
|
26512
|
+
requireEqual(
|
|
26513
|
+
lineage?.directionPolicy,
|
|
26514
|
+
directionPolicy,
|
|
26515
|
+
"directionPolicy",
|
|
26516
|
+
reference.artifactId
|
|
26517
|
+
);
|
|
26518
|
+
}
|
|
26359
26519
|
}
|
|
26360
26520
|
}
|
|
26361
26521
|
};
|
|
@@ -26391,6 +26551,12 @@ var init_strategyRelease = __esm({
|
|
|
26391
26551
|
input.composition.longEnabled && input.composition.shortEnabled,
|
|
26392
26552
|
"LONG and SHORT must both remain enabled"
|
|
26393
26553
|
);
|
|
26554
|
+
assert2(
|
|
26555
|
+
input.composition.directionPolicy == null || ["both", "long_only", "short_only", "direction_aware"].includes(
|
|
26556
|
+
input.composition.directionPolicy
|
|
26557
|
+
),
|
|
26558
|
+
"composition.directionPolicy is invalid"
|
|
26559
|
+
);
|
|
26394
26560
|
assert2(
|
|
26395
26561
|
Number.isInteger(input.monitoring.minimumProspectiveClosedTrades) && input.monitoring.minimumProspectiveClosedTrades > 0,
|
|
26396
26562
|
"monitoring.minimumProspectiveClosedTrades must be positive"
|
|
@@ -26455,7 +26621,8 @@ var init_strategyRelease = __esm({
|
|
|
26455
26621
|
gateFingerprint: input.composition.gateFingerprint,
|
|
26456
26622
|
gateContextFingerprint: input.composition.gateContextFingerprint,
|
|
26457
26623
|
runtimeContextFingerprint: input.composition.runtimeContextFingerprint,
|
|
26458
|
-
maxLossValue: input.composition.maxLossValue
|
|
26624
|
+
maxLossValue: input.composition.maxLossValue,
|
|
26625
|
+
...input.composition.directionPolicy == null ? {} : { directionPolicy: input.composition.directionPolicy }
|
|
26459
26626
|
};
|
|
26460
26627
|
const compositionId = `${safeSegment3(input.strategy, "strategy")}_${strategyReleaseSha256(compositionIdentity).slice(0, 16)}`;
|
|
26461
26628
|
const reasons = releaseReasons({ ...input, gates: evidenceGates });
|
|
@@ -26545,7 +26712,8 @@ var init_strategyRelease = __esm({
|
|
|
26545
26712
|
runtimeContextFingerprint: manifest.composition.runtimeContextFingerprint,
|
|
26546
26713
|
maxLossValue: manifest.composition.maxLossValue,
|
|
26547
26714
|
longEnabled: manifest.composition.longEnabled,
|
|
26548
|
-
shortEnabled: manifest.composition.shortEnabled
|
|
26715
|
+
shortEnabled: manifest.composition.shortEnabled,
|
|
26716
|
+
directionPolicy: manifest.composition.directionPolicy
|
|
26549
26717
|
},
|
|
26550
26718
|
marketWindow: manifest.marketWindow,
|
|
26551
26719
|
researchBudget: manifest.researchBudget,
|
|
@@ -26969,6 +27137,7 @@ var init_strategyRelease = __esm({
|
|
|
26969
27137
|
const number = Number(value);
|
|
26970
27138
|
return Number.isFinite(number) ? number : null;
|
|
26971
27139
|
};
|
|
27140
|
+
finiteDirectionPolicy = (value) => ["both", "long_only", "short_only", "direction_aware"].includes(String(value)) ? value : null;
|
|
26972
27141
|
uniqueStrings = (values) => [
|
|
26973
27142
|
...new Set(
|
|
26974
27143
|
values.map(finiteString3).filter((value) => value != null)
|
|
@@ -26985,6 +27154,7 @@ var init_strategyRelease = __esm({
|
|
|
26985
27154
|
gateContextFingerprint: null,
|
|
26986
27155
|
runtimeContextFingerprint: null,
|
|
26987
27156
|
maxLossValue: null,
|
|
27157
|
+
directionPolicy: null,
|
|
26988
27158
|
sourceSha256s: []
|
|
26989
27159
|
});
|
|
26990
27160
|
extractEvidenceLineage = ({
|
|
@@ -27047,6 +27217,7 @@ var init_strategyRelease = __esm({
|
|
|
27047
27217
|
gateConfigIdsFingerprint: finiteString3(lineage?.configIdsFingerprint),
|
|
27048
27218
|
gateFingerprint: finiteString3(lineage?.gateFingerprint),
|
|
27049
27219
|
gateContextFingerprint: finiteString3(lineage?.contextFingerprint),
|
|
27220
|
+
directionPolicy: finiteDirectionPolicy(lineage?.directionPolicy),
|
|
27050
27221
|
sourceSha256s: uniqueStrings(array(lineage?.sourceSha256s))
|
|
27051
27222
|
};
|
|
27052
27223
|
}
|
|
@@ -27082,7 +27253,8 @@ var init_strategyRelease = __esm({
|
|
|
27082
27253
|
gateFingerprint: one("gateFingerprint"),
|
|
27083
27254
|
runtimeConfigFingerprint: one("configFingerprint"),
|
|
27084
27255
|
runtimeContextFingerprint: one("contextFingerprint"),
|
|
27085
|
-
maxLossValue: maxLossValues.length === 1 ? maxLossValues[0] : null
|
|
27256
|
+
maxLossValue: maxLossValues.length === 1 ? maxLossValues[0] : null,
|
|
27257
|
+
directionPolicy: finiteDirectionPolicy(one("directionPolicy"))
|
|
27086
27258
|
};
|
|
27087
27259
|
}
|
|
27088
27260
|
if (reference.kind === "execution_calibration") {
|
|
@@ -27119,6 +27291,7 @@ var init_strategyRelease = __esm({
|
|
|
27119
27291
|
runtimeConfigFingerprint: one("configFingerprint"),
|
|
27120
27292
|
runtimeContextFingerprint: one("contextFingerprint"),
|
|
27121
27293
|
maxLossValue: maxLossValues.length === 1 ? maxLossValues[0] : null,
|
|
27294
|
+
directionPolicy: finiteDirectionPolicy(one("directionPolicy")),
|
|
27122
27295
|
sourceSha256s: uniqueStrings([
|
|
27123
27296
|
...array(nested(artifact.sources)?.sha256s),
|
|
27124
27297
|
...array(artifact.sourceSha256s)
|