@tradejs/cli 2.0.9 → 2.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/dist/cli.js +220 -70
- package/dist/lib/aiPocketSearch.js +3 -0
- package/dist/lib/aiPocketSearchCli.js +45 -3
- package/dist/lib/runtimeDebugEvidence.js +2 -1
- package/dist/lib/runtimeRedis.js +11 -0
- package/dist/scripts/aiPocketSearch.js +78 -31
- package/dist/scripts/replayRuntimeEvidence.js +2 -1
- package/dist/scripts/runtimeEvidence.js +2 -1
- package/dist/scripts/signalsSummary.js +138 -35
- 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",
|
|
@@ -11160,7 +11207,7 @@ var init_timeWindow = __esm({
|
|
|
11160
11207
|
});
|
|
11161
11208
|
|
|
11162
11209
|
// src/lib/runtimeRedis.ts
|
|
11163
|
-
var import_redis4, import_time, RESERVED_STRATEGY_NAMES, RESERVED_CONFIG_IDS, resolveStrategyConfigIdentityByKey, isRuntimeStrategyEnabled, isRuntimeTradeRecord, resolveStrategyNameByConfigKey, getRuntimeStrategyConfigKeys, loadRuntimeStrategyNames, loadRuntimeStrategyConfigs, loadRuntimeTrades, loadStrategyResultSymbols;
|
|
11210
|
+
var import_redis4, import_time, RESERVED_STRATEGY_NAMES, RESERVED_CONFIG_IDS, resolveStrategyConfigIdentityByKey, isRuntimeStrategyEnabled, isRuntimeTradeRecord, resolveStrategyNameByConfigKey, getRuntimeStrategyConfigKeys, loadRuntimeStrategyNames, loadRuntimeStrategyConfigs, loadRuntimeTrades, loadRuntimeActiveTradeOrderIds, loadStrategyResultSymbols;
|
|
11164
11211
|
var init_runtimeRedis = __esm({
|
|
11165
11212
|
"src/lib/runtimeRedis.ts"() {
|
|
11166
11213
|
"use strict";
|
|
@@ -11264,6 +11311,15 @@ var init_runtimeRedis = __esm({
|
|
|
11264
11311
|
const trades = await Promise.all(keys.map((key) => (0, import_redis4.getData)(key, null)));
|
|
11265
11312
|
return trades.filter(isRuntimeTradeRecord).filter(filterByWindow).sort((left, right) => left.entryTimestamp - right.entryTimestamp);
|
|
11266
11313
|
};
|
|
11314
|
+
loadRuntimeActiveTradeOrderIds = async (userName2) => {
|
|
11315
|
+
const keys = await (0, import_redis4.getKeys)(import_redis4.redisKeys.runtimeActiveTrades(userName2));
|
|
11316
|
+
const refs = await Promise.all(keys.map((key) => (0, import_redis4.getData)(key, null)));
|
|
11317
|
+
return new Set(
|
|
11318
|
+
refs.map(
|
|
11319
|
+
(ref) => ref && typeof ref === "object" && "orderId" in ref && typeof ref.orderId === "string" && ref.orderId.trim() ? ref.orderId.trim() : null
|
|
11320
|
+
).filter((orderId) => orderId != null)
|
|
11321
|
+
);
|
|
11322
|
+
};
|
|
11267
11323
|
loadStrategyResultSymbols = async ({
|
|
11268
11324
|
userName: userName2,
|
|
11269
11325
|
strategy
|
|
@@ -24892,7 +24948,8 @@ var init_runtimeDebugEvidence = __esm({
|
|
|
24892
24948
|
);
|
|
24893
24949
|
const activeTradeKey = import_redis14.redisKeys.runtimeActiveTrade(
|
|
24894
24950
|
userName2,
|
|
24895
|
-
trade.symbol
|
|
24951
|
+
trade.symbol,
|
|
24952
|
+
trade.deploymentId ?? trade.accountId
|
|
24896
24953
|
);
|
|
24897
24954
|
const signalKey = trade.signalId != null ? import_redis14.redisKeys.storeSignal(trade.symbol, trade.signalId) : null;
|
|
24898
24955
|
const evaluationBucketKey = import_redis14.redisKeys.runtimeSignalEvaluationBucket(
|
|
@@ -33975,7 +34032,7 @@ __export(signalsSummary_exports, {
|
|
|
33975
34032
|
main: () => main36,
|
|
33976
34033
|
signalsSummary: () => signalsSummary
|
|
33977
34034
|
});
|
|
33978
|
-
var import_config11, import_args29, import_logger12, import_connectors18, flags27, projectRoot13, SUMMARY_TIMEZONE2, SUMMARY_TIMEZONE_LABEL2, escapeHtml5, formatMskDateTime3, formatSigned3, formatPnlMoneyText, formatWinRateText, resolveSummaryTitle, normalizeStatus, shouldAttachDebugReport, getSummaryDateParts, formatSummaryIsoDayKey, unescapeHtml, stripTelegramHtml, buildSignalsSummaryAttachment, appendReportAttachmentSummary, resolveSummaryConnectorName, createRuntimeSignalStats, mergeRuntimeSignalStats, buildSummaryPrelude, resolveTradeStatusEmoji, isTimestampInWindow, isRuntimeTradeClosedInWindow, buildSummaryMessages, signalsSummary, main36;
|
|
34035
|
+
var import_config11, import_args29, import_logger12, import_connectors18, import_tradingAccounts4, flags27, projectRoot13, SUMMARY_TIMEZONE2, SUMMARY_TIMEZONE_LABEL2, escapeHtml5, formatMskDateTime3, formatSigned3, formatPnlMoneyText, formatWinRateText, resolveSummaryTitle, normalizeStatus, shouldAttachDebugReport, getSummaryDateParts, formatSummaryIsoDayKey, unescapeHtml, stripTelegramHtml, buildSignalsSummaryAttachment, appendReportAttachmentSummary, resolveSummaryConnectorName, groupRuntimeTradesByConnectorScope, syncRuntimeTradeScopes, createRuntimeSignalStats, mergeRuntimeSignalStats, buildSummaryPrelude, resolveTradeStatusEmoji, isTimestampInWindow, isRuntimeTradeClosedInWindow, buildSummaryMessages, signalsSummary, main36;
|
|
33979
34036
|
var init_signalsSummary = __esm({
|
|
33980
34037
|
"src/scripts/signalsSummary.ts"() {
|
|
33981
34038
|
"use strict";
|
|
@@ -33989,6 +34046,7 @@ var init_signalsSummary = __esm({
|
|
|
33989
34046
|
init_runtimeSignalsLoader();
|
|
33990
34047
|
init_runtimeSignalsStorage();
|
|
33991
34048
|
init_runtimeDebugEvidence();
|
|
34049
|
+
import_tradingAccounts4 = require("@tradejs/infra/tradingAccounts");
|
|
33992
34050
|
import_args29.default.option(["u", "user"], "Use user config", "root");
|
|
33993
34051
|
import_args29.default.option(
|
|
33994
34052
|
"connector",
|
|
@@ -34101,6 +34159,108 @@ var init_signalsSummary = __esm({
|
|
|
34101
34159
|
);
|
|
34102
34160
|
return import_connectors18.DEFAULT_CONNECTOR_NAME;
|
|
34103
34161
|
};
|
|
34162
|
+
groupRuntimeTradesByConnectorScope = ({
|
|
34163
|
+
trades,
|
|
34164
|
+
deployments,
|
|
34165
|
+
fallbackConnectorName
|
|
34166
|
+
}) => {
|
|
34167
|
+
const deploymentById = new Map(
|
|
34168
|
+
deployments.map((deployment) => [deployment.id, deployment])
|
|
34169
|
+
);
|
|
34170
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
34171
|
+
for (const trade of trades) {
|
|
34172
|
+
const deployment = trade.deploymentId ? deploymentById.get(trade.deploymentId) : void 0;
|
|
34173
|
+
const connectorName = deployment?.connectorName ?? fallbackConnectorName;
|
|
34174
|
+
const universe = trade.universe ?? deployment?.universe ?? "crypto";
|
|
34175
|
+
const accountId = trade.accountId ?? deployment?.accountId;
|
|
34176
|
+
const deploymentId = trade.deploymentId;
|
|
34177
|
+
const scopeKey = JSON.stringify([
|
|
34178
|
+
connectorName,
|
|
34179
|
+
universe,
|
|
34180
|
+
accountId ?? null,
|
|
34181
|
+
deploymentId ?? null
|
|
34182
|
+
]);
|
|
34183
|
+
const scope = scopes.get(scopeKey) ?? {
|
|
34184
|
+
connectorName,
|
|
34185
|
+
universe,
|
|
34186
|
+
...accountId ? { accountId } : {},
|
|
34187
|
+
...deploymentId ? { deploymentId } : {},
|
|
34188
|
+
trades: []
|
|
34189
|
+
};
|
|
34190
|
+
scope.trades.push(trade);
|
|
34191
|
+
scopes.set(scopeKey, scope);
|
|
34192
|
+
}
|
|
34193
|
+
return [...scopes.values()];
|
|
34194
|
+
};
|
|
34195
|
+
syncRuntimeTradeScopes = async ({
|
|
34196
|
+
userName: userName2,
|
|
34197
|
+
startTime,
|
|
34198
|
+
endTime,
|
|
34199
|
+
fallbackConnectorName,
|
|
34200
|
+
trades,
|
|
34201
|
+
deployments
|
|
34202
|
+
}) => {
|
|
34203
|
+
const scopes = groupRuntimeTradesByConnectorScope({
|
|
34204
|
+
trades,
|
|
34205
|
+
deployments,
|
|
34206
|
+
fallbackConnectorName
|
|
34207
|
+
});
|
|
34208
|
+
const syncedTrades = [];
|
|
34209
|
+
const connectorNames = /* @__PURE__ */ new Set();
|
|
34210
|
+
for (const scope of scopes) {
|
|
34211
|
+
const connectorName = await resolveSummaryConnectorName(
|
|
34212
|
+
scope.connectorName
|
|
34213
|
+
);
|
|
34214
|
+
const connectorFactory = await (0, import_connectors18.getConnectorCreatorByName)(
|
|
34215
|
+
connectorName,
|
|
34216
|
+
projectRoot13
|
|
34217
|
+
);
|
|
34218
|
+
if (!connectorFactory) {
|
|
34219
|
+
throw new Error(`Connector "${connectorName}" is not registered`);
|
|
34220
|
+
}
|
|
34221
|
+
const connector = await connectorFactory({
|
|
34222
|
+
userName: userName2,
|
|
34223
|
+
universe: scope.universe,
|
|
34224
|
+
accountId: scope.accountId,
|
|
34225
|
+
deploymentId: scope.deploymentId
|
|
34226
|
+
});
|
|
34227
|
+
connectorNames.add(connectorName);
|
|
34228
|
+
syncedTrades.push(
|
|
34229
|
+
...await syncRuntimeTrades({
|
|
34230
|
+
userName: userName2,
|
|
34231
|
+
connector,
|
|
34232
|
+
trades: scope.trades,
|
|
34233
|
+
startTime,
|
|
34234
|
+
endTime,
|
|
34235
|
+
openPositionCallbacks: {
|
|
34236
|
+
onError: (error) => {
|
|
34237
|
+
import_logger12.logger.warn(
|
|
34238
|
+
"signals summary: getOpenPositionPnl failed for scope account=%s deployment=%s: %s",
|
|
34239
|
+
scope.accountId ?? "default",
|
|
34240
|
+
scope.deploymentId ?? "default",
|
|
34241
|
+
formatRuntimeTradeSyncError(error)
|
|
34242
|
+
);
|
|
34243
|
+
}
|
|
34244
|
+
},
|
|
34245
|
+
closedPnlCallbacks: {
|
|
34246
|
+
onError: (error) => {
|
|
34247
|
+
import_logger12.logger.warn(
|
|
34248
|
+
"signals summary: getClosedPnl failed for scope account=%s deployment=%s: %s",
|
|
34249
|
+
scope.accountId ?? "default",
|
|
34250
|
+
scope.deploymentId ?? "default",
|
|
34251
|
+
formatRuntimeTradeSyncError(error)
|
|
34252
|
+
);
|
|
34253
|
+
}
|
|
34254
|
+
}
|
|
34255
|
+
})
|
|
34256
|
+
);
|
|
34257
|
+
}
|
|
34258
|
+
return {
|
|
34259
|
+
trades: syncedTrades,
|
|
34260
|
+
connectorNames: [...connectorNames].sort(),
|
|
34261
|
+
scopesCount: scopes.length
|
|
34262
|
+
};
|
|
34263
|
+
};
|
|
34104
34264
|
createRuntimeSignalStats = () => ({
|
|
34105
34265
|
evaluated: 0,
|
|
34106
34266
|
signals: 0,
|
|
@@ -34410,46 +34570,35 @@ none`
|
|
|
34410
34570
|
);
|
|
34411
34571
|
const endTime = Date.now();
|
|
34412
34572
|
const startTime = endTime - hours * 60 * 60 * 1e3;
|
|
34413
|
-
const
|
|
34414
|
-
|
|
34415
|
-
connectorName,
|
|
34416
|
-
projectRoot13
|
|
34573
|
+
const fallbackConnectorName = await resolveSummaryConnectorName(
|
|
34574
|
+
flags27.connector
|
|
34417
34575
|
);
|
|
34418
|
-
|
|
34419
|
-
|
|
34420
|
-
|
|
34421
|
-
|
|
34422
|
-
|
|
34423
|
-
|
|
34424
|
-
|
|
34576
|
+
const [
|
|
34577
|
+
configuredStrategyNames,
|
|
34578
|
+
signals2,
|
|
34579
|
+
evaluationStatsBuckets,
|
|
34580
|
+
trades,
|
|
34581
|
+
runtimeDeployments
|
|
34582
|
+
] = await Promise.all([
|
|
34425
34583
|
loadRuntimeStrategyNames(flags27.user),
|
|
34426
34584
|
loadRuntimeSignals(flags27.user, { startTime, endTime }),
|
|
34427
34585
|
loadRuntimeSignalEvaluationStatsBuckets(flags27.user),
|
|
34428
|
-
loadRuntimeTrades(flags27.user)
|
|
34586
|
+
loadRuntimeTrades(flags27.user),
|
|
34587
|
+
(0, import_tradingAccounts4.listRuntimeDeployments)(flags27.user)
|
|
34429
34588
|
]);
|
|
34430
|
-
const
|
|
34589
|
+
const {
|
|
34590
|
+
trades: syncedTrades,
|
|
34591
|
+
connectorNames,
|
|
34592
|
+
scopesCount
|
|
34593
|
+
} = await syncRuntimeTradeScopes({
|
|
34431
34594
|
userName: flags27.user,
|
|
34432
|
-
connector,
|
|
34433
|
-
trades,
|
|
34434
34595
|
startTime,
|
|
34435
34596
|
endTime,
|
|
34436
|
-
|
|
34437
|
-
|
|
34438
|
-
|
|
34439
|
-
"signals summary: getOpenPositionPnl failed: %s",
|
|
34440
|
-
formatRuntimeTradeSyncError(error)
|
|
34441
|
-
);
|
|
34442
|
-
}
|
|
34443
|
-
},
|
|
34444
|
-
closedPnlCallbacks: {
|
|
34445
|
-
onError: (error) => {
|
|
34446
|
-
import_logger12.logger.warn(
|
|
34447
|
-
"signals summary: getClosedPnl failed: %s",
|
|
34448
|
-
formatRuntimeTradeSyncError(error)
|
|
34449
|
-
);
|
|
34450
|
-
}
|
|
34451
|
-
}
|
|
34597
|
+
fallbackConnectorName,
|
|
34598
|
+
trades,
|
|
34599
|
+
deployments: runtimeDeployments
|
|
34452
34600
|
});
|
|
34601
|
+
const activeTradeOrderIds = await loadRuntimeActiveTradeOrderIds(flags27.user);
|
|
34453
34602
|
const windowSignals = signals2.filter(
|
|
34454
34603
|
(signal) => signal.timestamp >= startTime && signal.timestamp < endTime
|
|
34455
34604
|
);
|
|
@@ -34467,7 +34616,7 @@ none`
|
|
|
34467
34616
|
(trade) => isRuntimeTradeClosedInWindow(trade, startTime, endTime)
|
|
34468
34617
|
);
|
|
34469
34618
|
const openSnapshotTrades = syncedTrades.filter(
|
|
34470
|
-
(trade) => trade.status === "active" && typeof trade.entryTimestamp === "number" && Number.isFinite(trade.entryTimestamp) && trade.entryTimestamp < endTime
|
|
34619
|
+
(trade) => trade.status === "active" && activeTradeOrderIds.has(trade.orderId) && typeof trade.entryTimestamp === "number" && Number.isFinite(trade.entryTimestamp) && trade.entryTimestamp < endTime
|
|
34471
34620
|
);
|
|
34472
34621
|
const windowTrades = [...windowClosedTrades, ...openSnapshotTrades];
|
|
34473
34622
|
const windowTradeSignalIds = new Set(
|
|
@@ -34490,12 +34639,13 @@ none`
|
|
|
34490
34639
|
0
|
|
34491
34640
|
);
|
|
34492
34641
|
import_logger12.logger.info(
|
|
34493
|
-
"signals summary window=%sh signals=%s evaluations=%s trades=%s
|
|
34642
|
+
"signals summary window=%sh signals=%s evaluations=%s trades=%s connectors=%s scopes=%s user=%s",
|
|
34494
34643
|
hours,
|
|
34495
34644
|
windowSignals.length,
|
|
34496
34645
|
windowEvaluationsCount,
|
|
34497
34646
|
windowClosedTrades.length,
|
|
34498
|
-
|
|
34647
|
+
connectorNames.join(",") || fallbackConnectorName,
|
|
34648
|
+
scopesCount,
|
|
34499
34649
|
flags27.user
|
|
34500
34650
|
);
|
|
34501
34651
|
const debugAttachment = shouldAttachDebugReport(flags27.debugAttachment) ? await buildRuntimeDebugReportAttachment({
|
|
@@ -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
|
});
|
|
@@ -382,7 +382,8 @@ var buildRuntimeDebugReportPayload = async ({
|
|
|
382
382
|
);
|
|
383
383
|
const activeTradeKey = import_redis3.redisKeys.runtimeActiveTrade(
|
|
384
384
|
userName,
|
|
385
|
-
trade.symbol
|
|
385
|
+
trade.symbol,
|
|
386
|
+
trade.deploymentId ?? trade.accountId
|
|
386
387
|
);
|
|
387
388
|
const signalKey = trade.signalId != null ? import_redis3.redisKeys.storeSignal(trade.symbol, trade.signalId) : null;
|
|
388
389
|
const evaluationBucketKey = import_redis3.redisKeys.runtimeSignalEvaluationBucket(
|
package/dist/lib/runtimeRedis.js
CHANGED
|
@@ -23,6 +23,7 @@ __export(runtimeRedis_exports, {
|
|
|
23
23
|
getRuntimeStrategyConfigKeys: () => getRuntimeStrategyConfigKeys,
|
|
24
24
|
isRuntimeStrategyEnabled: () => isRuntimeStrategyEnabled,
|
|
25
25
|
isRuntimeTradeRecord: () => isRuntimeTradeRecord,
|
|
26
|
+
loadRuntimeActiveTradeOrderIds: () => loadRuntimeActiveTradeOrderIds,
|
|
26
27
|
loadRuntimeStrategyConfigs: () => loadRuntimeStrategyConfigs,
|
|
27
28
|
loadRuntimeStrategyNames: () => loadRuntimeStrategyNames,
|
|
28
29
|
loadRuntimeTrades: () => loadRuntimeTrades,
|
|
@@ -131,6 +132,15 @@ var loadRuntimeTrades = async (userName, {
|
|
|
131
132
|
const trades = await Promise.all(keys.map((key) => (0, import_redis.getData)(key, null)));
|
|
132
133
|
return trades.filter(isRuntimeTradeRecord).filter(filterByWindow).sort((left, right) => left.entryTimestamp - right.entryTimestamp);
|
|
133
134
|
};
|
|
135
|
+
var loadRuntimeActiveTradeOrderIds = async (userName) => {
|
|
136
|
+
const keys = await (0, import_redis.getKeys)(import_redis.redisKeys.runtimeActiveTrades(userName));
|
|
137
|
+
const refs = await Promise.all(keys.map((key) => (0, import_redis.getData)(key, null)));
|
|
138
|
+
return new Set(
|
|
139
|
+
refs.map(
|
|
140
|
+
(ref) => ref && typeof ref === "object" && "orderId" in ref && typeof ref.orderId === "string" && ref.orderId.trim() ? ref.orderId.trim() : null
|
|
141
|
+
).filter((orderId) => orderId != null)
|
|
142
|
+
);
|
|
143
|
+
};
|
|
134
144
|
var loadStrategyResultSymbols = async ({
|
|
135
145
|
userName,
|
|
136
146
|
strategy
|
|
@@ -148,6 +158,7 @@ var loadStrategyResultSymbols = async ({
|
|
|
148
158
|
getRuntimeStrategyConfigKeys,
|
|
149
159
|
isRuntimeStrategyEnabled,
|
|
150
160
|
isRuntimeTradeRecord,
|
|
161
|
+
loadRuntimeActiveTradeOrderIds,
|
|
151
162
|
loadRuntimeStrategyConfigs,
|
|
152
163
|
loadRuntimeStrategyNames,
|
|
153
164
|
loadRuntimeTrades,
|
|
@@ -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",
|
|
@@ -382,7 +382,8 @@ var buildRuntimeDebugReportPayload = async ({
|
|
|
382
382
|
);
|
|
383
383
|
const activeTradeKey = import_redis3.redisKeys.runtimeActiveTrade(
|
|
384
384
|
userName,
|
|
385
|
-
trade.symbol
|
|
385
|
+
trade.symbol,
|
|
386
|
+
trade.deploymentId ?? trade.accountId
|
|
386
387
|
);
|
|
387
388
|
const signalKey = trade.signalId != null ? import_redis3.redisKeys.storeSignal(trade.symbol, trade.signalId) : null;
|
|
388
389
|
const evaluationBucketKey = import_redis3.redisKeys.runtimeSignalEvaluationBucket(
|
|
@@ -381,7 +381,8 @@ var buildRuntimeDebugReportPayload = async ({
|
|
|
381
381
|
);
|
|
382
382
|
const activeTradeKey = import_redis3.redisKeys.runtimeActiveTrade(
|
|
383
383
|
userName,
|
|
384
|
-
trade.symbol
|
|
384
|
+
trade.symbol,
|
|
385
|
+
trade.deploymentId ?? trade.accountId
|
|
385
386
|
);
|
|
386
387
|
const signalKey = trade.signalId != null ? import_redis3.redisKeys.storeSignal(trade.symbol, trade.signalId) : null;
|
|
387
388
|
const evaluationBucketKey = import_redis3.redisKeys.runtimeSignalEvaluationBucket(
|
|
@@ -107,6 +107,15 @@ var loadRuntimeTrades = async (userName, {
|
|
|
107
107
|
const trades = await Promise.all(keys.map((key) => (0, import_redis.getData)(key, null)));
|
|
108
108
|
return trades.filter(isRuntimeTradeRecord).filter(filterByWindow).sort((left, right) => left.entryTimestamp - right.entryTimestamp);
|
|
109
109
|
};
|
|
110
|
+
var loadRuntimeActiveTradeOrderIds = async (userName) => {
|
|
111
|
+
const keys = await (0, import_redis.getKeys)(import_redis.redisKeys.runtimeActiveTrades(userName));
|
|
112
|
+
const refs = await Promise.all(keys.map((key) => (0, import_redis.getData)(key, null)));
|
|
113
|
+
return new Set(
|
|
114
|
+
refs.map(
|
|
115
|
+
(ref) => ref && typeof ref === "object" && "orderId" in ref && typeof ref.orderId === "string" && ref.orderId.trim() ? ref.orderId.trim() : null
|
|
116
|
+
).filter((orderId) => orderId != null)
|
|
117
|
+
);
|
|
118
|
+
};
|
|
110
119
|
|
|
111
120
|
// src/lib/runtimeTradeSync.ts
|
|
112
121
|
var import_constants = require("@tradejs/core/constants");
|
|
@@ -636,7 +645,8 @@ var buildRuntimeDebugReportPayload = async ({
|
|
|
636
645
|
);
|
|
637
646
|
const activeTradeKey = import_redis4.redisKeys.runtimeActiveTrade(
|
|
638
647
|
userName,
|
|
639
|
-
trade.symbol
|
|
648
|
+
trade.symbol,
|
|
649
|
+
trade.deploymentId ?? trade.accountId
|
|
640
650
|
);
|
|
641
651
|
const signalKey = trade.signalId != null ? import_redis4.redisKeys.storeSignal(trade.symbol, trade.signalId) : null;
|
|
642
652
|
const evaluationBucketKey = import_redis4.redisKeys.runtimeSignalEvaluationBucket(
|
|
@@ -771,6 +781,7 @@ var buildRuntimeDebugReportAttachment = async (params) => {
|
|
|
771
781
|
};
|
|
772
782
|
|
|
773
783
|
// src/scripts/signalsSummary.ts
|
|
784
|
+
var import_tradingAccounts = require("@tradejs/infra/tradingAccounts");
|
|
774
785
|
import_args.default.option(["u", "user"], "Use user config", "root");
|
|
775
786
|
import_args.default.option(
|
|
776
787
|
"connector",
|
|
@@ -883,6 +894,108 @@ var resolveSummaryConnectorName = async (value) => {
|
|
|
883
894
|
);
|
|
884
895
|
return import_connectors.DEFAULT_CONNECTOR_NAME;
|
|
885
896
|
};
|
|
897
|
+
var groupRuntimeTradesByConnectorScope = ({
|
|
898
|
+
trades,
|
|
899
|
+
deployments,
|
|
900
|
+
fallbackConnectorName
|
|
901
|
+
}) => {
|
|
902
|
+
const deploymentById = new Map(
|
|
903
|
+
deployments.map((deployment) => [deployment.id, deployment])
|
|
904
|
+
);
|
|
905
|
+
const scopes = /* @__PURE__ */ new Map();
|
|
906
|
+
for (const trade of trades) {
|
|
907
|
+
const deployment = trade.deploymentId ? deploymentById.get(trade.deploymentId) : void 0;
|
|
908
|
+
const connectorName = deployment?.connectorName ?? fallbackConnectorName;
|
|
909
|
+
const universe = trade.universe ?? deployment?.universe ?? "crypto";
|
|
910
|
+
const accountId = trade.accountId ?? deployment?.accountId;
|
|
911
|
+
const deploymentId = trade.deploymentId;
|
|
912
|
+
const scopeKey = JSON.stringify([
|
|
913
|
+
connectorName,
|
|
914
|
+
universe,
|
|
915
|
+
accountId ?? null,
|
|
916
|
+
deploymentId ?? null
|
|
917
|
+
]);
|
|
918
|
+
const scope = scopes.get(scopeKey) ?? {
|
|
919
|
+
connectorName,
|
|
920
|
+
universe,
|
|
921
|
+
...accountId ? { accountId } : {},
|
|
922
|
+
...deploymentId ? { deploymentId } : {},
|
|
923
|
+
trades: []
|
|
924
|
+
};
|
|
925
|
+
scope.trades.push(trade);
|
|
926
|
+
scopes.set(scopeKey, scope);
|
|
927
|
+
}
|
|
928
|
+
return [...scopes.values()];
|
|
929
|
+
};
|
|
930
|
+
var syncRuntimeTradeScopes = async ({
|
|
931
|
+
userName,
|
|
932
|
+
startTime,
|
|
933
|
+
endTime,
|
|
934
|
+
fallbackConnectorName,
|
|
935
|
+
trades,
|
|
936
|
+
deployments
|
|
937
|
+
}) => {
|
|
938
|
+
const scopes = groupRuntimeTradesByConnectorScope({
|
|
939
|
+
trades,
|
|
940
|
+
deployments,
|
|
941
|
+
fallbackConnectorName
|
|
942
|
+
});
|
|
943
|
+
const syncedTrades = [];
|
|
944
|
+
const connectorNames = /* @__PURE__ */ new Set();
|
|
945
|
+
for (const scope of scopes) {
|
|
946
|
+
const connectorName = await resolveSummaryConnectorName(
|
|
947
|
+
scope.connectorName
|
|
948
|
+
);
|
|
949
|
+
const connectorFactory = await (0, import_connectors.getConnectorCreatorByName)(
|
|
950
|
+
connectorName,
|
|
951
|
+
projectRoot
|
|
952
|
+
);
|
|
953
|
+
if (!connectorFactory) {
|
|
954
|
+
throw new Error(`Connector "${connectorName}" is not registered`);
|
|
955
|
+
}
|
|
956
|
+
const connector = await connectorFactory({
|
|
957
|
+
userName,
|
|
958
|
+
universe: scope.universe,
|
|
959
|
+
accountId: scope.accountId,
|
|
960
|
+
deploymentId: scope.deploymentId
|
|
961
|
+
});
|
|
962
|
+
connectorNames.add(connectorName);
|
|
963
|
+
syncedTrades.push(
|
|
964
|
+
...await syncRuntimeTrades({
|
|
965
|
+
userName,
|
|
966
|
+
connector,
|
|
967
|
+
trades: scope.trades,
|
|
968
|
+
startTime,
|
|
969
|
+
endTime,
|
|
970
|
+
openPositionCallbacks: {
|
|
971
|
+
onError: (error) => {
|
|
972
|
+
import_logger.logger.warn(
|
|
973
|
+
"signals summary: getOpenPositionPnl failed for scope account=%s deployment=%s: %s",
|
|
974
|
+
scope.accountId ?? "default",
|
|
975
|
+
scope.deploymentId ?? "default",
|
|
976
|
+
formatRuntimeTradeSyncError(error)
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
},
|
|
980
|
+
closedPnlCallbacks: {
|
|
981
|
+
onError: (error) => {
|
|
982
|
+
import_logger.logger.warn(
|
|
983
|
+
"signals summary: getClosedPnl failed for scope account=%s deployment=%s: %s",
|
|
984
|
+
scope.accountId ?? "default",
|
|
985
|
+
scope.deploymentId ?? "default",
|
|
986
|
+
formatRuntimeTradeSyncError(error)
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
})
|
|
991
|
+
);
|
|
992
|
+
}
|
|
993
|
+
return {
|
|
994
|
+
trades: syncedTrades,
|
|
995
|
+
connectorNames: [...connectorNames].sort(),
|
|
996
|
+
scopesCount: scopes.length
|
|
997
|
+
};
|
|
998
|
+
};
|
|
886
999
|
var createRuntimeSignalStats = () => ({
|
|
887
1000
|
evaluated: 0,
|
|
888
1001
|
signals: 0,
|
|
@@ -1192,46 +1305,35 @@ var signalsSummary = async () => {
|
|
|
1192
1305
|
);
|
|
1193
1306
|
const endTime = Date.now();
|
|
1194
1307
|
const startTime = endTime - hours * 60 * 60 * 1e3;
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1197
|
-
connectorName,
|
|
1198
|
-
projectRoot
|
|
1308
|
+
const fallbackConnectorName = await resolveSummaryConnectorName(
|
|
1309
|
+
flags.connector
|
|
1199
1310
|
);
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1311
|
+
const [
|
|
1312
|
+
configuredStrategyNames,
|
|
1313
|
+
signals,
|
|
1314
|
+
evaluationStatsBuckets,
|
|
1315
|
+
trades,
|
|
1316
|
+
runtimeDeployments
|
|
1317
|
+
] = await Promise.all([
|
|
1207
1318
|
loadRuntimeStrategyNames(flags.user),
|
|
1208
1319
|
loadRuntimeSignals(flags.user, { startTime, endTime }),
|
|
1209
1320
|
loadRuntimeSignalEvaluationStatsBuckets(flags.user),
|
|
1210
|
-
loadRuntimeTrades(flags.user)
|
|
1321
|
+
loadRuntimeTrades(flags.user),
|
|
1322
|
+
(0, import_tradingAccounts.listRuntimeDeployments)(flags.user)
|
|
1211
1323
|
]);
|
|
1212
|
-
const
|
|
1324
|
+
const {
|
|
1325
|
+
trades: syncedTrades,
|
|
1326
|
+
connectorNames,
|
|
1327
|
+
scopesCount
|
|
1328
|
+
} = await syncRuntimeTradeScopes({
|
|
1213
1329
|
userName: flags.user,
|
|
1214
|
-
connector,
|
|
1215
|
-
trades,
|
|
1216
1330
|
startTime,
|
|
1217
1331
|
endTime,
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
"signals summary: getOpenPositionPnl failed: %s",
|
|
1222
|
-
formatRuntimeTradeSyncError(error)
|
|
1223
|
-
);
|
|
1224
|
-
}
|
|
1225
|
-
},
|
|
1226
|
-
closedPnlCallbacks: {
|
|
1227
|
-
onError: (error) => {
|
|
1228
|
-
import_logger.logger.warn(
|
|
1229
|
-
"signals summary: getClosedPnl failed: %s",
|
|
1230
|
-
formatRuntimeTradeSyncError(error)
|
|
1231
|
-
);
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1332
|
+
fallbackConnectorName,
|
|
1333
|
+
trades,
|
|
1334
|
+
deployments: runtimeDeployments
|
|
1234
1335
|
});
|
|
1336
|
+
const activeTradeOrderIds = await loadRuntimeActiveTradeOrderIds(flags.user);
|
|
1235
1337
|
const windowSignals = signals.filter(
|
|
1236
1338
|
(signal) => signal.timestamp >= startTime && signal.timestamp < endTime
|
|
1237
1339
|
);
|
|
@@ -1249,7 +1351,7 @@ var signalsSummary = async () => {
|
|
|
1249
1351
|
(trade) => isRuntimeTradeClosedInWindow(trade, startTime, endTime)
|
|
1250
1352
|
);
|
|
1251
1353
|
const openSnapshotTrades = syncedTrades.filter(
|
|
1252
|
-
(trade) => trade.status === "active" && typeof trade.entryTimestamp === "number" && Number.isFinite(trade.entryTimestamp) && trade.entryTimestamp < endTime
|
|
1354
|
+
(trade) => trade.status === "active" && activeTradeOrderIds.has(trade.orderId) && typeof trade.entryTimestamp === "number" && Number.isFinite(trade.entryTimestamp) && trade.entryTimestamp < endTime
|
|
1253
1355
|
);
|
|
1254
1356
|
const windowTrades = [...windowClosedTrades, ...openSnapshotTrades];
|
|
1255
1357
|
const windowTradeSignalIds = new Set(
|
|
@@ -1272,12 +1374,13 @@ var signalsSummary = async () => {
|
|
|
1272
1374
|
0
|
|
1273
1375
|
);
|
|
1274
1376
|
import_logger.logger.info(
|
|
1275
|
-
"signals summary window=%sh signals=%s evaluations=%s trades=%s
|
|
1377
|
+
"signals summary window=%sh signals=%s evaluations=%s trades=%s connectors=%s scopes=%s user=%s",
|
|
1276
1378
|
hours,
|
|
1277
1379
|
windowSignals.length,
|
|
1278
1380
|
windowEvaluationsCount,
|
|
1279
1381
|
windowClosedTrades.length,
|
|
1280
|
-
|
|
1382
|
+
connectorNames.join(",") || fallbackConnectorName,
|
|
1383
|
+
scopesCount,
|
|
1281
1384
|
flags.user
|
|
1282
1385
|
);
|
|
1283
1386
|
const debugAttachment = shouldAttachDebugReport(flags.debugAttachment) ? await buildRuntimeDebugReportAttachment({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tradejs/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.11",
|
|
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.11",
|
|
33
|
+
"@tradejs/connectors": "^2.0.11",
|
|
34
|
+
"@tradejs/core": "^2.0.11",
|
|
35
|
+
"@tradejs/indicators": "^2.0.11",
|
|
36
|
+
"@tradejs/infra": "^2.0.11",
|
|
37
|
+
"@tradejs/node": "^2.0.11",
|
|
38
|
+
"@tradejs/strategies": "^2.0.11",
|
|
39
|
+
"@tradejs/types": "^2.0.11",
|
|
40
40
|
"args": "^5.0.3",
|
|
41
41
|
"bcryptjs": "^2.4.3",
|
|
42
42
|
"chalk": "4.1.2",
|