@tradejs/cli 2.0.10 → 2.0.12
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 +80 -33
- package/dist/lib/aiPocketSearch.js +3 -0
- package/dist/lib/aiPocketSearchCli.js +45 -3
- package/dist/scripts/aiPocketSearch.js +78 -31
- package/package.json +9 -9
package/dist/cli.js
CHANGED
|
@@ -4728,10 +4728,13 @@ var init_aiPocketSearch = __esm({
|
|
|
4728
4728
|
["selected_rows", run.selectedRows],
|
|
4729
4729
|
["evaluated_rows", run.evaluatedRows],
|
|
4730
4730
|
["scope", run.scope],
|
|
4731
|
+
["direction", run.direction ?? "all"],
|
|
4731
4732
|
["scope_rows", run.scopeRows],
|
|
4732
4733
|
["train_rows", run.trainRows],
|
|
4733
4734
|
["validation_rows", run.validationRows],
|
|
4735
|
+
["test_rows", run.testRows],
|
|
4734
4736
|
["validation_split", formatMdPercent(run.validationSplit)],
|
|
4737
|
+
["test_split", formatMdPercent(run.testSplit)],
|
|
4735
4738
|
["min_validation_support", run.minValidationSupport],
|
|
4736
4739
|
["failed", run.failed],
|
|
4737
4740
|
["recent", run.recent === 0 ? "all" : run.recent],
|
|
@@ -4869,7 +4872,7 @@ var init_aiPocketSearch = __esm({
|
|
|
4869
4872
|
});
|
|
4870
4873
|
|
|
4871
4874
|
// src/lib/aiPocketSearchCli.ts
|
|
4872
|
-
var AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS, readAiPocketSearchCliOption;
|
|
4875
|
+
var AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS, splitAiPocketResearchRowsByTimestamp, readAiPocketSearchCliOption;
|
|
4873
4876
|
var init_aiPocketSearchCli = __esm({
|
|
4874
4877
|
"src/lib/aiPocketSearchCli.ts"() {
|
|
4875
4878
|
"use strict";
|
|
@@ -4877,7 +4880,47 @@ var init_aiPocketSearchCli = __esm({
|
|
|
4877
4880
|
minProfitFactor: "1.2",
|
|
4878
4881
|
minWinRate: "0",
|
|
4879
4882
|
minTotalProfit: "0",
|
|
4880
|
-
validationSplit: "0.25"
|
|
4883
|
+
validationSplit: "0.25",
|
|
4884
|
+
testSplit: "0"
|
|
4885
|
+
};
|
|
4886
|
+
splitAiPocketResearchRowsByTimestamp = (rows, validationSplit, testSplit = 0) => {
|
|
4887
|
+
const timestamps = [
|
|
4888
|
+
...new Set(
|
|
4889
|
+
rows.map((row) => row.timestamp).filter((timestamp) => Number.isFinite(timestamp))
|
|
4890
|
+
)
|
|
4891
|
+
].sort((left, right) => left - right);
|
|
4892
|
+
if (timestamps.length < 2) {
|
|
4893
|
+
return { trainRows: rows, validationRows: [], testRows: [] };
|
|
4894
|
+
}
|
|
4895
|
+
const getSplitCount = (ratio) => ratio > 0 ? Math.max(1, Math.floor(timestamps.length * ratio)) : 0;
|
|
4896
|
+
let testEvents = getSplitCount(testSplit);
|
|
4897
|
+
let validationEvents = getSplitCount(validationSplit);
|
|
4898
|
+
const maximumHeldOut = timestamps.length - 1;
|
|
4899
|
+
if (testEvents + validationEvents > maximumHeldOut) {
|
|
4900
|
+
validationEvents = Math.max(
|
|
4901
|
+
0,
|
|
4902
|
+
validationEvents - (testEvents + validationEvents - maximumHeldOut)
|
|
4903
|
+
);
|
|
4904
|
+
}
|
|
4905
|
+
if (testEvents + validationEvents > maximumHeldOut) {
|
|
4906
|
+
testEvents = Math.max(0, maximumHeldOut - validationEvents);
|
|
4907
|
+
}
|
|
4908
|
+
const testStart = timestamps.length - testEvents;
|
|
4909
|
+
const validationStart = testStart - validationEvents;
|
|
4910
|
+
const validationTimestamps = new Set(
|
|
4911
|
+
timestamps.slice(validationStart, testStart)
|
|
4912
|
+
);
|
|
4913
|
+
const testTimestamps = new Set(timestamps.slice(testStart));
|
|
4914
|
+
const isIn = (timestampsSet, timestamp) => typeof timestamp === "number" && timestampsSet.has(timestamp);
|
|
4915
|
+
return {
|
|
4916
|
+
trainRows: rows.filter(
|
|
4917
|
+
(row) => !isIn(validationTimestamps, row.timestamp) && !isIn(testTimestamps, row.timestamp)
|
|
4918
|
+
),
|
|
4919
|
+
validationRows: rows.filter(
|
|
4920
|
+
(row) => isIn(validationTimestamps, row.timestamp)
|
|
4921
|
+
),
|
|
4922
|
+
testRows: rows.filter((row) => isIn(testTimestamps, row.timestamp))
|
|
4923
|
+
};
|
|
4881
4924
|
};
|
|
4882
4925
|
readAiPocketSearchCliOption = ({
|
|
4883
4926
|
argv,
|
|
@@ -4904,7 +4947,7 @@ var aiPocketSearch_exports = {};
|
|
|
4904
4947
|
__export(aiPocketSearch_exports, {
|
|
4905
4948
|
main: () => main3
|
|
4906
4949
|
});
|
|
4907
|
-
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, normalizeFeatureProfile, normalizeQuality, isAiApproval, listMergedFiles, deriveStrategyNameFromFile, getMergedGroupId, resolveMarkdownReportPath, sortDatasetPartPaths, resolveMergedDatasetFiles, getDatasetRowTimestamp, findMaxSelectedTimestamp, getDeterministicModelCandidate, resolveScopeRows,
|
|
4950
|
+
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, normalizeFeatureProfile, normalizeQuality, isAiApproval, listMergedFiles, deriveStrategyNameFromFile, getMergedGroupId, resolveMarkdownReportPath, sortDatasetPartPaths, resolveMergedDatasetFiles, getDatasetRowTimestamp, findMaxSelectedTimestamp, getDeterministicModelCandidate, resolveScopeRows, printSection, buildSummaryRows, buildPocketRows, buildQualityRows, tickProgressBarTo, searchPhaseLabels, hasCliOption2, main3;
|
|
4908
4951
|
var init_aiPocketSearch2 = __esm({
|
|
4909
4952
|
"src/scripts/aiPocketSearch.ts"() {
|
|
4910
4953
|
"use strict";
|
|
@@ -4968,6 +5011,11 @@ var init_aiPocketSearch2 = __esm({
|
|
|
4968
5011
|
"Search scope: all, approved, rejected, or candidates",
|
|
4969
5012
|
"all"
|
|
4970
5013
|
);
|
|
5014
|
+
import_args3.default.option(
|
|
5015
|
+
["I", "direction"],
|
|
5016
|
+
"Only search rows with this signal direction: LONG or SHORT",
|
|
5017
|
+
""
|
|
5018
|
+
);
|
|
4971
5019
|
import_args3.default.option(["d", "maxDepth"], "Maximum predicate-combination depth", 2);
|
|
4972
5020
|
import_args3.default.option(["m", "minSupport"], "Minimum rows required for a pocket", 20);
|
|
4973
5021
|
import_args3.default.option(
|
|
@@ -5000,6 +5048,11 @@ var init_aiPocketSearch2 = __esm({
|
|
|
5000
5048
|
"Trailing time-ordered scope share reserved for validation (0 disables)",
|
|
5001
5049
|
AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS.validationSplit
|
|
5002
5050
|
);
|
|
5051
|
+
import_args3.default.option(
|
|
5052
|
+
["T", "testSplit"],
|
|
5053
|
+
"Trailing timestamp-grouped scope share withheld as untouched test",
|
|
5054
|
+
AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS.testSplit
|
|
5055
|
+
);
|
|
5003
5056
|
import_args3.default.option(
|
|
5004
5057
|
["N", "minValidationSupport"],
|
|
5005
5058
|
"Minimum validation rows required for positive pockets (0 = auto)",
|
|
@@ -5288,31 +5341,6 @@ var init_aiPocketSearch2 = __esm({
|
|
|
5288
5341
|
}
|
|
5289
5342
|
return rows;
|
|
5290
5343
|
};
|
|
5291
|
-
splitValidationRows = (rows, validationSplit) => {
|
|
5292
|
-
if (validationSplit <= 0 || rows.length < 2) {
|
|
5293
|
-
return {
|
|
5294
|
-
trainRows: rows,
|
|
5295
|
-
validationRows: []
|
|
5296
|
-
};
|
|
5297
|
-
}
|
|
5298
|
-
const sortedRows = [...rows].sort((left, right) => {
|
|
5299
|
-
const leftTimestamp = typeof left.timestamp === "number" && Number.isFinite(left.timestamp) ? left.timestamp : Number.POSITIVE_INFINITY;
|
|
5300
|
-
const rightTimestamp = typeof right.timestamp === "number" && Number.isFinite(right.timestamp) ? right.timestamp : Number.POSITIVE_INFINITY;
|
|
5301
|
-
return leftTimestamp - rightTimestamp;
|
|
5302
|
-
});
|
|
5303
|
-
const validationCount = Math.max(
|
|
5304
|
-
1,
|
|
5305
|
-
Math.min(
|
|
5306
|
-
sortedRows.length - 1,
|
|
5307
|
-
Math.floor(sortedRows.length * validationSplit)
|
|
5308
|
-
)
|
|
5309
|
-
);
|
|
5310
|
-
const splitIndex = sortedRows.length - validationCount;
|
|
5311
|
-
return {
|
|
5312
|
-
trainRows: sortedRows.slice(0, splitIndex),
|
|
5313
|
-
validationRows: sortedRows.slice(splitIndex)
|
|
5314
|
-
};
|
|
5315
|
-
};
|
|
5316
5344
|
printSection = (title, table) => {
|
|
5317
5345
|
console.log(import_chalk4.default.gray(`${title}:`));
|
|
5318
5346
|
console.log(table);
|
|
@@ -5412,6 +5440,12 @@ var init_aiPocketSearch2 = __esm({
|
|
|
5412
5440
|
`Unsupported --scope "${scope}". Use all, approved, rejected, or candidates.`
|
|
5413
5441
|
);
|
|
5414
5442
|
}
|
|
5443
|
+
const direction = String(flags3.direction || "").trim().toUpperCase();
|
|
5444
|
+
if (direction && !["LONG", "SHORT"].includes(direction)) {
|
|
5445
|
+
throw new Error(
|
|
5446
|
+
`Unsupported --direction "${direction}". Use LONG or SHORT.`
|
|
5447
|
+
);
|
|
5448
|
+
}
|
|
5415
5449
|
const maxDepth = normalizePositiveInt(flags3.maxDepth, 2);
|
|
5416
5450
|
const minSupport = normalizePositiveInt(flags3.minSupport, 20);
|
|
5417
5451
|
const minProfitFactor = normalizeNonNegativeNumber(
|
|
@@ -5449,6 +5483,14 @@ var init_aiPocketSearch2 = __esm({
|
|
|
5449
5483
|
}) ?? flags3.validationSplit,
|
|
5450
5484
|
0.25
|
|
5451
5485
|
) : 0.25;
|
|
5486
|
+
const testSplit = hasCliOption2("testSplit", "T") ? normalizeRatio(
|
|
5487
|
+
readAiPocketSearchCliOption({
|
|
5488
|
+
argv: process.argv,
|
|
5489
|
+
longName: "testSplit",
|
|
5490
|
+
shortName: "T"
|
|
5491
|
+
}) ?? flags3.testSplit,
|
|
5492
|
+
0
|
|
5493
|
+
) : 0;
|
|
5452
5494
|
const explicitMinValidationSupport = normalizeInt(
|
|
5453
5495
|
flags3.minValidationSupport,
|
|
5454
5496
|
0
|
|
@@ -5588,11 +5630,9 @@ var init_aiPocketSearch2 = __esm({
|
|
|
5588
5630
|
}
|
|
5589
5631
|
}
|
|
5590
5632
|
});
|
|
5591
|
-
const
|
|
5592
|
-
const
|
|
5593
|
-
|
|
5594
|
-
validationSplit
|
|
5595
|
-
);
|
|
5633
|
+
const scopedRows = resolveScopeRows(rows, scope);
|
|
5634
|
+
const scopeRows = direction ? scopedRows.filter((row) => row.direction === direction) : scopedRows;
|
|
5635
|
+
const { trainRows, validationRows, testRows } = splitAiPocketResearchRowsByTimestamp(scopeRows, validationSplit, testSplit);
|
|
5596
5636
|
const minValidationSupport = explicitMinValidationSupport > 0 ? explicitMinValidationSupport : validationRows.length ? Math.max(3, Math.ceil(minSupport * validationSplit * 0.5)) : 0;
|
|
5597
5637
|
const currentGateSummary = summarizeAiTrainEvaluations(rows);
|
|
5598
5638
|
const currentGateQualityThresholds = summarizeAiTrainEvaluationsByQualityThreshold(rows, qualityThresholds);
|
|
@@ -5655,9 +5695,11 @@ var init_aiPocketSearch2 = __esm({
|
|
|
5655
5695
|
selectedRows,
|
|
5656
5696
|
evaluatedRows: rows.length,
|
|
5657
5697
|
scope,
|
|
5698
|
+
direction: direction || null,
|
|
5658
5699
|
scopeRows: scopeRows.length,
|
|
5659
5700
|
trainRows: trainRows.length,
|
|
5660
5701
|
validationRows: validationRows.length,
|
|
5702
|
+
testRows: testRows.length,
|
|
5661
5703
|
scanned,
|
|
5662
5704
|
dateSkipped,
|
|
5663
5705
|
failed,
|
|
@@ -5672,6 +5714,7 @@ var init_aiPocketSearch2 = __esm({
|
|
|
5672
5714
|
includeGateContext,
|
|
5673
5715
|
featureProfile,
|
|
5674
5716
|
validationSplit,
|
|
5717
|
+
testSplit,
|
|
5675
5718
|
minValidationSupport,
|
|
5676
5719
|
reportPath,
|
|
5677
5720
|
search: {
|
|
@@ -5683,6 +5726,7 @@ var init_aiPocketSearch2 = __esm({
|
|
|
5683
5726
|
maxAtomicPredicates,
|
|
5684
5727
|
maxCombinations,
|
|
5685
5728
|
validationSplit,
|
|
5729
|
+
testSplit,
|
|
5686
5730
|
minValidationSupport,
|
|
5687
5731
|
dedupeEquivalentSelections,
|
|
5688
5732
|
top
|
|
@@ -5724,9 +5768,11 @@ var init_aiPocketSearch2 = __esm({
|
|
|
5724
5768
|
["selected", import_chalk4.default.blue(String(selectedRows))],
|
|
5725
5769
|
["evaluated", import_chalk4.default.blue(String(rows.length))],
|
|
5726
5770
|
["scope", import_chalk4.default.yellow(scope)],
|
|
5771
|
+
["direction", direction ? import_chalk4.default.yellow(direction) : import_chalk4.default.gray("all")],
|
|
5727
5772
|
["scope_rows", import_chalk4.default.blue(String(scopeRows.length))],
|
|
5728
5773
|
["train_rows", import_chalk4.default.blue(String(trainRows.length))],
|
|
5729
5774
|
["validation_rows", import_chalk4.default.blue(String(validationRows.length))],
|
|
5775
|
+
["test_rows", import_chalk4.default.blue(String(testRows.length))],
|
|
5730
5776
|
["source_rows", import_chalk4.default.blue(String(totalRows))],
|
|
5731
5777
|
["date_skipped", import_chalk4.default.blue(String(dateSkipped))],
|
|
5732
5778
|
[
|
|
@@ -5753,6 +5799,7 @@ var init_aiPocketSearch2 = __esm({
|
|
|
5753
5799
|
["max_atomic_predicates", import_chalk4.default.magenta(String(maxAtomicPredicates))],
|
|
5754
5800
|
["max_combinations", import_chalk4.default.magenta(String(maxCombinations))],
|
|
5755
5801
|
["validation_split", import_chalk4.default.magenta(formatRatio(validationSplit))],
|
|
5802
|
+
["test_split", import_chalk4.default.magenta(formatRatio(testSplit))],
|
|
5756
5803
|
["min_validation_support", import_chalk4.default.magenta(String(minValidationSupport))],
|
|
5757
5804
|
[
|
|
5758
5805
|
"dedupe_equivalent",
|
|
@@ -1187,10 +1187,13 @@ var buildAiPocketMarkdownReport = ({
|
|
|
1187
1187
|
["selected_rows", run.selectedRows],
|
|
1188
1188
|
["evaluated_rows", run.evaluatedRows],
|
|
1189
1189
|
["scope", run.scope],
|
|
1190
|
+
["direction", run.direction ?? "all"],
|
|
1190
1191
|
["scope_rows", run.scopeRows],
|
|
1191
1192
|
["train_rows", run.trainRows],
|
|
1192
1193
|
["validation_rows", run.validationRows],
|
|
1194
|
+
["test_rows", run.testRows],
|
|
1193
1195
|
["validation_split", formatMdPercent(run.validationSplit)],
|
|
1196
|
+
["test_split", formatMdPercent(run.testSplit)],
|
|
1194
1197
|
["min_validation_support", run.minValidationSupport],
|
|
1195
1198
|
["failed", run.failed],
|
|
1196
1199
|
["recent", run.recent === 0 ? "all" : run.recent],
|
|
@@ -21,14 +21,55 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var aiPocketSearchCli_exports = {};
|
|
22
22
|
__export(aiPocketSearchCli_exports, {
|
|
23
23
|
AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS: () => AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS,
|
|
24
|
-
readAiPocketSearchCliOption: () => readAiPocketSearchCliOption
|
|
24
|
+
readAiPocketSearchCliOption: () => readAiPocketSearchCliOption,
|
|
25
|
+
splitAiPocketResearchRowsByTimestamp: () => splitAiPocketResearchRowsByTimestamp
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(aiPocketSearchCli_exports);
|
|
27
28
|
var AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS = {
|
|
28
29
|
minProfitFactor: "1.2",
|
|
29
30
|
minWinRate: "0",
|
|
30
31
|
minTotalProfit: "0",
|
|
31
|
-
validationSplit: "0.25"
|
|
32
|
+
validationSplit: "0.25",
|
|
33
|
+
testSplit: "0"
|
|
34
|
+
};
|
|
35
|
+
var splitAiPocketResearchRowsByTimestamp = (rows, validationSplit, testSplit = 0) => {
|
|
36
|
+
const timestamps = [
|
|
37
|
+
...new Set(
|
|
38
|
+
rows.map((row) => row.timestamp).filter((timestamp) => Number.isFinite(timestamp))
|
|
39
|
+
)
|
|
40
|
+
].sort((left, right) => left - right);
|
|
41
|
+
if (timestamps.length < 2) {
|
|
42
|
+
return { trainRows: rows, validationRows: [], testRows: [] };
|
|
43
|
+
}
|
|
44
|
+
const getSplitCount = (ratio) => ratio > 0 ? Math.max(1, Math.floor(timestamps.length * ratio)) : 0;
|
|
45
|
+
let testEvents = getSplitCount(testSplit);
|
|
46
|
+
let validationEvents = getSplitCount(validationSplit);
|
|
47
|
+
const maximumHeldOut = timestamps.length - 1;
|
|
48
|
+
if (testEvents + validationEvents > maximumHeldOut) {
|
|
49
|
+
validationEvents = Math.max(
|
|
50
|
+
0,
|
|
51
|
+
validationEvents - (testEvents + validationEvents - maximumHeldOut)
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
if (testEvents + validationEvents > maximumHeldOut) {
|
|
55
|
+
testEvents = Math.max(0, maximumHeldOut - validationEvents);
|
|
56
|
+
}
|
|
57
|
+
const testStart = timestamps.length - testEvents;
|
|
58
|
+
const validationStart = testStart - validationEvents;
|
|
59
|
+
const validationTimestamps = new Set(
|
|
60
|
+
timestamps.slice(validationStart, testStart)
|
|
61
|
+
);
|
|
62
|
+
const testTimestamps = new Set(timestamps.slice(testStart));
|
|
63
|
+
const isIn = (timestampsSet, timestamp) => typeof timestamp === "number" && timestampsSet.has(timestamp);
|
|
64
|
+
return {
|
|
65
|
+
trainRows: rows.filter(
|
|
66
|
+
(row) => !isIn(validationTimestamps, row.timestamp) && !isIn(testTimestamps, row.timestamp)
|
|
67
|
+
),
|
|
68
|
+
validationRows: rows.filter(
|
|
69
|
+
(row) => isIn(validationTimestamps, row.timestamp)
|
|
70
|
+
),
|
|
71
|
+
testRows: rows.filter((row) => isIn(testTimestamps, row.timestamp))
|
|
72
|
+
};
|
|
32
73
|
};
|
|
33
74
|
var readAiPocketSearchCliOption = ({
|
|
34
75
|
argv,
|
|
@@ -50,5 +91,6 @@ var readAiPocketSearchCliOption = ({
|
|
|
50
91
|
// Annotate the CommonJS export names for ESM import in node:
|
|
51
92
|
0 && (module.exports = {
|
|
52
93
|
AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS,
|
|
53
|
-
readAiPocketSearchCliOption
|
|
94
|
+
readAiPocketSearchCliOption,
|
|
95
|
+
splitAiPocketResearchRowsByTimestamp
|
|
54
96
|
});
|
|
@@ -3176,10 +3176,13 @@ var buildAiPocketMarkdownReport = ({
|
|
|
3176
3176
|
["selected_rows", run.selectedRows],
|
|
3177
3177
|
["evaluated_rows", run.evaluatedRows],
|
|
3178
3178
|
["scope", run.scope],
|
|
3179
|
+
["direction", run.direction ?? "all"],
|
|
3179
3180
|
["scope_rows", run.scopeRows],
|
|
3180
3181
|
["train_rows", run.trainRows],
|
|
3181
3182
|
["validation_rows", run.validationRows],
|
|
3183
|
+
["test_rows", run.testRows],
|
|
3182
3184
|
["validation_split", formatMdPercent(run.validationSplit)],
|
|
3185
|
+
["test_split", formatMdPercent(run.testSplit)],
|
|
3183
3186
|
["min_validation_support", run.minValidationSupport],
|
|
3184
3187
|
["failed", run.failed],
|
|
3185
3188
|
["recent", run.recent === 0 ? "all" : run.recent],
|
|
@@ -3319,7 +3322,47 @@ var AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS = {
|
|
|
3319
3322
|
minProfitFactor: "1.2",
|
|
3320
3323
|
minWinRate: "0",
|
|
3321
3324
|
minTotalProfit: "0",
|
|
3322
|
-
validationSplit: "0.25"
|
|
3325
|
+
validationSplit: "0.25",
|
|
3326
|
+
testSplit: "0"
|
|
3327
|
+
};
|
|
3328
|
+
var splitAiPocketResearchRowsByTimestamp = (rows, validationSplit, testSplit = 0) => {
|
|
3329
|
+
const timestamps = [
|
|
3330
|
+
...new Set(
|
|
3331
|
+
rows.map((row) => row.timestamp).filter((timestamp) => Number.isFinite(timestamp))
|
|
3332
|
+
)
|
|
3333
|
+
].sort((left, right) => left - right);
|
|
3334
|
+
if (timestamps.length < 2) {
|
|
3335
|
+
return { trainRows: rows, validationRows: [], testRows: [] };
|
|
3336
|
+
}
|
|
3337
|
+
const getSplitCount = (ratio) => ratio > 0 ? Math.max(1, Math.floor(timestamps.length * ratio)) : 0;
|
|
3338
|
+
let testEvents = getSplitCount(testSplit);
|
|
3339
|
+
let validationEvents = getSplitCount(validationSplit);
|
|
3340
|
+
const maximumHeldOut = timestamps.length - 1;
|
|
3341
|
+
if (testEvents + validationEvents > maximumHeldOut) {
|
|
3342
|
+
validationEvents = Math.max(
|
|
3343
|
+
0,
|
|
3344
|
+
validationEvents - (testEvents + validationEvents - maximumHeldOut)
|
|
3345
|
+
);
|
|
3346
|
+
}
|
|
3347
|
+
if (testEvents + validationEvents > maximumHeldOut) {
|
|
3348
|
+
testEvents = Math.max(0, maximumHeldOut - validationEvents);
|
|
3349
|
+
}
|
|
3350
|
+
const testStart = timestamps.length - testEvents;
|
|
3351
|
+
const validationStart = testStart - validationEvents;
|
|
3352
|
+
const validationTimestamps = new Set(
|
|
3353
|
+
timestamps.slice(validationStart, testStart)
|
|
3354
|
+
);
|
|
3355
|
+
const testTimestamps = new Set(timestamps.slice(testStart));
|
|
3356
|
+
const isIn = (timestampsSet, timestamp) => typeof timestamp === "number" && timestampsSet.has(timestamp);
|
|
3357
|
+
return {
|
|
3358
|
+
trainRows: rows.filter(
|
|
3359
|
+
(row) => !isIn(validationTimestamps, row.timestamp) && !isIn(testTimestamps, row.timestamp)
|
|
3360
|
+
),
|
|
3361
|
+
validationRows: rows.filter(
|
|
3362
|
+
(row) => isIn(validationTimestamps, row.timestamp)
|
|
3363
|
+
),
|
|
3364
|
+
testRows: rows.filter((row) => isIn(testTimestamps, row.timestamp))
|
|
3365
|
+
};
|
|
3323
3366
|
};
|
|
3324
3367
|
var readAiPocketSearchCliOption = ({
|
|
3325
3368
|
argv,
|
|
@@ -3388,6 +3431,11 @@ import_args.default.option(
|
|
|
3388
3431
|
"Search scope: all, approved, rejected, or candidates",
|
|
3389
3432
|
"all"
|
|
3390
3433
|
);
|
|
3434
|
+
import_args.default.option(
|
|
3435
|
+
["I", "direction"],
|
|
3436
|
+
"Only search rows with this signal direction: LONG or SHORT",
|
|
3437
|
+
""
|
|
3438
|
+
);
|
|
3391
3439
|
import_args.default.option(["d", "maxDepth"], "Maximum predicate-combination depth", 2);
|
|
3392
3440
|
import_args.default.option(["m", "minSupport"], "Minimum rows required for a pocket", 20);
|
|
3393
3441
|
import_args.default.option(
|
|
@@ -3420,6 +3468,11 @@ import_args.default.option(
|
|
|
3420
3468
|
"Trailing time-ordered scope share reserved for validation (0 disables)",
|
|
3421
3469
|
AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS.validationSplit
|
|
3422
3470
|
);
|
|
3471
|
+
import_args.default.option(
|
|
3472
|
+
["T", "testSplit"],
|
|
3473
|
+
"Trailing timestamp-grouped scope share withheld as untouched test",
|
|
3474
|
+
AI_POCKET_SEARCH_CLI_DECIMAL_DEFAULTS.testSplit
|
|
3475
|
+
);
|
|
3423
3476
|
import_args.default.option(
|
|
3424
3477
|
["N", "minValidationSupport"],
|
|
3425
3478
|
"Minimum validation rows required for positive pockets (0 = auto)",
|
|
@@ -3708,31 +3761,6 @@ var resolveScopeRows = (rows, scope) => {
|
|
|
3708
3761
|
}
|
|
3709
3762
|
return rows;
|
|
3710
3763
|
};
|
|
3711
|
-
var splitValidationRows = (rows, validationSplit) => {
|
|
3712
|
-
if (validationSplit <= 0 || rows.length < 2) {
|
|
3713
|
-
return {
|
|
3714
|
-
trainRows: rows,
|
|
3715
|
-
validationRows: []
|
|
3716
|
-
};
|
|
3717
|
-
}
|
|
3718
|
-
const sortedRows = [...rows].sort((left, right) => {
|
|
3719
|
-
const leftTimestamp = typeof left.timestamp === "number" && Number.isFinite(left.timestamp) ? left.timestamp : Number.POSITIVE_INFINITY;
|
|
3720
|
-
const rightTimestamp = typeof right.timestamp === "number" && Number.isFinite(right.timestamp) ? right.timestamp : Number.POSITIVE_INFINITY;
|
|
3721
|
-
return leftTimestamp - rightTimestamp;
|
|
3722
|
-
});
|
|
3723
|
-
const validationCount = Math.max(
|
|
3724
|
-
1,
|
|
3725
|
-
Math.min(
|
|
3726
|
-
sortedRows.length - 1,
|
|
3727
|
-
Math.floor(sortedRows.length * validationSplit)
|
|
3728
|
-
)
|
|
3729
|
-
);
|
|
3730
|
-
const splitIndex = sortedRows.length - validationCount;
|
|
3731
|
-
return {
|
|
3732
|
-
trainRows: sortedRows.slice(0, splitIndex),
|
|
3733
|
-
validationRows: sortedRows.slice(splitIndex)
|
|
3734
|
-
};
|
|
3735
|
-
};
|
|
3736
3764
|
var printSection = (title, table) => {
|
|
3737
3765
|
console.log(import_chalk.default.gray(`${title}:`));
|
|
3738
3766
|
console.log(table);
|
|
@@ -3832,6 +3860,12 @@ var main = async () => {
|
|
|
3832
3860
|
`Unsupported --scope "${scope}". Use all, approved, rejected, or candidates.`
|
|
3833
3861
|
);
|
|
3834
3862
|
}
|
|
3863
|
+
const direction = String(flags.direction || "").trim().toUpperCase();
|
|
3864
|
+
if (direction && !["LONG", "SHORT"].includes(direction)) {
|
|
3865
|
+
throw new Error(
|
|
3866
|
+
`Unsupported --direction "${direction}". Use LONG or SHORT.`
|
|
3867
|
+
);
|
|
3868
|
+
}
|
|
3835
3869
|
const maxDepth = normalizePositiveInt(flags.maxDepth, 2);
|
|
3836
3870
|
const minSupport = normalizePositiveInt(flags.minSupport, 20);
|
|
3837
3871
|
const minProfitFactor = normalizeNonNegativeNumber(
|
|
@@ -3869,6 +3903,14 @@ var main = async () => {
|
|
|
3869
3903
|
}) ?? flags.validationSplit,
|
|
3870
3904
|
0.25
|
|
3871
3905
|
) : 0.25;
|
|
3906
|
+
const testSplit = hasCliOption2("testSplit", "T") ? normalizeRatio(
|
|
3907
|
+
readAiPocketSearchCliOption({
|
|
3908
|
+
argv: process.argv,
|
|
3909
|
+
longName: "testSplit",
|
|
3910
|
+
shortName: "T"
|
|
3911
|
+
}) ?? flags.testSplit,
|
|
3912
|
+
0
|
|
3913
|
+
) : 0;
|
|
3872
3914
|
const explicitMinValidationSupport = normalizeInt(
|
|
3873
3915
|
flags.minValidationSupport,
|
|
3874
3916
|
0
|
|
@@ -4008,11 +4050,9 @@ var main = async () => {
|
|
|
4008
4050
|
}
|
|
4009
4051
|
}
|
|
4010
4052
|
});
|
|
4011
|
-
const
|
|
4012
|
-
const
|
|
4013
|
-
|
|
4014
|
-
validationSplit
|
|
4015
|
-
);
|
|
4053
|
+
const scopedRows = resolveScopeRows(rows, scope);
|
|
4054
|
+
const scopeRows = direction ? scopedRows.filter((row) => row.direction === direction) : scopedRows;
|
|
4055
|
+
const { trainRows, validationRows, testRows } = splitAiPocketResearchRowsByTimestamp(scopeRows, validationSplit, testSplit);
|
|
4016
4056
|
const minValidationSupport = explicitMinValidationSupport > 0 ? explicitMinValidationSupport : validationRows.length ? Math.max(3, Math.ceil(minSupport * validationSplit * 0.5)) : 0;
|
|
4017
4057
|
const currentGateSummary = summarizeAiTrainEvaluations(rows);
|
|
4018
4058
|
const currentGateQualityThresholds = summarizeAiTrainEvaluationsByQualityThreshold(rows, qualityThresholds);
|
|
@@ -4075,9 +4115,11 @@ var main = async () => {
|
|
|
4075
4115
|
selectedRows,
|
|
4076
4116
|
evaluatedRows: rows.length,
|
|
4077
4117
|
scope,
|
|
4118
|
+
direction: direction || null,
|
|
4078
4119
|
scopeRows: scopeRows.length,
|
|
4079
4120
|
trainRows: trainRows.length,
|
|
4080
4121
|
validationRows: validationRows.length,
|
|
4122
|
+
testRows: testRows.length,
|
|
4081
4123
|
scanned,
|
|
4082
4124
|
dateSkipped,
|
|
4083
4125
|
failed,
|
|
@@ -4092,6 +4134,7 @@ var main = async () => {
|
|
|
4092
4134
|
includeGateContext,
|
|
4093
4135
|
featureProfile,
|
|
4094
4136
|
validationSplit,
|
|
4137
|
+
testSplit,
|
|
4095
4138
|
minValidationSupport,
|
|
4096
4139
|
reportPath,
|
|
4097
4140
|
search: {
|
|
@@ -4103,6 +4146,7 @@ var main = async () => {
|
|
|
4103
4146
|
maxAtomicPredicates,
|
|
4104
4147
|
maxCombinations,
|
|
4105
4148
|
validationSplit,
|
|
4149
|
+
testSplit,
|
|
4106
4150
|
minValidationSupport,
|
|
4107
4151
|
dedupeEquivalentSelections,
|
|
4108
4152
|
top
|
|
@@ -4144,9 +4188,11 @@ var main = async () => {
|
|
|
4144
4188
|
["selected", import_chalk.default.blue(String(selectedRows))],
|
|
4145
4189
|
["evaluated", import_chalk.default.blue(String(rows.length))],
|
|
4146
4190
|
["scope", import_chalk.default.yellow(scope)],
|
|
4191
|
+
["direction", direction ? import_chalk.default.yellow(direction) : import_chalk.default.gray("all")],
|
|
4147
4192
|
["scope_rows", import_chalk.default.blue(String(scopeRows.length))],
|
|
4148
4193
|
["train_rows", import_chalk.default.blue(String(trainRows.length))],
|
|
4149
4194
|
["validation_rows", import_chalk.default.blue(String(validationRows.length))],
|
|
4195
|
+
["test_rows", import_chalk.default.blue(String(testRows.length))],
|
|
4150
4196
|
["source_rows", import_chalk.default.blue(String(totalRows))],
|
|
4151
4197
|
["date_skipped", import_chalk.default.blue(String(dateSkipped))],
|
|
4152
4198
|
[
|
|
@@ -4173,6 +4219,7 @@ var main = async () => {
|
|
|
4173
4219
|
["max_atomic_predicates", import_chalk.default.magenta(String(maxAtomicPredicates))],
|
|
4174
4220
|
["max_combinations", import_chalk.default.magenta(String(maxCombinations))],
|
|
4175
4221
|
["validation_split", import_chalk.default.magenta(formatRatio(validationSplit))],
|
|
4222
|
+
["test_split", import_chalk.default.magenta(formatRatio(testSplit))],
|
|
4176
4223
|
["min_validation_support", import_chalk.default.magenta(String(minValidationSupport))],
|
|
4177
4224
|
[
|
|
4178
4225
|
"dedupe_equivalent",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tradejs/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.12",
|
|
4
4
|
"description": "Official CLI for the TradeJS TypeScript framework: infra setup, backtests, signals, bots, and ML workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tradejs",
|
|
@@ -29,14 +29,14 @@
|
|
|
29
29
|
"tradejs": "dist/cli.js"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@tradejs/base": "^2.0.
|
|
33
|
-
"@tradejs/connectors": "^2.0.
|
|
34
|
-
"@tradejs/core": "^2.0.
|
|
35
|
-
"@tradejs/indicators": "^2.0.
|
|
36
|
-
"@tradejs/infra": "^2.0.
|
|
37
|
-
"@tradejs/node": "^2.0.
|
|
38
|
-
"@tradejs/strategies": "^2.0.
|
|
39
|
-
"@tradejs/types": "^2.0.
|
|
32
|
+
"@tradejs/base": "^2.0.12",
|
|
33
|
+
"@tradejs/connectors": "^2.0.12",
|
|
34
|
+
"@tradejs/core": "^2.0.12",
|
|
35
|
+
"@tradejs/indicators": "^2.0.12",
|
|
36
|
+
"@tradejs/infra": "^2.0.12",
|
|
37
|
+
"@tradejs/node": "^2.0.12",
|
|
38
|
+
"@tradejs/strategies": "^2.0.12",
|
|
39
|
+
"@tradejs/types": "^2.0.12",
|
|
40
40
|
"args": "^5.0.3",
|
|
41
41
|
"bcryptjs": "^2.4.3",
|
|
42
42
|
"chalk": "4.1.2",
|