@tradejs/cli 2.0.13 → 2.0.14
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 +1543 -531
- package/dist/lib/aiTrainResearch.js +18 -3
- package/dist/lib/hyperliquidRateLimiter.js +75 -0
- package/dist/lib/hyperliquidWhaleBackfill.js +435 -0
- package/dist/lib/hyperliquidWhaleData.js +307 -0
- package/dist/lib/hyperliquidWhaleSelection.js +116 -0
- package/dist/lib/hyperliquidWhaleStream.js +326 -0
- package/dist/lib/marketContextPrepare.js +436 -2
- package/dist/lib/runtimeLineage.js +60 -4
- package/dist/scripts/aiTrain.js +18 -3
- package/dist/scripts/backtest.js +438 -0
- package/dist/scripts/hyperliquidWhaleBackfill.js +476 -0
- package/dist/scripts/hyperliquidWhaleIngest.js +333 -0
- package/dist/scripts/hyperliquidWhalesUpdate.js +369 -0
- package/dist/scripts/replay.js +500 -11
- package/dist/scripts/replayRunner.js +500 -11
- package/dist/scripts/runtimeParity.js +433 -0
- package/dist/scripts/signals.js +500 -13
- package/dist/scripts/signalsDaemon.js +500 -13
- package/package.json +12 -9
package/dist/cli.js
CHANGED
|
@@ -1093,7 +1093,7 @@ var require_common = __commonJS({
|
|
|
1093
1093
|
let enableOverride = null;
|
|
1094
1094
|
let namespacesCache;
|
|
1095
1095
|
let enabledCache;
|
|
1096
|
-
function debug(...
|
|
1096
|
+
function debug(...args33) {
|
|
1097
1097
|
if (!debug.enabled) {
|
|
1098
1098
|
return;
|
|
1099
1099
|
}
|
|
@@ -1104,28 +1104,28 @@ var require_common = __commonJS({
|
|
|
1104
1104
|
self.prev = prevTime;
|
|
1105
1105
|
self.curr = curr;
|
|
1106
1106
|
prevTime = curr;
|
|
1107
|
-
|
|
1108
|
-
if (typeof
|
|
1109
|
-
|
|
1107
|
+
args33[0] = createDebug.coerce(args33[0]);
|
|
1108
|
+
if (typeof args33[0] !== "string") {
|
|
1109
|
+
args33.unshift("%O");
|
|
1110
1110
|
}
|
|
1111
1111
|
let index = 0;
|
|
1112
|
-
|
|
1112
|
+
args33[0] = args33[0].replace(/%([a-zA-Z%])/g, (match, format2) => {
|
|
1113
1113
|
if (match === "%%") {
|
|
1114
1114
|
return "%";
|
|
1115
1115
|
}
|
|
1116
1116
|
index++;
|
|
1117
1117
|
const formatter = createDebug.formatters[format2];
|
|
1118
1118
|
if (typeof formatter === "function") {
|
|
1119
|
-
const val =
|
|
1119
|
+
const val = args33[index];
|
|
1120
1120
|
match = formatter.call(self, val);
|
|
1121
|
-
|
|
1121
|
+
args33.splice(index, 1);
|
|
1122
1122
|
index--;
|
|
1123
1123
|
}
|
|
1124
1124
|
return match;
|
|
1125
1125
|
});
|
|
1126
|
-
createDebug.formatArgs.call(self,
|
|
1126
|
+
createDebug.formatArgs.call(self, args33);
|
|
1127
1127
|
const logFn = self.log || createDebug.log;
|
|
1128
|
-
logFn.apply(self,
|
|
1128
|
+
logFn.apply(self, args33);
|
|
1129
1129
|
}
|
|
1130
1130
|
debug.namespace = namespace;
|
|
1131
1131
|
debug.useColors = createDebug.useColors();
|
|
@@ -1348,16 +1348,16 @@ var require_browser = __commonJS({
|
|
|
1348
1348
|
typeof navigator !== "undefined" && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker
|
|
1349
1349
|
typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
|
|
1350
1350
|
}
|
|
1351
|
-
function formatArgs(
|
|
1352
|
-
|
|
1351
|
+
function formatArgs(args33) {
|
|
1352
|
+
args33[0] = (this.useColors ? "%c" : "") + this.namespace + (this.useColors ? " %c" : " ") + args33[0] + (this.useColors ? "%c " : " ") + "+" + module2.exports.humanize(this.diff);
|
|
1353
1353
|
if (!this.useColors) {
|
|
1354
1354
|
return;
|
|
1355
1355
|
}
|
|
1356
1356
|
const c = "color: " + this.color;
|
|
1357
|
-
|
|
1357
|
+
args33.splice(1, 0, c, "color: inherit");
|
|
1358
1358
|
let index = 0;
|
|
1359
1359
|
let lastC = 0;
|
|
1360
|
-
|
|
1360
|
+
args33[0].replace(/%[a-zA-Z%]/g, (match) => {
|
|
1361
1361
|
if (match === "%%") {
|
|
1362
1362
|
return;
|
|
1363
1363
|
}
|
|
@@ -1366,7 +1366,7 @@ var require_browser = __commonJS({
|
|
|
1366
1366
|
lastC = index;
|
|
1367
1367
|
}
|
|
1368
1368
|
});
|
|
1369
|
-
|
|
1369
|
+
args33.splice(lastC, 0, c);
|
|
1370
1370
|
}
|
|
1371
1371
|
exports2.log = console.debug || console.log || (() => {
|
|
1372
1372
|
});
|
|
@@ -1648,16 +1648,16 @@ var require_node = __commonJS({
|
|
|
1648
1648
|
function useColors() {
|
|
1649
1649
|
return "colors" in exports2.inspectOpts ? Boolean(exports2.inspectOpts.colors) : tty.isatty(process.stderr.fd);
|
|
1650
1650
|
}
|
|
1651
|
-
function formatArgs(
|
|
1651
|
+
function formatArgs(args33) {
|
|
1652
1652
|
const { namespace: name, useColors: useColors2 } = this;
|
|
1653
1653
|
if (useColors2) {
|
|
1654
1654
|
const c = this.color;
|
|
1655
1655
|
const colorCode = "\x1B[3" + (c < 8 ? c : "8;5;" + c);
|
|
1656
1656
|
const prefix = ` ${colorCode};1m${name} \x1B[0m`;
|
|
1657
|
-
|
|
1658
|
-
|
|
1657
|
+
args33[0] = prefix + args33[0].split("\n").join("\n" + prefix);
|
|
1658
|
+
args33.push(colorCode + "m+" + module2.exports.humanize(this.diff) + "\x1B[0m");
|
|
1659
1659
|
} else {
|
|
1660
|
-
|
|
1660
|
+
args33[0] = getDate() + name + " " + args33[0];
|
|
1661
1661
|
}
|
|
1662
1662
|
}
|
|
1663
1663
|
function getDate() {
|
|
@@ -1666,8 +1666,8 @@ var require_node = __commonJS({
|
|
|
1666
1666
|
}
|
|
1667
1667
|
return (/* @__PURE__ */ new Date()).toISOString() + " ";
|
|
1668
1668
|
}
|
|
1669
|
-
function log(...
|
|
1670
|
-
return process.stderr.write(util.formatWithOptions(exports2.inspectOpts, ...
|
|
1669
|
+
function log(...args33) {
|
|
1670
|
+
return process.stderr.write(util.formatWithOptions(exports2.inspectOpts, ...args33) + "\n");
|
|
1671
1671
|
}
|
|
1672
1672
|
function save(namespaces) {
|
|
1673
1673
|
if (namespaces) {
|
|
@@ -6432,7 +6432,7 @@ var init_aiTrainEvaluationDump = __esm({
|
|
|
6432
6432
|
});
|
|
6433
6433
|
|
|
6434
6434
|
// src/lib/aiTrainResearch.ts
|
|
6435
|
-
var import_node_child_process2, import_node_crypto2, import_promises5, import_node_path3, DAY_MS3, RESEARCH_CONTEXT_ENV_KEYS, DERIVATIVES_CONTEXT_DATA_MODEL, normalizeForStableJson, fingerprintResearchValue, getTimestampRange, summarizeAiTrainCoverage, summarizeAiTrainRejectReasons, summarizeAiTrainTerminalWindows, readOptionalFile, getGitLineage, resolveGateFingerprint, buildAiTrainLineage;
|
|
6435
|
+
var import_node_child_process2, import_node_crypto2, import_promises5, import_node_path3, import_strategies2, import_types, DAY_MS3, RESEARCH_CONTEXT_ENV_KEYS, DERIVATIVES_CONTEXT_DATA_MODEL, normalizeForStableJson, fingerprintResearchValue, getTimestampRange, summarizeAiTrainCoverage, summarizeAiTrainRejectReasons, summarizeAiTrainTerminalWindows, readOptionalFile, getGitLineage, resolveGateFingerprint, buildAiTrainLineage;
|
|
6436
6436
|
var init_aiTrainResearch = __esm({
|
|
6437
6437
|
"src/lib/aiTrainResearch.ts"() {
|
|
6438
6438
|
"use strict";
|
|
@@ -6441,6 +6441,8 @@ var init_aiTrainResearch = __esm({
|
|
|
6441
6441
|
import_promises5 = __toESM(require("fs/promises"));
|
|
6442
6442
|
import_node_path3 = __toESM(require("path"));
|
|
6443
6443
|
init_aiTrainMetrics();
|
|
6444
|
+
import_strategies2 = require("@tradejs/node/strategies");
|
|
6445
|
+
import_types = require("@tradejs/types");
|
|
6444
6446
|
DAY_MS3 = 24 * 60 * 60 * 1e3;
|
|
6445
6447
|
RESEARCH_CONTEXT_ENV_KEYS = [
|
|
6446
6448
|
"AI_MODE",
|
|
@@ -6459,13 +6461,22 @@ var init_aiTrainResearch = __esm({
|
|
|
6459
6461
|
"COINMARKETCAP_CONTEXT_HISTORICAL_ACCESS_MONTHS",
|
|
6460
6462
|
"COINMARKETCAP_CONTEXT_BACKFILL_ENABLED",
|
|
6461
6463
|
"COINMARKETCAP_CONTEXT_BACKFILL_MAX_DAYS",
|
|
6462
|
-
"COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS"
|
|
6464
|
+
"COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS",
|
|
6465
|
+
"HYPERLIQUID_WHALE_CONTEXT_ENABLED",
|
|
6466
|
+
"HYPERLIQUID_WHALE_BACKFILL_ENABLED",
|
|
6467
|
+
"HYPERLIQUID_WHALE_MIN_COVERAGE_PCT",
|
|
6468
|
+
"HYPERLIQUID_WHALE_CONCURRENCY",
|
|
6469
|
+
"HYPERLIQUID_WHALE_RATE_LIMIT_WEIGHT"
|
|
6463
6470
|
];
|
|
6464
6471
|
DERIVATIVES_CONTEXT_DATA_MODEL = {
|
|
6465
6472
|
derivativesSourceIntervals: "15m",
|
|
6466
6473
|
derivativesDerivedIntervals: "1h",
|
|
6467
6474
|
derivativesHourlyFallback: "stored-1h",
|
|
6468
|
-
derivativesDataModelVersion: 2
|
|
6475
|
+
derivativesDataModelVersion: 2,
|
|
6476
|
+
hyperliquidWhaleCanonicalInterval: "1m",
|
|
6477
|
+
hyperliquidWhaleGateMinNotionalUsd: 5e4,
|
|
6478
|
+
hyperliquidWhaleMinCoveragePct: 0.8,
|
|
6479
|
+
hyperliquidWhaleDataModelVersion: import_types.HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
6469
6480
|
};
|
|
6470
6481
|
normalizeForStableJson = (value) => {
|
|
6471
6482
|
if (Array.isArray(value)) {
|
|
@@ -6622,12 +6633,16 @@ var init_aiTrainResearch = __esm({
|
|
|
6622
6633
|
const { gitSha, gitDirty } = getGitLineage(projectRoot14);
|
|
6623
6634
|
const gate = await resolveGateFingerprint(projectRoot14, strategyName, gitSha);
|
|
6624
6635
|
const normalizedConfigIds = [...new Set(configIds.filter(Boolean))].sort();
|
|
6636
|
+
const hyperliquidPerpUniverseFingerprint = typeof import_strategies2.getHyperliquidPerpUniverseSnapshot === "function" ? (0, import_strategies2.getHyperliquidPerpUniverseSnapshot)().fingerprint : null;
|
|
6637
|
+
const hyperliquidWhaleRegistryFingerprint = typeof import_strategies2.getHyperliquidWhaleRegistrySnapshot === "function" ? (0, import_strategies2.getHyperliquidWhaleRegistrySnapshot)().fingerprint : null;
|
|
6625
6638
|
const context = {
|
|
6626
6639
|
...Object.fromEntries(
|
|
6627
6640
|
RESEARCH_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
|
|
6628
6641
|
),
|
|
6629
6642
|
...runContext,
|
|
6630
|
-
...DERIVATIVES_CONTEXT_DATA_MODEL
|
|
6643
|
+
...DERIVATIVES_CONTEXT_DATA_MODEL,
|
|
6644
|
+
hyperliquidPerpUniverseFingerprint,
|
|
6645
|
+
hyperliquidWhaleRegistryFingerprint
|
|
6631
6646
|
};
|
|
6632
6647
|
return {
|
|
6633
6648
|
gitSha,
|
|
@@ -8439,7 +8454,7 @@ var init_binanceMarketData = __esm({
|
|
|
8439
8454
|
});
|
|
8440
8455
|
|
|
8441
8456
|
// src/lib/binanceMarketContextBackfill.ts
|
|
8442
|
-
var import_progress4, import_chalk6, import_connectors, import_connectors2,
|
|
8457
|
+
var import_progress4, import_chalk6, import_connectors, import_connectors2, import_strategies3, import_timescale, DAY_MS5, sleep, asInt, asFloat, parseEnabledFlag, uniqueSymbols, getReferenceSymbols, intervalToMarketFeatureInterval, marketIntervalToConnectorInterval, resolveBinanceMarketContextBackfillWindow, hasCoverage, buildBreadthBackfillChunks, buildTradeFlowBackfillChunks, filterMissingBreadthBackfillChunks, fetchAggTradesForWindow, getBinanceConnector, shouldBackfillBinanceMarketContextForBacktest, shouldBackfillBinanceMarketContextForSignals, shouldBackfillBinanceMarketContextForReplay, skippedBackfillResult, backfillBinanceMarketContext, backfillBinanceMarketContextForBacktest, backfillBinanceMarketContextForSignals, backfillBinanceMarketContextForReplay;
|
|
8443
8458
|
var init_binanceMarketContextBackfill = __esm({
|
|
8444
8459
|
"src/lib/binanceMarketContextBackfill.ts"() {
|
|
8445
8460
|
"use strict";
|
|
@@ -8447,7 +8462,7 @@ var init_binanceMarketContextBackfill = __esm({
|
|
|
8447
8462
|
import_chalk6 = __toESM(require("chalk"));
|
|
8448
8463
|
import_connectors = require("@tradejs/connectors");
|
|
8449
8464
|
import_connectors2 = require("@tradejs/node/connectors");
|
|
8450
|
-
|
|
8465
|
+
import_strategies3 = require("@tradejs/node/strategies");
|
|
8451
8466
|
import_timescale = require("@tradejs/infra/timescale");
|
|
8452
8467
|
init_binanceMarketData();
|
|
8453
8468
|
DAY_MS5 = 864e5;
|
|
@@ -8799,7 +8814,7 @@ var init_binanceMarketContextBackfill = __esm({
|
|
|
8799
8814
|
}
|
|
8800
8815
|
}
|
|
8801
8816
|
if (includeBreadth) {
|
|
8802
|
-
const breadthUniverses = (0,
|
|
8817
|
+
const breadthUniverses = (0, import_strategies3.getBinanceBreadthUniverses)();
|
|
8803
8818
|
const breadthSymbols = breadthUniverses.at(-1)?.symbols ?? [];
|
|
8804
8819
|
const connectorInterval = marketIntervalToConnectorInterval(interval5);
|
|
8805
8820
|
const chunks = buildBreadthBackfillChunks({
|
|
@@ -10985,8 +11000,432 @@ var init_coinMarketCapContextBackfill = __esm({
|
|
|
10985
11000
|
}
|
|
10986
11001
|
});
|
|
10987
11002
|
|
|
11003
|
+
// src/lib/hyperliquidWhaleData.ts
|
|
11004
|
+
var HYPERLIQUID_WHALE_BUCKET_MS, finiteNumber, POSITION_EPSILON, normalizePosition, classifyHyperliquidPositionChange, normalizeAddress, normalizeTradeFields, normalizeHyperliquidUserFill;
|
|
11005
|
+
var init_hyperliquidWhaleData = __esm({
|
|
11006
|
+
"src/lib/hyperliquidWhaleData.ts"() {
|
|
11007
|
+
"use strict";
|
|
11008
|
+
HYPERLIQUID_WHALE_BUCKET_MS = 6e4;
|
|
11009
|
+
finiteNumber = (value) => {
|
|
11010
|
+
const numeric = typeof value === "number" ? value : Number(value);
|
|
11011
|
+
return Number.isFinite(numeric) ? numeric : null;
|
|
11012
|
+
};
|
|
11013
|
+
POSITION_EPSILON = 1e-12;
|
|
11014
|
+
normalizePosition = (value) => Math.abs(value) <= POSITION_EPSILON ? 0 : value;
|
|
11015
|
+
classifyHyperliquidPositionChange = (params) => {
|
|
11016
|
+
const startValue = finiteNumber(params.startPosition);
|
|
11017
|
+
const size = finiteNumber(params.size);
|
|
11018
|
+
const side = String(params.side ?? "").toUpperCase();
|
|
11019
|
+
if (startValue == null || size == null || size <= 0) return null;
|
|
11020
|
+
if (side !== "B" && side !== "A") return null;
|
|
11021
|
+
const startPosition = normalizePosition(startValue);
|
|
11022
|
+
const endPosition = normalizePosition(
|
|
11023
|
+
startPosition + (side === "B" ? size : -size)
|
|
11024
|
+
);
|
|
11025
|
+
const startLong = Math.max(startPosition, 0);
|
|
11026
|
+
const endLong = Math.max(endPosition, 0);
|
|
11027
|
+
const startShort = Math.max(-startPosition, 0);
|
|
11028
|
+
const endShort = Math.max(-endPosition, 0);
|
|
11029
|
+
const longEntrySize = Math.max(0, endLong - startLong);
|
|
11030
|
+
const shortEntrySize = Math.max(0, endShort - startShort);
|
|
11031
|
+
const longExitSize = Math.max(0, startLong - endLong);
|
|
11032
|
+
const shortExitSize = Math.max(0, startShort - endShort);
|
|
11033
|
+
const flipped = startPosition * endPosition < 0;
|
|
11034
|
+
const opened = startPosition === 0;
|
|
11035
|
+
const closed = endPosition === 0;
|
|
11036
|
+
const increased = Math.sign(startPosition) === Math.sign(endPosition) && Math.abs(endPosition) > Math.abs(startPosition);
|
|
11037
|
+
return {
|
|
11038
|
+
startPosition,
|
|
11039
|
+
endPosition,
|
|
11040
|
+
action: flipped ? "flip" : opened ? "open" : closed ? "close" : increased ? "increase" : "reduce",
|
|
11041
|
+
longEntrySize,
|
|
11042
|
+
shortEntrySize,
|
|
11043
|
+
longExitSize,
|
|
11044
|
+
shortExitSize
|
|
11045
|
+
};
|
|
11046
|
+
};
|
|
11047
|
+
normalizeAddress = (value) => typeof value === "string" && /^0x[0-9a-fA-F]{40}$/.test(value) ? value.toLowerCase() : null;
|
|
11048
|
+
normalizeTradeFields = (value) => {
|
|
11049
|
+
const symbol = typeof value.coin === "string" ? value.coin.trim() : "";
|
|
11050
|
+
const price = finiteNumber(value.px);
|
|
11051
|
+
const size = finiteNumber(value.sz);
|
|
11052
|
+
const timestamp = finiteNumber(value.time);
|
|
11053
|
+
const tid = typeof value.tid === "string" || typeof value.tid === "number" ? String(value.tid) : "";
|
|
11054
|
+
if (!symbol || price == null || size == null || timestamp == null || !tid || price <= 0 || size <= 0) {
|
|
11055
|
+
return null;
|
|
11056
|
+
}
|
|
11057
|
+
return {
|
|
11058
|
+
symbol,
|
|
11059
|
+
price,
|
|
11060
|
+
size,
|
|
11061
|
+
timestamp,
|
|
11062
|
+
tid,
|
|
11063
|
+
notionalUsd: price * size
|
|
11064
|
+
};
|
|
11065
|
+
};
|
|
11066
|
+
normalizeHyperliquidUserFill = (params) => {
|
|
11067
|
+
const normalized = normalizeTradeFields(params.fill);
|
|
11068
|
+
if (!normalized || !params.trackedSymbols.has(normalized.symbol)) return null;
|
|
11069
|
+
const address = normalizeAddress(params.address);
|
|
11070
|
+
if (!address) return null;
|
|
11071
|
+
const side = String(params.fill.side ?? "").toUpperCase();
|
|
11072
|
+
if (side !== "B" && side !== "A") return null;
|
|
11073
|
+
const positionChange = classifyHyperliquidPositionChange({
|
|
11074
|
+
startPosition: params.fill.startPosition,
|
|
11075
|
+
side,
|
|
11076
|
+
size: normalized.size
|
|
11077
|
+
});
|
|
11078
|
+
const closedPnl = finiteNumber(params.fill.closedPnl);
|
|
11079
|
+
const liquidation = params.fill.liquidation != null && typeof params.fill.liquidation === "object";
|
|
11080
|
+
const positionFields = positionChange ? side === "B" ? {
|
|
11081
|
+
buyerStartPosition: positionChange.startPosition,
|
|
11082
|
+
buyerEndPosition: positionChange.endPosition,
|
|
11083
|
+
buyerPositionAction: positionChange.action,
|
|
11084
|
+
buyerClosedPnl: closedPnl,
|
|
11085
|
+
buyerLiquidation: liquidation
|
|
11086
|
+
} : {
|
|
11087
|
+
sellerStartPosition: positionChange.startPosition,
|
|
11088
|
+
sellerEndPosition: positionChange.endPosition,
|
|
11089
|
+
sellerPositionAction: positionChange.action,
|
|
11090
|
+
sellerClosedPnl: closedPnl,
|
|
11091
|
+
sellerLiquidation: liquidation
|
|
11092
|
+
} : {};
|
|
11093
|
+
return {
|
|
11094
|
+
symbol: normalized.symbol,
|
|
11095
|
+
ts: new Date(normalized.timestamp),
|
|
11096
|
+
tid: normalized.tid,
|
|
11097
|
+
price: normalized.price,
|
|
11098
|
+
size: normalized.size,
|
|
11099
|
+
notionalUsd: normalized.notionalUsd,
|
|
11100
|
+
buyerAddress: side === "B" ? address : null,
|
|
11101
|
+
sellerAddress: side === "A" ? address : null,
|
|
11102
|
+
buyerTracked: side === "B",
|
|
11103
|
+
sellerTracked: side === "A",
|
|
11104
|
+
...positionFields,
|
|
11105
|
+
...params.identity,
|
|
11106
|
+
source: "hyperliquid_user_fills"
|
|
11107
|
+
};
|
|
11108
|
+
};
|
|
11109
|
+
}
|
|
11110
|
+
});
|
|
11111
|
+
|
|
11112
|
+
// src/lib/hyperliquidRateLimiter.ts
|
|
11113
|
+
var DEFAULT_WEIGHT_PER_MINUTE, WINDOW_MS, sleep2, HyperliquidInfoRateLimiter, hyperliquidInfoResponseWeight;
|
|
11114
|
+
var init_hyperliquidRateLimiter = __esm({
|
|
11115
|
+
"src/lib/hyperliquidRateLimiter.ts"() {
|
|
11116
|
+
"use strict";
|
|
11117
|
+
DEFAULT_WEIGHT_PER_MINUTE = 1e3;
|
|
11118
|
+
WINDOW_MS = 6e4;
|
|
11119
|
+
sleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
11120
|
+
HyperliquidInfoRateLimiter = class {
|
|
11121
|
+
constructor(capacity = DEFAULT_WEIGHT_PER_MINUTE, now2 = Date.now, wait = sleep2) {
|
|
11122
|
+
this.capacity = capacity;
|
|
11123
|
+
this.now = now2;
|
|
11124
|
+
this.wait = wait;
|
|
11125
|
+
this.reservations = [];
|
|
11126
|
+
this.queue = Promise.resolve();
|
|
11127
|
+
}
|
|
11128
|
+
reserve(weight) {
|
|
11129
|
+
const normalizedWeight = Math.max(1, Math.ceil(weight));
|
|
11130
|
+
const pending = this.queue.then(
|
|
11131
|
+
() => this.reserveInternal(normalizedWeight)
|
|
11132
|
+
);
|
|
11133
|
+
this.queue = pending.catch(() => void 0);
|
|
11134
|
+
return pending;
|
|
11135
|
+
}
|
|
11136
|
+
async reserveInternal(weight) {
|
|
11137
|
+
if (weight > this.capacity) {
|
|
11138
|
+
throw new Error(
|
|
11139
|
+
`Hyperliquid request weight ${weight} exceeds limiter capacity ${this.capacity}`
|
|
11140
|
+
);
|
|
11141
|
+
}
|
|
11142
|
+
for (; ; ) {
|
|
11143
|
+
const nowMs = this.now();
|
|
11144
|
+
this.reservations = this.reservations.filter(
|
|
11145
|
+
(reservation) => reservation.atMs > nowMs - WINDOW_MS
|
|
11146
|
+
);
|
|
11147
|
+
const used = this.reservations.reduce(
|
|
11148
|
+
(sum2, reservation) => sum2 + reservation.weight,
|
|
11149
|
+
0
|
|
11150
|
+
);
|
|
11151
|
+
if (used + weight <= this.capacity) {
|
|
11152
|
+
this.reservations.push({ atMs: nowMs, weight });
|
|
11153
|
+
return;
|
|
11154
|
+
}
|
|
11155
|
+
const oldest = this.reservations[0];
|
|
11156
|
+
await this.wait(Math.max(1, oldest.atMs + WINDOW_MS - nowMs));
|
|
11157
|
+
}
|
|
11158
|
+
}
|
|
11159
|
+
};
|
|
11160
|
+
hyperliquidInfoResponseWeight = (rows) => Math.max(0, Math.ceil(Math.max(0, rows) / 20));
|
|
11161
|
+
}
|
|
11162
|
+
});
|
|
11163
|
+
|
|
11164
|
+
// src/lib/hyperliquidWhaleBackfill.ts
|
|
11165
|
+
var import_timescale4, import_strategies4, DEFAULT_BASE_URL, DEFAULT_CONCURRENCY, DEFAULT_RATE_LIMIT_WEIGHT, MAX_PAGE_SIZE, MAX_PAGES_PER_WHALE, REQUEST_WEIGHT, sleep3, positiveInt, minuteCeil, nextMinute, fetchHyperliquidUserFillsByTime, runConcurrent, backfillHyperliquidWhaleContext;
|
|
11166
|
+
var init_hyperliquidWhaleBackfill = __esm({
|
|
11167
|
+
"src/lib/hyperliquidWhaleBackfill.ts"() {
|
|
11168
|
+
"use strict";
|
|
11169
|
+
import_timescale4 = require("@tradejs/infra/timescale");
|
|
11170
|
+
import_strategies4 = require("@tradejs/node/strategies");
|
|
11171
|
+
init_hyperliquidWhaleData();
|
|
11172
|
+
init_hyperliquidRateLimiter();
|
|
11173
|
+
DEFAULT_BASE_URL = "https://api.hyperliquid.xyz";
|
|
11174
|
+
DEFAULT_CONCURRENCY = 2;
|
|
11175
|
+
DEFAULT_RATE_LIMIT_WEIGHT = 1e3;
|
|
11176
|
+
MAX_PAGE_SIZE = 2e3;
|
|
11177
|
+
MAX_PAGES_PER_WHALE = 5;
|
|
11178
|
+
REQUEST_WEIGHT = 20;
|
|
11179
|
+
sleep3 = (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
11180
|
+
positiveInt = (value, fallback) => {
|
|
11181
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
11182
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
11183
|
+
};
|
|
11184
|
+
minuteCeil = (value) => Math.ceil(value / HYPERLIQUID_WHALE_BUCKET_MS) * HYPERLIQUID_WHALE_BUCKET_MS;
|
|
11185
|
+
nextMinute = (value) => (Math.floor(value / HYPERLIQUID_WHALE_BUCKET_MS) + 1) * HYPERLIQUID_WHALE_BUCKET_MS;
|
|
11186
|
+
fetchHyperliquidUserFillsByTime = async (params) => {
|
|
11187
|
+
const fetchImpl = params.fetchImpl ?? fetch;
|
|
11188
|
+
const limiter = params.rateLimiter ?? new HyperliquidInfoRateLimiter(
|
|
11189
|
+
positiveInt(
|
|
11190
|
+
process.env.HYPERLIQUID_WHALE_RATE_LIMIT_WEIGHT,
|
|
11191
|
+
DEFAULT_RATE_LIMIT_WEIGHT
|
|
11192
|
+
),
|
|
11193
|
+
Date.now,
|
|
11194
|
+
params.wait ?? sleep3
|
|
11195
|
+
);
|
|
11196
|
+
const fills = [];
|
|
11197
|
+
let cursor = params.startTime;
|
|
11198
|
+
for (let page = 0; page < (params.maxPages ?? MAX_PAGES_PER_WHALE) && cursor <= params.endTime; page += 1) {
|
|
11199
|
+
await limiter.reserve(REQUEST_WEIGHT);
|
|
11200
|
+
const response = await fetchImpl(
|
|
11201
|
+
`${process.env.HYPERLIQUID_API_BASE_URL?.trim() || DEFAULT_BASE_URL}/info`,
|
|
11202
|
+
{
|
|
11203
|
+
method: "POST",
|
|
11204
|
+
headers: { "content-type": "application/json" },
|
|
11205
|
+
body: JSON.stringify({
|
|
11206
|
+
type: "userFillsByTime",
|
|
11207
|
+
user: params.address,
|
|
11208
|
+
startTime: cursor,
|
|
11209
|
+
endTime: params.endTime,
|
|
11210
|
+
aggregateByTime: true
|
|
11211
|
+
})
|
|
11212
|
+
}
|
|
11213
|
+
);
|
|
11214
|
+
if (!response.ok) {
|
|
11215
|
+
throw new Error(
|
|
11216
|
+
`Hyperliquid userFillsByTime ${response.status}: ${await response.text()}`
|
|
11217
|
+
);
|
|
11218
|
+
}
|
|
11219
|
+
const raw = await response.json();
|
|
11220
|
+
const pageRows = Array.isArray(raw) ? raw : [];
|
|
11221
|
+
const responseWeight = hyperliquidInfoResponseWeight(pageRows.length);
|
|
11222
|
+
if (responseWeight > 0) await limiter.reserve(responseWeight);
|
|
11223
|
+
fills.push(...pageRows);
|
|
11224
|
+
if (pageRows.length < MAX_PAGE_SIZE) {
|
|
11225
|
+
return { fills, truncated: false, coveredFromMs: params.startTime };
|
|
11226
|
+
}
|
|
11227
|
+
const lastTime = Math.max(
|
|
11228
|
+
...pageRows.map((row) => Number(row.time)).filter(Number.isFinite)
|
|
11229
|
+
);
|
|
11230
|
+
if (!Number.isFinite(lastTime) || lastTime < cursor) {
|
|
11231
|
+
throw new Error(
|
|
11232
|
+
`Hyperliquid fills pagination stalled for ${params.address}`
|
|
11233
|
+
);
|
|
11234
|
+
}
|
|
11235
|
+
cursor = lastTime + 1;
|
|
11236
|
+
}
|
|
11237
|
+
if (cursor > params.endTime) {
|
|
11238
|
+
return { fills, truncated: false, coveredFromMs: params.startTime };
|
|
11239
|
+
}
|
|
11240
|
+
const firstAvailableTime = Math.min(
|
|
11241
|
+
...fills.map((fill) => Number(fill.time)).filter(Number.isFinite)
|
|
11242
|
+
);
|
|
11243
|
+
return {
|
|
11244
|
+
fills,
|
|
11245
|
+
truncated: true,
|
|
11246
|
+
coveredFromMs: Math.min(params.endTime + 1, nextMinute(firstAvailableTime))
|
|
11247
|
+
};
|
|
11248
|
+
};
|
|
11249
|
+
runConcurrent = async (params) => {
|
|
11250
|
+
let nextIndex = 0;
|
|
11251
|
+
await Promise.all(
|
|
11252
|
+
Array.from(
|
|
11253
|
+
{ length: Math.min(params.concurrency, params.items.length) },
|
|
11254
|
+
async () => {
|
|
11255
|
+
for (; ; ) {
|
|
11256
|
+
const index = nextIndex;
|
|
11257
|
+
nextIndex += 1;
|
|
11258
|
+
if (index >= params.items.length) return;
|
|
11259
|
+
await params.worker(params.items[index], index);
|
|
11260
|
+
}
|
|
11261
|
+
}
|
|
11262
|
+
)
|
|
11263
|
+
);
|
|
11264
|
+
};
|
|
11265
|
+
backfillHyperliquidWhaleContext = async (params) => {
|
|
11266
|
+
const universe = (0, import_strategies4.getHyperliquidPerpUniverseSnapshot)();
|
|
11267
|
+
const whales = (0, import_strategies4.getHyperliquidWhaleRegistrySnapshot)();
|
|
11268
|
+
const trackedSymbols = new Set(universe.symbols);
|
|
11269
|
+
const identity = {
|
|
11270
|
+
universeFingerprint: universe.fingerprint,
|
|
11271
|
+
whaleRegistryFingerprint: whales.fingerprint
|
|
11272
|
+
};
|
|
11273
|
+
const fromMs = Math.floor(params.startMs / HYPERLIQUID_WHALE_BUCKET_MS) * HYPERLIQUID_WHALE_BUCKET_MS;
|
|
11274
|
+
const toMs = minuteCeil(params.endMs);
|
|
11275
|
+
const cached = await (0, import_timescale4.hasHyperliquidWhaleBackfillCoverage)({
|
|
11276
|
+
fromMs,
|
|
11277
|
+
toMs,
|
|
11278
|
+
...identity
|
|
11279
|
+
});
|
|
11280
|
+
const emptyResult = () => ({
|
|
11281
|
+
cached,
|
|
11282
|
+
cacheOnly: params.cacheOnly,
|
|
11283
|
+
fills: 0,
|
|
11284
|
+
events: 0,
|
|
11285
|
+
buckets: 0,
|
|
11286
|
+
coverageBuckets: 0,
|
|
11287
|
+
complete: cached,
|
|
11288
|
+
walletsProcessed: 0,
|
|
11289
|
+
walletsCached: 0,
|
|
11290
|
+
completeWallets: 0,
|
|
11291
|
+
truncatedWallets: 0,
|
|
11292
|
+
failedWallets: 0,
|
|
11293
|
+
fromMs,
|
|
11294
|
+
toMs
|
|
11295
|
+
});
|
|
11296
|
+
if (cached || params.cacheOnly) return emptyResult();
|
|
11297
|
+
const limiter = new HyperliquidInfoRateLimiter(
|
|
11298
|
+
positiveInt(
|
|
11299
|
+
process.env.HYPERLIQUID_WHALE_RATE_LIMIT_WEIGHT,
|
|
11300
|
+
DEFAULT_RATE_LIMIT_WEIGHT
|
|
11301
|
+
),
|
|
11302
|
+
Date.now,
|
|
11303
|
+
params.wait ?? sleep3
|
|
11304
|
+
);
|
|
11305
|
+
const concurrency = Math.min(
|
|
11306
|
+
4,
|
|
11307
|
+
positiveInt(
|
|
11308
|
+
params.concurrency ?? process.env.HYPERLIQUID_WHALE_CONCURRENCY,
|
|
11309
|
+
DEFAULT_CONCURRENCY
|
|
11310
|
+
)
|
|
11311
|
+
);
|
|
11312
|
+
let fills = 0;
|
|
11313
|
+
let events = 0;
|
|
11314
|
+
let walletsProcessed = 0;
|
|
11315
|
+
let walletsCached = 0;
|
|
11316
|
+
let completeWallets = 0;
|
|
11317
|
+
let truncatedWallets = 0;
|
|
11318
|
+
let failedWallets = 0;
|
|
11319
|
+
const failures = [];
|
|
11320
|
+
await runConcurrent({
|
|
11321
|
+
items: whales.addresses,
|
|
11322
|
+
concurrency,
|
|
11323
|
+
worker: async (address) => {
|
|
11324
|
+
const previous = await (0, import_timescale4.getHyperliquidWhaleWalletCoverage)({
|
|
11325
|
+
address,
|
|
11326
|
+
fromMs,
|
|
11327
|
+
toMs,
|
|
11328
|
+
...identity
|
|
11329
|
+
});
|
|
11330
|
+
if (previous && previous.status !== "failed") {
|
|
11331
|
+
walletsCached += 1;
|
|
11332
|
+
if (previous.status === "complete") completeWallets += 1;
|
|
11333
|
+
else truncatedWallets += 1;
|
|
11334
|
+
} else {
|
|
11335
|
+
try {
|
|
11336
|
+
const result2 = await fetchHyperliquidUserFillsByTime({
|
|
11337
|
+
address,
|
|
11338
|
+
startTime: fromMs,
|
|
11339
|
+
endTime: toMs - 1,
|
|
11340
|
+
fetchImpl: params.fetchImpl,
|
|
11341
|
+
rateLimiter: limiter
|
|
11342
|
+
});
|
|
11343
|
+
const addressEvents = result2.fills.map(
|
|
11344
|
+
(fill) => normalizeHyperliquidUserFill({
|
|
11345
|
+
fill,
|
|
11346
|
+
address,
|
|
11347
|
+
trackedSymbols,
|
|
11348
|
+
identity
|
|
11349
|
+
})
|
|
11350
|
+
).filter((row) => row != null);
|
|
11351
|
+
await (0, import_timescale4.upsertHyperliquidWhaleTradeEvents)(addressEvents);
|
|
11352
|
+
await (0, import_timescale4.upsertHyperliquidWhaleWalletCoverage)({
|
|
11353
|
+
address,
|
|
11354
|
+
fromMs,
|
|
11355
|
+
toMs,
|
|
11356
|
+
coveredFromMs: result2.coveredFromMs,
|
|
11357
|
+
coveredToMs: toMs,
|
|
11358
|
+
status: result2.truncated ? "truncated" : "complete",
|
|
11359
|
+
fillsCount: result2.fills.length,
|
|
11360
|
+
...identity
|
|
11361
|
+
});
|
|
11362
|
+
fills += result2.fills.length;
|
|
11363
|
+
events += addressEvents.length;
|
|
11364
|
+
if (result2.truncated) truncatedWallets += 1;
|
|
11365
|
+
else completeWallets += 1;
|
|
11366
|
+
} catch (error) {
|
|
11367
|
+
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
11368
|
+
failures.push(normalizedError);
|
|
11369
|
+
failedWallets += 1;
|
|
11370
|
+
await (0, import_timescale4.upsertHyperliquidWhaleWalletCoverage)({
|
|
11371
|
+
address,
|
|
11372
|
+
fromMs,
|
|
11373
|
+
toMs,
|
|
11374
|
+
coveredFromMs: null,
|
|
11375
|
+
coveredToMs: null,
|
|
11376
|
+
status: "failed",
|
|
11377
|
+
fillsCount: 0,
|
|
11378
|
+
error: normalizedError.message,
|
|
11379
|
+
...identity
|
|
11380
|
+
});
|
|
11381
|
+
}
|
|
11382
|
+
}
|
|
11383
|
+
walletsProcessed += 1;
|
|
11384
|
+
params.log?.(
|
|
11385
|
+
`Hyperliquid whales ${walletsProcessed}/${whales.addresses.length}: complete=${completeWallets}, truncated=${truncatedWallets}, failed=${failedWallets}, fills=${fills}, events=${events}`
|
|
11386
|
+
);
|
|
11387
|
+
}
|
|
11388
|
+
});
|
|
11389
|
+
const buckets = await (0, import_timescale4.rebuildHyperliquidWhaleFlowRows)({
|
|
11390
|
+
fromMs,
|
|
11391
|
+
toMs,
|
|
11392
|
+
...identity
|
|
11393
|
+
});
|
|
11394
|
+
const coverageBuckets = await (0, import_timescale4.rebuildHyperliquidWhaleCoverageRows)({
|
|
11395
|
+
fromMs,
|
|
11396
|
+
toMs,
|
|
11397
|
+
expectedWhales: whales.addresses.length,
|
|
11398
|
+
...identity
|
|
11399
|
+
});
|
|
11400
|
+
const result = {
|
|
11401
|
+
cached: false,
|
|
11402
|
+
cacheOnly: false,
|
|
11403
|
+
fills,
|
|
11404
|
+
events,
|
|
11405
|
+
buckets,
|
|
11406
|
+
coverageBuckets,
|
|
11407
|
+
complete: truncatedWallets === 0 && failedWallets === 0,
|
|
11408
|
+
walletsProcessed,
|
|
11409
|
+
walletsCached,
|
|
11410
|
+
completeWallets,
|
|
11411
|
+
truncatedWallets,
|
|
11412
|
+
failedWallets,
|
|
11413
|
+
fromMs,
|
|
11414
|
+
toMs
|
|
11415
|
+
};
|
|
11416
|
+
if (params.strict !== false && failures.length) {
|
|
11417
|
+
throw new AggregateError(
|
|
11418
|
+
failures,
|
|
11419
|
+
`Hyperliquid whale backfill failed for ${failures.length}/${whales.addresses.length} wallets`
|
|
11420
|
+
);
|
|
11421
|
+
}
|
|
11422
|
+
return result;
|
|
11423
|
+
};
|
|
11424
|
+
}
|
|
11425
|
+
});
|
|
11426
|
+
|
|
10988
11427
|
// src/lib/marketContextPrepare.ts
|
|
10989
|
-
var import_chalk9, shouldPrepareDerivativesContextForRun, shouldPrepareBinanceMarketContextForRun, shouldPrepareCoinMarketCapContextForRun, resolveCoinMarketCapBackfillForMode, buildCoinMarketCapBackfillParams, buildBinanceMarketBackfillParams, buildDerivativesBackfillParams, prepareMarketContextForRun;
|
|
11428
|
+
var import_chalk9, shouldPrepareDerivativesContextForRun, shouldPrepareBinanceMarketContextForRun, shouldPrepareCoinMarketCapContextForRun, shouldPrepareHyperliquidWhaleContextForRun, resolveCoinMarketCapBackfillForMode, buildCoinMarketCapBackfillParams, buildBinanceMarketBackfillParams, buildDerivativesBackfillParams, prepareMarketContextForRun;
|
|
10990
11429
|
var init_marketContextPrepare = __esm({
|
|
10991
11430
|
"src/lib/marketContextPrepare.ts"() {
|
|
10992
11431
|
"use strict";
|
|
@@ -10995,6 +11434,7 @@ var init_marketContextPrepare = __esm({
|
|
|
10995
11434
|
init_derivativesContextBackfill();
|
|
10996
11435
|
init_coinMarketCapContextBackfill();
|
|
10997
11436
|
init_runFormatting();
|
|
11437
|
+
init_hyperliquidWhaleBackfill();
|
|
10998
11438
|
shouldPrepareDerivativesContextForRun = (params) => {
|
|
10999
11439
|
if (params.universe === "tradfi") return false;
|
|
11000
11440
|
if (params.mode === "signals") {
|
|
@@ -11044,6 +11484,20 @@ var init_marketContextPrepare = __esm({
|
|
|
11044
11484
|
cacheOnly: params.cacheOnly
|
|
11045
11485
|
});
|
|
11046
11486
|
};
|
|
11487
|
+
shouldPrepareHyperliquidWhaleContextForRun = (params) => {
|
|
11488
|
+
const strategyRequiresContext = params.strategyNames?.includes(
|
|
11489
|
+
"HyperliquidConsensus"
|
|
11490
|
+
);
|
|
11491
|
+
if (params.universe === "tradfi" || params.mode === "signals" || params.cacheOnly || !params.aiEnabled && !params.mlEnabled && !strategyRequiresContext) {
|
|
11492
|
+
return false;
|
|
11493
|
+
}
|
|
11494
|
+
const configured = String(
|
|
11495
|
+
process.env.HYPERLIQUID_WHALE_BACKFILL_ENABLED ?? ""
|
|
11496
|
+
).trim().toLowerCase();
|
|
11497
|
+
if (["0", "false", "no", "off"].includes(configured)) return false;
|
|
11498
|
+
if (strategyRequiresContext) return true;
|
|
11499
|
+
return ["1", "true", "yes", "on"].includes(configured);
|
|
11500
|
+
};
|
|
11047
11501
|
resolveCoinMarketCapBackfillForMode = (mode) => mode === "backtest" ? backfillCoinMarketCapContextForBacktest : mode === "signals" ? backfillCoinMarketCapContextForSignals : backfillCoinMarketCapContextForReplay;
|
|
11048
11502
|
buildCoinMarketCapBackfillParams = (params) => ({
|
|
11049
11503
|
userName: params.userName,
|
|
@@ -11080,6 +11534,18 @@ var init_marketContextPrepare = __esm({
|
|
|
11080
11534
|
)
|
|
11081
11535
|
);
|
|
11082
11536
|
}
|
|
11537
|
+
if (shouldPrepareHyperliquidWhaleContextForRun(params)) {
|
|
11538
|
+
await timeOperation4(
|
|
11539
|
+
"hyperliquid whale context backfill",
|
|
11540
|
+
() => backfillHyperliquidWhaleContext({
|
|
11541
|
+
startMs: params.preloadStartMs ?? params.startMs,
|
|
11542
|
+
endMs: params.endMs,
|
|
11543
|
+
cacheOnly: params.cacheOnly,
|
|
11544
|
+
strict: false,
|
|
11545
|
+
log
|
|
11546
|
+
})
|
|
11547
|
+
);
|
|
11548
|
+
}
|
|
11083
11549
|
if (shouldPrepareBinanceMarketContextForRun(params)) {
|
|
11084
11550
|
await timeOperation4("binance market context backfill", () => {
|
|
11085
11551
|
const backfill = params.mode === "backtest" ? backfillBinanceMarketContextForBacktest : params.mode === "signals" ? backfillBinanceMarketContextForSignals : backfillBinanceMarketContextForReplay;
|
|
@@ -11855,13 +12321,13 @@ var init_cliArgs = __esm({
|
|
|
11855
12321
|
});
|
|
11856
12322
|
|
|
11857
12323
|
// src/lib/backtest/cliConfig.ts
|
|
11858
|
-
var import_args5, import_os,
|
|
12324
|
+
var import_args5, import_os, import_types2, import_constants7, BYTES_IN_MB, MAX_PARALLEL, resolveDefaultWorkerHeapMb, resolveDefaultParallel, DEFAULT_WORKER_HEAP_MB, DEFAULT_PARALLEL, DEFAULT_BACKTEST_ENTRY_DELAY_BARS, resolveWorkerHeapMb, resolveEffectiveParallel, resolveRequestedTestsLimit, hasCliFlag, stripIgnoredFlags, normalizedArgv, flags5, interval, marketUniverse, resolveBacktestPriceMode, backtestPriceMode, resolveBacktestEntryDelayBars, backtestEntryDelayBars, progressStep, testsLimit, testsSkip, hasExplicitTestsLimit, isUpdateOnlyRun, isContinueRun, continueRunId, isFastMode, testItemTimeoutMs, workerHeapMb, effectiveParallel, resultArtifactsIoConcurrency, userName, projectRoot3;
|
|
11859
12325
|
var init_cliConfig = __esm({
|
|
11860
12326
|
"src/lib/backtest/cliConfig.ts"() {
|
|
11861
12327
|
"use strict";
|
|
11862
12328
|
import_args5 = __toESM(require("args"));
|
|
11863
12329
|
import_os = __toESM(require("os"));
|
|
11864
|
-
|
|
12330
|
+
import_types2 = require("@tradejs/types");
|
|
11865
12331
|
import_constants7 = require("@tradejs/core/constants");
|
|
11866
12332
|
init_cliArgs();
|
|
11867
12333
|
BYTES_IN_MB = 1024 * 1024;
|
|
@@ -11990,7 +12456,7 @@ var init_cliConfig = __esm({
|
|
|
11990
12456
|
interval = String(flags5.timeframe ?? 15);
|
|
11991
12457
|
marketUniverse = (() => {
|
|
11992
12458
|
const value = String(flags5.universe ?? "crypto");
|
|
11993
|
-
if (!(0,
|
|
12459
|
+
if (!(0, import_types2.isMarketUniverse)(value)) {
|
|
11994
12460
|
throw new Error(`Unknown market universe: ${value}`);
|
|
11995
12461
|
}
|
|
11996
12462
|
return value;
|
|
@@ -12039,15 +12505,15 @@ var init_cliConfig = __esm({
|
|
|
12039
12505
|
});
|
|
12040
12506
|
|
|
12041
12507
|
// src/lib/backtest/runState.ts
|
|
12042
|
-
var
|
|
12508
|
+
var import_types3, successTests, errorTests, warningCounts, errorMessages, topResults, bestTickerResults, configResultBuckets, progressStats, replayResultsByStrategyAndTicker, persistedTestSummaryByKey, runStartedAt, testsStartedAt, runtimeCompareConnector, runtimeCompareConnectorName, runtimeCompareWindow, getResultNetProfit, addResultToAggregate, addResultWarnings, getAggregateAverageProfit, getAggregateWinRate, incrementSuccessTests, incrementErrorTests, recordRunError, getRunStartedAt, getTestsStartedAt, markTestsStarted, getRunCounters, getTopResults, replaceTopResults, getBestTickerResults, getBestTickerResultForSymbol, setBestTickerResultForSymbol, recordResultAggregates, getProgressStats, getConfigResultBuckets, getTopConfigResultBuckets, getPersistedTestSummariesMap, setPersistedTestSummary, setRuntimeCompareContext, getRuntimeCompareContext, resetRunState;
|
|
12043
12509
|
var init_runState = __esm({
|
|
12044
12510
|
"src/lib/backtest/runState.ts"() {
|
|
12045
12511
|
"use strict";
|
|
12046
|
-
|
|
12512
|
+
import_types3 = require("@tradejs/types");
|
|
12047
12513
|
successTests = 0;
|
|
12048
12514
|
errorTests = 0;
|
|
12049
12515
|
warningCounts = {
|
|
12050
|
-
[
|
|
12516
|
+
[import_types3.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY]: 0
|
|
12051
12517
|
};
|
|
12052
12518
|
errorMessages = [];
|
|
12053
12519
|
topResults = [];
|
|
@@ -12096,7 +12562,7 @@ var init_runState = __esm({
|
|
|
12096
12562
|
}
|
|
12097
12563
|
};
|
|
12098
12564
|
addResultWarnings = (result) => {
|
|
12099
|
-
for (const code of Object.values(
|
|
12565
|
+
for (const code of Object.values(import_types3.BACKTEST_WARNING_CODES)) {
|
|
12100
12566
|
const count = Number(result.warningCounts?.[code] ?? 0);
|
|
12101
12567
|
if (Number.isFinite(count) && count > 0) {
|
|
12102
12568
|
warningCounts[code] = (warningCounts[code] ?? 0) + count;
|
|
@@ -12197,7 +12663,7 @@ var init_runState = __esm({
|
|
|
12197
12663
|
resetRunState = () => {
|
|
12198
12664
|
successTests = 0;
|
|
12199
12665
|
errorTests = 0;
|
|
12200
|
-
for (const code of Object.values(
|
|
12666
|
+
for (const code of Object.values(import_types3.BACKTEST_WARNING_CODES)) {
|
|
12201
12667
|
warningCounts[code] = 0;
|
|
12202
12668
|
}
|
|
12203
12669
|
errorMessages.length = 0;
|
|
@@ -12376,7 +12842,7 @@ var init_workerPool = __esm({
|
|
|
12376
12842
|
});
|
|
12377
12843
|
|
|
12378
12844
|
// src/lib/backtest/runnerCore.ts
|
|
12379
|
-
var import_fs, import_path4, import_chalk14, import_backtest, import_constants8, import_time3, import_backtestArtifacts, import_redis6, import_tradingAccounts,
|
|
12845
|
+
var import_fs, import_path4, import_chalk14, import_backtest, import_constants8, import_time3, import_backtestArtifacts, import_redis6, import_tradingAccounts, import_types4, formatWindowDays, getResultAmount, getResultNetProfit2, recordError, insertTopResult, updateTopResults, updateBestTickerResult, chunkTestSuiteBySymbol, mergePersistedTestSummaries, getLogsById, resolveResultArtifacts, setTestData, persistTestSummariesIndex, validateBacktestRuntimeDeployment, prepareRunEnvironment2, buildPreparedTestSuite, trackTopResult, buildRunIntroLines, resolveTesterWorker, executeTestSuite, printRunOutro;
|
|
12380
12846
|
var init_runnerCore = __esm({
|
|
12381
12847
|
"src/lib/backtest/runnerCore.ts"() {
|
|
12382
12848
|
"use strict";
|
|
@@ -12389,7 +12855,7 @@ var init_runnerCore = __esm({
|
|
|
12389
12855
|
import_backtestArtifacts = require("@tradejs/infra/backtestArtifacts");
|
|
12390
12856
|
import_redis6 = require("@tradejs/infra/redis");
|
|
12391
12857
|
import_tradingAccounts = require("@tradejs/infra/tradingAccounts");
|
|
12392
|
-
|
|
12858
|
+
import_types4 = require("@tradejs/types");
|
|
12393
12859
|
init_runFormatting();
|
|
12394
12860
|
init_marketContextPrepare();
|
|
12395
12861
|
init_runEnvironment();
|
|
@@ -12694,6 +13160,9 @@ var init_runnerCore = __esm({
|
|
|
12694
13160
|
cacheOnly: Boolean(flags5.cacheOnly),
|
|
12695
13161
|
aiEnabled,
|
|
12696
13162
|
mlEnabled,
|
|
13163
|
+
strategyNames: Array.from(
|
|
13164
|
+
new Set(preparedSuite.map((test) => test.strategyName))
|
|
13165
|
+
),
|
|
12697
13166
|
log: (message) => console.log(import_chalk14.default.gray(message))
|
|
12698
13167
|
});
|
|
12699
13168
|
return preparedSuite;
|
|
@@ -12829,10 +13298,10 @@ var init_runnerCore = __esm({
|
|
|
12829
13298
|
const { successTests: successTests2, errorTests: errorTests2, warningCounts: warningCounts2 } = getRunCounters();
|
|
12830
13299
|
console.log(`${import_chalk14.default.green("SUCCESS TESTS")}: ${successTests2}`);
|
|
12831
13300
|
console.log(`${import_chalk14.default.red("ERRORS")}: ${errorTests2}`);
|
|
12832
|
-
const takeProfitCrossedWarnings = warningCounts2[
|
|
13301
|
+
const takeProfitCrossedWarnings = warningCounts2[import_types4.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY] ?? 0;
|
|
12833
13302
|
console.log(
|
|
12834
13303
|
import_chalk14.default.yellow(
|
|
12835
|
-
`WARNINGS (${
|
|
13304
|
+
`WARNINGS (${import_types4.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY}): ${takeProfitCrossedWarnings}`
|
|
12836
13305
|
)
|
|
12837
13306
|
);
|
|
12838
13307
|
console.log("");
|
|
@@ -13258,6 +13727,9 @@ var init_backtest = __esm({
|
|
|
13258
13727
|
cacheOnly: manifest.flags.cacheOnly,
|
|
13259
13728
|
aiEnabled: manifest.flags.ai,
|
|
13260
13729
|
mlEnabled: manifest.flags.ml,
|
|
13730
|
+
strategyNames: Array.from(
|
|
13731
|
+
new Set(remainingSuite.map((test) => test.strategyName))
|
|
13732
|
+
),
|
|
13261
13733
|
log: (message) => console.log(import_chalk15.default.gray(message))
|
|
13262
13734
|
});
|
|
13263
13735
|
};
|
|
@@ -13402,7 +13874,7 @@ __export(binanceBreadthUniversesUpdate_exports, {
|
|
|
13402
13874
|
BINANCE_BREADTH_UNIVERSES_RELATIVE_PATH: () => BINANCE_BREADTH_UNIVERSES_RELATIVE_PATH,
|
|
13403
13875
|
main: () => main6
|
|
13404
13876
|
});
|
|
13405
|
-
var import_config, import_args6, import_promises7, import_node_path4, import_chalk16, import_connectors6, import_connectors7,
|
|
13877
|
+
var import_config, import_args6, import_promises7, import_node_path4, import_chalk16, import_connectors6, import_connectors7, import_strategies5, flags6, BINANCE_BREADTH_UNIVERSES_RELATIVE_PATH, main6;
|
|
13406
13878
|
var init_binanceBreadthUniversesUpdate = __esm({
|
|
13407
13879
|
"src/scripts/binanceBreadthUniversesUpdate.ts"() {
|
|
13408
13880
|
"use strict";
|
|
@@ -13413,7 +13885,7 @@ var init_binanceBreadthUniversesUpdate = __esm({
|
|
|
13413
13885
|
import_chalk16 = __toESM(require("chalk"));
|
|
13414
13886
|
import_connectors6 = require("@tradejs/connectors");
|
|
13415
13887
|
import_connectors7 = require("@tradejs/node/connectors");
|
|
13416
|
-
|
|
13888
|
+
import_strategies5 = require("@tradejs/node/strategies");
|
|
13417
13889
|
init_binanceMarketData();
|
|
13418
13890
|
import_args6.default.example(
|
|
13419
13891
|
"yarn breadth:universes:update --write",
|
|
@@ -13444,7 +13916,7 @@ var init_binanceBreadthUniversesUpdate = __esm({
|
|
|
13444
13916
|
await connector.getTickers(),
|
|
13445
13917
|
100
|
|
13446
13918
|
);
|
|
13447
|
-
const snapshot = (0,
|
|
13919
|
+
const snapshot = (0, import_strategies5.buildBinanceBreadthUniverseSnapshot)({ rankedSymbols });
|
|
13448
13920
|
const serialized = `${JSON.stringify(snapshot, null, 2)}
|
|
13449
13921
|
`;
|
|
13450
13922
|
console.log(
|
|
@@ -13479,7 +13951,7 @@ var binanceMarketIngest_exports = {};
|
|
|
13479
13951
|
__export(binanceMarketIngest_exports, {
|
|
13480
13952
|
main: () => main7
|
|
13481
13953
|
});
|
|
13482
|
-
var import_config2, import_args7, import_chalk17, import_connectors8,
|
|
13954
|
+
var import_config2, import_args7, import_chalk17, import_connectors8, import_strategies6, import_timescale5, flags7, asFloat2, asInt4, sleep4, intervalToConnectorInterval, fetchAggTradesForWindow2, printEstimate, main7;
|
|
13483
13955
|
var init_binanceMarketIngest = __esm({
|
|
13484
13956
|
"src/scripts/binanceMarketIngest.ts"() {
|
|
13485
13957
|
"use strict";
|
|
@@ -13487,8 +13959,8 @@ var init_binanceMarketIngest = __esm({
|
|
|
13487
13959
|
import_args7 = __toESM(require("args"));
|
|
13488
13960
|
import_chalk17 = __toESM(require("chalk"));
|
|
13489
13961
|
import_connectors8 = require("@tradejs/connectors");
|
|
13490
|
-
|
|
13491
|
-
|
|
13962
|
+
import_strategies6 = require("@tradejs/node/strategies");
|
|
13963
|
+
import_timescale5 = require("@tradejs/infra/timescale");
|
|
13492
13964
|
init_binanceMarketData();
|
|
13493
13965
|
import_args7.default.example(
|
|
13494
13966
|
"yarn cli:node8g binance:market-ingest --all --symbols BTCUSDT,ETHUSDT --days 0.05 --interval 1m --write",
|
|
@@ -13531,7 +14003,7 @@ var init_binanceMarketIngest = __esm({
|
|
|
13531
14003
|
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
13532
14004
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
13533
14005
|
};
|
|
13534
|
-
|
|
14006
|
+
sleep4 = (ms) => ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve();
|
|
13535
14007
|
intervalToConnectorInterval = (interval5) => {
|
|
13536
14008
|
if (interval5 === "1m") return "1";
|
|
13537
14009
|
if (interval5 === "5m") return "5";
|
|
@@ -13567,10 +14039,10 @@ var init_binanceMarketIngest = __esm({
|
|
|
13567
14039
|
const nextCursor = Math.max(pageCursor + 1, Number(lastTs) + 1);
|
|
13568
14040
|
if (nextCursor > endTime) break;
|
|
13569
14041
|
pageCursor = nextCursor;
|
|
13570
|
-
await
|
|
14042
|
+
await sleep4(requestDelayMs);
|
|
13571
14043
|
}
|
|
13572
14044
|
cursor = endTime + 1;
|
|
13573
|
-
await
|
|
14045
|
+
await sleep4(requestDelayMs);
|
|
13574
14046
|
}
|
|
13575
14047
|
return rows;
|
|
13576
14048
|
};
|
|
@@ -13591,7 +14063,7 @@ var init_binanceMarketIngest = __esm({
|
|
|
13591
14063
|
const interval5 = normalizeMarketFeatureInterval(flags7.interval);
|
|
13592
14064
|
const hours = flags7.hours == null ? null : asFloat2(flags7.hours, 0);
|
|
13593
14065
|
const days = hours != null && hours > 0 ? hours / 24 : asFloat2(flags7.days, 1);
|
|
13594
|
-
const breadthUniverses = (0,
|
|
14066
|
+
const breadthUniverses = (0, import_strategies6.getBinanceBreadthUniverses)();
|
|
13595
14067
|
const batchMinutes = asInt4(flags7.batchMinutes, 15);
|
|
13596
14068
|
const requestDelayMs = asInt4(flags7.requestDelayMs, 75);
|
|
13597
14069
|
const includeAll = Boolean(flags7.all);
|
|
@@ -13616,7 +14088,7 @@ var init_binanceMarketIngest = __esm({
|
|
|
13616
14088
|
console.log(import_chalk17.default.yellow("Dry run only. Pass --write to ingest."));
|
|
13617
14089
|
return;
|
|
13618
14090
|
}
|
|
13619
|
-
await (0,
|
|
14091
|
+
await (0, import_timescale5.waitForDbReady)();
|
|
13620
14092
|
const connector = await import_connectors8.connectors[import_connectors8.ConnectorNames.Binance]({
|
|
13621
14093
|
userName: "root"
|
|
13622
14094
|
});
|
|
@@ -13638,7 +14110,7 @@ var init_binanceMarketIngest = __esm({
|
|
|
13638
14110
|
requestDelayMs
|
|
13639
14111
|
});
|
|
13640
14112
|
const rows = aggregateAggTradesToRows({ symbol, interval: interval5, trades });
|
|
13641
|
-
await (0,
|
|
14113
|
+
await (0, import_timescale5.upsertMarketTradeFlowRows)(rows);
|
|
13642
14114
|
aggTradesRaw += trades.length;
|
|
13643
14115
|
tradeFlowRows += rows.length;
|
|
13644
14116
|
process.stdout.write(
|
|
@@ -13685,7 +14157,7 @@ var init_binanceMarketIngest = __esm({
|
|
|
13685
14157
|
candlesBySymbol: universeCandles,
|
|
13686
14158
|
btcCandles
|
|
13687
14159
|
});
|
|
13688
|
-
await (0,
|
|
14160
|
+
await (0, import_timescale5.upsertMarketBreadthRows)(rows);
|
|
13689
14161
|
breadthRows += rows.length;
|
|
13690
14162
|
}
|
|
13691
14163
|
}
|
|
@@ -13702,14 +14174,14 @@ var init_binanceMarketIngest = __esm({
|
|
|
13702
14174
|
});
|
|
13703
14175
|
|
|
13704
14176
|
// src/lib/runBot.ts
|
|
13705
|
-
var import_lodash, import_connectors9, import_data2,
|
|
14177
|
+
var import_lodash, import_connectors9, import_data2, import_strategies7, import_async3, import_constants10, import_time4, import_logger, import_redis8, projectRoot4, runBot;
|
|
13706
14178
|
var init_runBot = __esm({
|
|
13707
14179
|
"src/lib/runBot.ts"() {
|
|
13708
14180
|
"use strict";
|
|
13709
14181
|
import_lodash = __toESM(require("lodash"));
|
|
13710
14182
|
import_connectors9 = require("@tradejs/node/connectors");
|
|
13711
14183
|
import_data2 = require("@tradejs/core/data");
|
|
13712
|
-
|
|
14184
|
+
import_strategies7 = require("@tradejs/node/strategies");
|
|
13713
14185
|
import_async3 = require("@tradejs/core/async");
|
|
13714
14186
|
import_constants10 = require("@tradejs/core/constants");
|
|
13715
14187
|
import_time4 = require("@tradejs/core/time");
|
|
@@ -13741,7 +14213,7 @@ var init_runBot = __esm({
|
|
|
13741
14213
|
continue;
|
|
13742
14214
|
}
|
|
13743
14215
|
try {
|
|
13744
|
-
const strategyCreator = await (0,
|
|
14216
|
+
const strategyCreator = await (0, import_strategies7.getStrategyCreator)(
|
|
13745
14217
|
strategyName,
|
|
13746
14218
|
projectRoot4
|
|
13747
14219
|
);
|
|
@@ -14605,7 +15077,7 @@ var require_binaryParsers = __commonJS({
|
|
|
14605
15077
|
};
|
|
14606
15078
|
var parseArray = function(value) {
|
|
14607
15079
|
var dim = parseBits(value, 32);
|
|
14608
|
-
var
|
|
15080
|
+
var flags31 = parseBits(value, 32, 32);
|
|
14609
15081
|
var elementType = parseBits(value, 32, 64);
|
|
14610
15082
|
var offset = 96;
|
|
14611
15083
|
var dims = [];
|
|
@@ -17303,8 +17775,8 @@ var require_helper = __commonJS({
|
|
|
17303
17775
|
function warn() {
|
|
17304
17776
|
var isWritable = warnStream instanceof Stream && true === warnStream.writable;
|
|
17305
17777
|
if (isWritable) {
|
|
17306
|
-
var
|
|
17307
|
-
warnStream.write(util.format.apply(util,
|
|
17778
|
+
var args33 = Array.prototype.slice.call(arguments).concat("\n");
|
|
17779
|
+
warnStream.write(util.format.apply(util, args33));
|
|
17308
17780
|
}
|
|
17309
17781
|
}
|
|
17310
17782
|
Object.defineProperty(module2.exports, "isWin", {
|
|
@@ -19521,14 +19993,14 @@ var init_cleanTests = __esm({
|
|
|
19521
19993
|
});
|
|
19522
19994
|
|
|
19523
19995
|
// src/lib/continuity.ts
|
|
19524
|
-
var
|
|
19996
|
+
var import_types5, parseContinuityUniverse, resolveContinuityUniverses, findRepairableContinuityGap;
|
|
19525
19997
|
var init_continuity = __esm({
|
|
19526
19998
|
"src/lib/continuity.ts"() {
|
|
19527
19999
|
"use strict";
|
|
19528
|
-
|
|
20000
|
+
import_types5 = require("@tradejs/types");
|
|
19529
20001
|
parseContinuityUniverse = (value) => {
|
|
19530
20002
|
const normalized = String(value ?? "all").trim().toLowerCase();
|
|
19531
|
-
if (normalized === "all" || (0,
|
|
20003
|
+
if (normalized === "all" || (0, import_types5.isMarketUniverse)(normalized)) {
|
|
19532
20004
|
return normalized;
|
|
19533
20005
|
}
|
|
19534
20006
|
throw new Error(
|
|
@@ -19560,7 +20032,7 @@ var continuity_exports = {};
|
|
|
19560
20032
|
__export(continuity_exports, {
|
|
19561
20033
|
main: () => main13
|
|
19562
20034
|
});
|
|
19563
|
-
var import_args12, import_progress7, import_chalk20, import_connectors10, import_cli8, import_constants11, import_time5, import_logger2,
|
|
20035
|
+
var import_args12, import_progress7, import_chalk20, import_connectors10, import_cli8, import_constants11, import_time5, import_logger2, import_timescale6, flags12, interval2, intervalKey, projectRoot5, parseProviders, runContinuity, main13;
|
|
19564
20036
|
var init_continuity2 = __esm({
|
|
19565
20037
|
"src/scripts/continuity.ts"() {
|
|
19566
20038
|
"use strict";
|
|
@@ -19572,7 +20044,7 @@ var init_continuity2 = __esm({
|
|
|
19572
20044
|
import_constants11 = require("@tradejs/core/constants");
|
|
19573
20045
|
import_time5 = require("@tradejs/core/time");
|
|
19574
20046
|
import_logger2 = require("@tradejs/infra/logger");
|
|
19575
|
-
|
|
20047
|
+
import_timescale6 = require("@tradejs/infra/timescale");
|
|
19576
20048
|
init_continuity();
|
|
19577
20049
|
import_args12.default.option(["t", "tickers"], "Selected tickers");
|
|
19578
20050
|
import_args12.default.option(["f", "timeframe"], "Timeframe", 15);
|
|
@@ -19656,7 +20128,7 @@ var init_continuity2 = __esm({
|
|
|
19656
20128
|
(0, import_time5.formatUnix)(gap.ts),
|
|
19657
20129
|
gap.diffSeconds
|
|
19658
20130
|
);
|
|
19659
|
-
await (0,
|
|
20131
|
+
await (0, import_timescale6.deleteCandles)(provider, symbol, interval2);
|
|
19660
20132
|
const reloaded = await connector.kline({
|
|
19661
20133
|
symbol,
|
|
19662
20134
|
interval: intervalKey,
|
|
@@ -19686,7 +20158,7 @@ var init_continuity2 = __esm({
|
|
|
19686
20158
|
import_logger2.logger.error("Invalid timeframe: %s", flags12.timeframe);
|
|
19687
20159
|
process.exit(1);
|
|
19688
20160
|
}
|
|
19689
|
-
await (0,
|
|
20161
|
+
await (0, import_timescale6.waitForDbReady)();
|
|
19690
20162
|
const reloadStart = (0, import_time5.getTimestamp)(import_constants11.PRELOAD_DAYS);
|
|
19691
20163
|
const reloadEnd = (0, import_time5.getTimestamp)();
|
|
19692
20164
|
let requestedUniverse;
|
|
@@ -19774,7 +20246,7 @@ var derivativesIngest_exports = {};
|
|
|
19774
20246
|
__export(derivativesIngest_exports, {
|
|
19775
20247
|
main: () => main14
|
|
19776
20248
|
});
|
|
19777
|
-
var import_config4, import_args13, import_chalk21, import_indicators2,
|
|
20249
|
+
var import_config4, import_args13, import_chalk21, import_indicators2, import_timescale7, import_userSettings4, import_connectors11, flags13, asInt5, main14;
|
|
19778
20250
|
var init_derivativesIngest = __esm({
|
|
19779
20251
|
"src/scripts/derivativesIngest.ts"() {
|
|
19780
20252
|
"use strict";
|
|
@@ -19782,7 +20254,7 @@ var init_derivativesIngest = __esm({
|
|
|
19782
20254
|
import_args13 = __toESM(require("args"));
|
|
19783
20255
|
import_chalk21 = __toESM(require("chalk"));
|
|
19784
20256
|
import_indicators2 = require("@tradejs/core/indicators");
|
|
19785
|
-
|
|
20257
|
+
import_timescale7 = require("@tradejs/infra/timescale");
|
|
19786
20258
|
import_userSettings4 = require("@tradejs/infra/userSettings");
|
|
19787
20259
|
import_connectors11 = require("@tradejs/connectors");
|
|
19788
20260
|
import_args13.default.example(
|
|
@@ -19820,7 +20292,7 @@ var init_derivativesIngest = __esm({
|
|
|
19820
20292
|
const coinalyzeApiKey = providerName === "coinalyze" ? (await (0, import_userSettings4.getUserSettings)(userName2)).COINALYZE_API_KEY : void 0;
|
|
19821
20293
|
if (!symbols.length) throw new Error("No symbols provided");
|
|
19822
20294
|
if (!intervals.length) throw new Error("No intervals provided");
|
|
19823
|
-
await (0,
|
|
20295
|
+
await (0, import_timescale7.waitForDbReady)();
|
|
19824
20296
|
const now2 = Date.now();
|
|
19825
20297
|
const fromMs = now2 - days * 24 * 60 * 60 * 1e3;
|
|
19826
20298
|
let totalDerivativesRows = 0;
|
|
@@ -19849,12 +20321,12 @@ var init_derivativesIngest = __esm({
|
|
|
19849
20321
|
});
|
|
19850
20322
|
const derivativesRows = window2.derivativesRows ?? [];
|
|
19851
20323
|
if (derivativesRows.length) {
|
|
19852
|
-
await (0,
|
|
20324
|
+
await (0, import_timescale7.upsertDerivatives)(derivativesRows);
|
|
19853
20325
|
totalDerivativesRows += derivativesRows.length;
|
|
19854
20326
|
}
|
|
19855
20327
|
const spreadRows = window2.spreadRows ?? [];
|
|
19856
20328
|
if (spreadRows.length) {
|
|
19857
|
-
await (0,
|
|
20329
|
+
await (0, import_timescale7.upsertSpreadRows)(spreadRows);
|
|
19858
20330
|
totalSpreadRows += spreadRows.length;
|
|
19859
20331
|
}
|
|
19860
20332
|
cursor = toMs + 1;
|
|
@@ -19876,7 +20348,7 @@ var derivativesIngestCoinalyzeAll_exports = {};
|
|
|
19876
20348
|
__export(derivativesIngestCoinalyzeAll_exports, {
|
|
19877
20349
|
main: () => main15
|
|
19878
20350
|
});
|
|
19879
|
-
var import_config5, import_args14, import_chalk22, import_progress8, import_lodash2, import_connectors12, import_indicators3, import_async4, import_userSettings5, import_cli9,
|
|
20351
|
+
var import_config5, import_args14, import_chalk22, import_progress8, import_lodash2, import_connectors12, import_indicators3, import_async4, import_userSettings5, import_cli9, import_timescale8, coinalyzeIntervalMap2, flags14, asInt6, parseList2, coinalyzeBaseUrl, coinalyzeMaxRetries, lastRequestTs3, fetchJsonWithRateLimit2, fetchCoinalyzeMarkets2, selectBestMarket2, buildMatches2, normalizeMetricPoint2, toSeriesMap2, fetchMetricBatch2, main15;
|
|
19880
20352
|
var init_derivativesIngestCoinalyzeAll = __esm({
|
|
19881
20353
|
"src/scripts/derivativesIngestCoinalyzeAll.ts"() {
|
|
19882
20354
|
"use strict";
|
|
@@ -19890,7 +20362,7 @@ var init_derivativesIngestCoinalyzeAll = __esm({
|
|
|
19890
20362
|
import_async4 = require("@tradejs/core/async");
|
|
19891
20363
|
import_userSettings5 = require("@tradejs/infra/userSettings");
|
|
19892
20364
|
import_cli9 = require("@tradejs/node/cli");
|
|
19893
|
-
|
|
20365
|
+
import_timescale8 = require("@tradejs/infra/timescale");
|
|
19894
20366
|
coinalyzeIntervalMap2 = {
|
|
19895
20367
|
"15m": "15min",
|
|
19896
20368
|
"1h": "1hour"
|
|
@@ -20154,7 +20626,7 @@ var init_derivativesIngestCoinalyzeAll = __esm({
|
|
|
20154
20626
|
);
|
|
20155
20627
|
return;
|
|
20156
20628
|
}
|
|
20157
|
-
await (0,
|
|
20629
|
+
await (0, import_timescale8.waitForDbReady)();
|
|
20158
20630
|
const now2 = Date.now();
|
|
20159
20631
|
const fromMs = now2 - days * 24 * 60 * 60 * 1e3;
|
|
20160
20632
|
const symbolBatches = import_lodash2.default.chunk(matches, symbolBatchSize);
|
|
@@ -20222,7 +20694,7 @@ var init_derivativesIngestCoinalyzeAll = __esm({
|
|
|
20222
20694
|
return (0, import_indicators3.coinalyzePointsToRows)(points, interval5, "coinalyze");
|
|
20223
20695
|
});
|
|
20224
20696
|
if (rows.length) {
|
|
20225
|
-
await (0,
|
|
20697
|
+
await (0, import_timescale8.upsertDerivatives)(rows);
|
|
20226
20698
|
totalRows += rows.length;
|
|
20227
20699
|
}
|
|
20228
20700
|
const confirmedLiquidationWindow = (0, import_indicators3.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
@@ -20232,7 +20704,7 @@ var init_derivativesIngestCoinalyzeAll = __esm({
|
|
|
20232
20704
|
nowMs: now2
|
|
20233
20705
|
});
|
|
20234
20706
|
if (confirmedLiquidationWindow) {
|
|
20235
|
-
await (0,
|
|
20707
|
+
await (0, import_timescale8.applyDerivativesMetricCoverage)(
|
|
20236
20708
|
batch.map((item) => {
|
|
20237
20709
|
const marketSymbol = item.marketSymbol.toUpperCase();
|
|
20238
20710
|
const eventRowsCount = (liqMap.get(marketSymbol) ?? []).filter(
|
|
@@ -20466,7 +20938,7 @@ var init_doctor = __esm({
|
|
|
20466
20938
|
});
|
|
20467
20939
|
|
|
20468
20940
|
// src/lib/executionCalibration.ts
|
|
20469
|
-
var import_constants12, import_data3, import_trade, roundMetric, asRecord2, asArray,
|
|
20941
|
+
var import_constants12, import_data3, import_trade, roundMetric, asRecord2, asArray, finiteNumber2, finiteString, finiteDirection, metricValues, quantile, summarizeNumbers, getIntervalMs, getSignalKey, addSignalToMap, getRuntimePayload, extractRuntimeTradeRows, normalizeReplayMatch, extractReplayMatches, buildReplayMatchMaps, getSignalCandles, adverseEntryBps, feeBps, latencyDelta, resolveTelemetryQuality, finiteTelemetryQuality, countBy, toLatencyBucket, toSpreadBucket, buildSample, buildReplayOnlySample, hasTelemetry, hasFullTelemetry, buildGroupSummary, groupSamples, positiveMetricValues, buildRecommendation, buildExecutionCalibrationReport;
|
|
20470
20942
|
var init_executionCalibration = __esm({
|
|
20471
20943
|
"src/lib/executionCalibration.ts"() {
|
|
20472
20944
|
"use strict";
|
|
@@ -20476,7 +20948,7 @@ var init_executionCalibration = __esm({
|
|
|
20476
20948
|
roundMetric = (value, decimals = 6) => typeof value === "number" && Number.isFinite(value) ? Number(value.toFixed(decimals)) : null;
|
|
20477
20949
|
asRecord2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
20478
20950
|
asArray = (value) => Array.isArray(value) ? value : [];
|
|
20479
|
-
|
|
20951
|
+
finiteNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
20480
20952
|
finiteString = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
|
|
20481
20953
|
finiteDirection = (value) => value === "LONG" || value === "SHORT" ? value : null;
|
|
20482
20954
|
metricValues = (samples, key) => samples.map((sample) => sample[key]).filter(
|
|
@@ -20552,7 +21024,7 @@ var init_executionCalibration = __esm({
|
|
|
20552
21024
|
const shapeKey = getSignalKey({
|
|
20553
21025
|
strategy: finiteString(signal.strategy),
|
|
20554
21026
|
symbol: finiteString(signal.symbol),
|
|
20555
|
-
timestamp:
|
|
21027
|
+
timestamp: finiteNumber2(signal.timestamp)
|
|
20556
21028
|
});
|
|
20557
21029
|
if (shapeKey) {
|
|
20558
21030
|
signalsByShape.set(shapeKey, signal);
|
|
@@ -20594,7 +21066,7 @@ var init_executionCalibration = __esm({
|
|
|
20594
21066
|
const shapeKey = getSignalKey({
|
|
20595
21067
|
strategy: finiteString(trade.strategy),
|
|
20596
21068
|
symbol: finiteString(trade.symbol),
|
|
20597
|
-
timestamp:
|
|
21069
|
+
timestamp: finiteNumber2(trade.signalTimestamp ?? trade.entryTimestamp)
|
|
20598
21070
|
});
|
|
20599
21071
|
const signal = asRecord2(redisValues?.signal) ?? (signalId ? signalsById.get(signalId) ?? null : null) ?? (shapeKey ? signalsByShape.get(shapeKey) ?? null : null);
|
|
20600
21072
|
return {
|
|
@@ -20623,8 +21095,8 @@ var init_executionCalibration = __esm({
|
|
|
20623
21095
|
direction: finiteDirection(
|
|
20624
21096
|
item.direction ?? backtest2?.direction ?? runtime?.direction
|
|
20625
21097
|
),
|
|
20626
|
-
backtestPrice:
|
|
20627
|
-
runtimePrice:
|
|
21098
|
+
backtestPrice: finiteNumber2(item.backtestPrice ?? backtest2?.price),
|
|
21099
|
+
runtimePrice: finiteNumber2(item.runtimePrice ?? runtime?.price)
|
|
20628
21100
|
};
|
|
20629
21101
|
};
|
|
20630
21102
|
extractReplayMatches = (replayEvidenceArtifact) => {
|
|
@@ -20773,24 +21245,24 @@ var init_executionCalibration = __esm({
|
|
|
20773
21245
|
const direction = finiteDirection(trade.direction ?? signal?.direction);
|
|
20774
21246
|
const orderId = finiteString(trade.orderId);
|
|
20775
21247
|
const signalId = finiteString(trade.signalId ?? signal?.signalId);
|
|
20776
|
-
const qty =
|
|
20777
|
-
const signalTimestamp =
|
|
21248
|
+
const qty = finiteNumber2(trade.qty);
|
|
21249
|
+
const signalTimestamp = finiteNumber2(
|
|
20778
21250
|
trade.signalTimestamp ?? signal?.timestamp
|
|
20779
21251
|
);
|
|
20780
21252
|
const intervalMs2 = getIntervalMs(interval5);
|
|
20781
21253
|
const signalCloseTimestamp = signalTimestamp != null && intervalMs2 != null ? signalTimestamp + intervalMs2 : signalTimestamp;
|
|
20782
|
-
const signalClosePrice =
|
|
20783
|
-
const arrivalSnapshotTime =
|
|
21254
|
+
const signalClosePrice = finiteNumber2(trade.signalClosePrice);
|
|
21255
|
+
const arrivalSnapshotTime = finiteNumber2(trade.arrivalSnapshotTime);
|
|
20784
21256
|
const arrivalSource = finiteString(trade.arrivalSource);
|
|
20785
|
-
const arrivalMid =
|
|
20786
|
-
const bid =
|
|
20787
|
-
const ask =
|
|
20788
|
-
const spreadBps =
|
|
20789
|
-
const orderSubmitTime =
|
|
20790
|
-
const orderAckTime =
|
|
20791
|
-
const fillAvgPrice =
|
|
21257
|
+
const arrivalMid = finiteNumber2(trade.arrivalMid);
|
|
21258
|
+
const bid = finiteNumber2(trade.bid);
|
|
21259
|
+
const ask = finiteNumber2(trade.ask);
|
|
21260
|
+
const spreadBps = finiteNumber2(trade.spreadBps);
|
|
21261
|
+
const orderSubmitTime = finiteNumber2(trade.orderSubmitTime);
|
|
21262
|
+
const orderAckTime = finiteNumber2(trade.orderAckTime);
|
|
21263
|
+
const fillAvgPrice = finiteNumber2(trade.fillAvgPrice);
|
|
20792
21264
|
const fillSource = finiteString(trade.fillSource);
|
|
20793
|
-
const fillTime =
|
|
21265
|
+
const fillTime = finiteNumber2(trade.fillTime);
|
|
20794
21266
|
const telemetryQuality = finiteTelemetryQuality(trade.telemetryQuality) ?? resolveTelemetryQuality({
|
|
20795
21267
|
signalClosePrice,
|
|
20796
21268
|
arrivalMid,
|
|
@@ -20799,7 +21271,7 @@ var init_executionCalibration = __esm({
|
|
|
20799
21271
|
fillAvgPrice,
|
|
20800
21272
|
fillTime
|
|
20801
21273
|
});
|
|
20802
|
-
const fee =
|
|
21274
|
+
const fee = finiteNumber2(trade.fee ?? trade.openFee);
|
|
20803
21275
|
const arrivalSnapshotAgeMs = latencyDelta(
|
|
20804
21276
|
orderSubmitTime,
|
|
20805
21277
|
arrivalSnapshotTime
|
|
@@ -21331,8 +21803,8 @@ volumes:
|
|
|
21331
21803
|
`${DEV_COMPOSE_FILE} not found in project root. Run "npx @tradejs/cli infra-init" first.`
|
|
21332
21804
|
);
|
|
21333
21805
|
};
|
|
21334
|
-
runDockerCompose = (composePath,
|
|
21335
|
-
const result = (0, import_node_child_process3.spawnSync)("docker", ["compose", "-f", composePath, ...
|
|
21806
|
+
runDockerCompose = (composePath, args33) => {
|
|
21807
|
+
const result = (0, import_node_child_process3.spawnSync)("docker", ["compose", "-f", composePath, ...args33], {
|
|
21336
21808
|
stdio: "inherit"
|
|
21337
21809
|
});
|
|
21338
21810
|
if (result.error) {
|
|
@@ -21416,14 +21888,14 @@ var cleanupMarketContext_exports = {};
|
|
|
21416
21888
|
__export(cleanupMarketContext_exports, {
|
|
21417
21889
|
main: () => main21
|
|
21418
21890
|
});
|
|
21419
|
-
var import_config7, import_args17, import_chalk29,
|
|
21891
|
+
var import_config7, import_args17, import_chalk29, import_timescale9, flags17, main21;
|
|
21420
21892
|
var init_cleanupMarketContext = __esm({
|
|
21421
21893
|
"src/scripts/cleanupMarketContext.ts"() {
|
|
21422
21894
|
"use strict";
|
|
21423
21895
|
import_config7 = require("dotenv/config");
|
|
21424
21896
|
import_args17 = __toESM(require("args"));
|
|
21425
21897
|
import_chalk29 = __toESM(require("chalk"));
|
|
21426
|
-
|
|
21898
|
+
import_timescale9 = require("@tradejs/infra/timescale");
|
|
21427
21899
|
import_args17.default.example(
|
|
21428
21900
|
"yarn cli maintenance:cleanup-market-context --apply",
|
|
21429
21901
|
"Remove deprecated snapshot-only market context data from Timescale"
|
|
@@ -21432,8 +21904,8 @@ var init_cleanupMarketContext = __esm({
|
|
|
21432
21904
|
flags17 = import_args17.default.parse(process.argv);
|
|
21433
21905
|
main21 = async () => {
|
|
21434
21906
|
const apply = Boolean(flags17.apply);
|
|
21435
|
-
await (0,
|
|
21436
|
-
const items = await (0,
|
|
21907
|
+
await (0, import_timescale9.waitForDbReady)();
|
|
21908
|
+
const items = await (0, import_timescale9.cleanupDeprecatedMarketContext)({ apply });
|
|
21437
21909
|
if (!items.length) {
|
|
21438
21910
|
console.log(import_chalk29.default.green("No deprecated market context data found."));
|
|
21439
21911
|
return;
|
|
@@ -21458,7 +21930,7 @@ var migration_exports = {};
|
|
|
21458
21930
|
__export(migration_exports, {
|
|
21459
21931
|
main: () => main22
|
|
21460
21932
|
});
|
|
21461
|
-
var import_progress9, import_chalk30, import_logger3, import_files,
|
|
21933
|
+
var import_progress9, import_chalk30, import_logger3, import_files, import_timescale10, DIR, re, BATCH, projectRoot7, migrateFile, main22;
|
|
21462
21934
|
var init_migration = __esm({
|
|
21463
21935
|
"src/scripts/migration.ts"() {
|
|
21464
21936
|
"use strict";
|
|
@@ -21466,7 +21938,7 @@ var init_migration = __esm({
|
|
|
21466
21938
|
import_chalk30 = __toESM(require("chalk"));
|
|
21467
21939
|
import_logger3 = require("@tradejs/infra/logger");
|
|
21468
21940
|
import_files = require("@tradejs/infra/files");
|
|
21469
|
-
|
|
21941
|
+
import_timescale10 = require("@tradejs/infra/timescale");
|
|
21470
21942
|
DIR = "data/history";
|
|
21471
21943
|
re = /^(.+?)_(\d+)\.json$/;
|
|
21472
21944
|
BATCH = 2e3;
|
|
@@ -21482,9 +21954,9 @@ var init_migration = __esm({
|
|
|
21482
21954
|
[],
|
|
21483
21955
|
projectRoot7
|
|
21484
21956
|
);
|
|
21485
|
-
const rows = (0,
|
|
21957
|
+
const rows = (0, import_timescale10.toRows)("bybit", symbol, interval5, data);
|
|
21486
21958
|
for (let i = 0; i < rows.length; i += BATCH) {
|
|
21487
|
-
await (0,
|
|
21959
|
+
await (0, import_timescale10.upsertCandles)(rows.slice(i, i + BATCH));
|
|
21488
21960
|
}
|
|
21489
21961
|
};
|
|
21490
21962
|
main22 = async () => {
|
|
@@ -21513,21 +21985,21 @@ var init_migration = __esm({
|
|
|
21513
21985
|
});
|
|
21514
21986
|
|
|
21515
21987
|
// src/lib/marketData/klineEvents.ts
|
|
21516
|
-
var import_ioredis2, import_redis10, MARKET_KLINE_CHANNEL,
|
|
21988
|
+
var import_ioredis2, import_redis10, MARKET_KLINE_CHANNEL, positiveInt2, redisOptions, buildMarketKlineTopic, publishMarketKlineEvent, createMarketKlineSubscriber, parseMarketKlineEvent;
|
|
21517
21989
|
var init_klineEvents = __esm({
|
|
21518
21990
|
"src/lib/marketData/klineEvents.ts"() {
|
|
21519
21991
|
"use strict";
|
|
21520
21992
|
import_ioredis2 = __toESM(require("ioredis"));
|
|
21521
21993
|
import_redis10 = require("@tradejs/infra/redis");
|
|
21522
21994
|
MARKET_KLINE_CHANNEL = "tradejs:market:kline";
|
|
21523
|
-
|
|
21995
|
+
positiveInt2 = (value, fallback) => {
|
|
21524
21996
|
const parsed = Number(value);
|
|
21525
21997
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
21526
21998
|
};
|
|
21527
21999
|
redisOptions = () => ({
|
|
21528
22000
|
host: process.env.REDIS_HOST || "127.0.0.1",
|
|
21529
|
-
port:
|
|
21530
|
-
connectTimeout:
|
|
22001
|
+
port: positiveInt2(process.env.REDIS_PORT, 6379),
|
|
22002
|
+
connectTimeout: positiveInt2(process.env.REDIS_CONNECT_TIMEOUT_MS, 3e3),
|
|
21531
22003
|
maxRetriesPerRequest: null,
|
|
21532
22004
|
retryStrategy: (attempt) => Math.min(attempt * 200, 2e3)
|
|
21533
22005
|
});
|
|
@@ -21718,45 +22190,524 @@ var init_marketWs = __esm({
|
|
|
21718
22190
|
}
|
|
21719
22191
|
});
|
|
21720
22192
|
|
|
22193
|
+
// src/scripts/hyperliquidWhaleBackfill.ts
|
|
22194
|
+
var hyperliquidWhaleBackfill_exports = {};
|
|
22195
|
+
__export(hyperliquidWhaleBackfill_exports, {
|
|
22196
|
+
main: () => main24
|
|
22197
|
+
});
|
|
22198
|
+
var import_args18, flags18, parseTime, main24;
|
|
22199
|
+
var init_hyperliquidWhaleBackfill2 = __esm({
|
|
22200
|
+
"src/scripts/hyperliquidWhaleBackfill.ts"() {
|
|
22201
|
+
"use strict";
|
|
22202
|
+
import_args18 = __toESM(require("args"));
|
|
22203
|
+
init_hyperliquidWhaleBackfill();
|
|
22204
|
+
import_args18.default.option(["d", "days"], "Historical lookback in days", 1);
|
|
22205
|
+
import_args18.default.option(["f", "from"], "Inclusive ISO timestamp");
|
|
22206
|
+
import_args18.default.option(["t", "to"], "Exclusive ISO timestamp");
|
|
22207
|
+
import_args18.default.option(
|
|
22208
|
+
["c", "cacheOnly"],
|
|
22209
|
+
"Do not call Hyperliquid when coverage is missing"
|
|
22210
|
+
);
|
|
22211
|
+
flags18 = import_args18.default.parse(process.argv);
|
|
22212
|
+
parseTime = (value, fallback) => {
|
|
22213
|
+
if (value == null || value === "") return fallback;
|
|
22214
|
+
const parsed = Date.parse(String(value));
|
|
22215
|
+
if (!Number.isFinite(parsed)) throw new Error(`Invalid timestamp: ${value}`);
|
|
22216
|
+
return parsed;
|
|
22217
|
+
};
|
|
22218
|
+
main24 = async () => {
|
|
22219
|
+
const toMs = parseTime(flags18.to, Date.now());
|
|
22220
|
+
const days = Number(flags18.days);
|
|
22221
|
+
const lookbackMs = (Number.isFinite(days) && days > 0 ? days : 1) * 864e5;
|
|
22222
|
+
const startMs = parseTime(flags18.from, toMs - lookbackMs);
|
|
22223
|
+
if (startMs >= toMs) throw new Error("--from must be before --to");
|
|
22224
|
+
const result = await backfillHyperliquidWhaleContext({
|
|
22225
|
+
startMs,
|
|
22226
|
+
endMs: toMs,
|
|
22227
|
+
cacheOnly: Boolean(flags18.cacheOnly),
|
|
22228
|
+
log: console.log
|
|
22229
|
+
});
|
|
22230
|
+
console.log(JSON.stringify(result, null, 2));
|
|
22231
|
+
};
|
|
22232
|
+
}
|
|
22233
|
+
});
|
|
22234
|
+
|
|
22235
|
+
// src/lib/hyperliquidWhaleStream.ts
|
|
22236
|
+
var import_ws2, import_timescale11, import_strategies8, DEFAULT_WS_URL, EVENT_BATCH_SIZE, FLUSH_INTERVAL_MS, FINALIZE_INTERVAL_MS, FINALIZE_LAG_MS, REBUILD_LOOKBACK_MS, RAW_EVENT_RETENTION_MS, sleep5, parseHyperliquidUserFillsMessage, runHyperliquidWhaleStream;
|
|
22237
|
+
var init_hyperliquidWhaleStream = __esm({
|
|
22238
|
+
"src/lib/hyperliquidWhaleStream.ts"() {
|
|
22239
|
+
"use strict";
|
|
22240
|
+
import_ws2 = require("ws");
|
|
22241
|
+
import_timescale11 = require("@tradejs/infra/timescale");
|
|
22242
|
+
import_strategies8 = require("@tradejs/node/strategies");
|
|
22243
|
+
init_hyperliquidWhaleData();
|
|
22244
|
+
DEFAULT_WS_URL = "wss://api.hyperliquid.xyz/ws";
|
|
22245
|
+
EVENT_BATCH_SIZE = 500;
|
|
22246
|
+
FLUSH_INTERVAL_MS = 2e3;
|
|
22247
|
+
FINALIZE_INTERVAL_MS = 15e3;
|
|
22248
|
+
FINALIZE_LAG_MS = 5e3;
|
|
22249
|
+
REBUILD_LOOKBACK_MS = 10 * 6e4;
|
|
22250
|
+
RAW_EVENT_RETENTION_MS = 48 * 60 * 6e4;
|
|
22251
|
+
sleep5 = (ms, signal) => new Promise((resolve) => {
|
|
22252
|
+
if (signal.aborted) return resolve();
|
|
22253
|
+
const timer = setTimeout(resolve, ms);
|
|
22254
|
+
signal.addEventListener(
|
|
22255
|
+
"abort",
|
|
22256
|
+
() => {
|
|
22257
|
+
clearTimeout(timer);
|
|
22258
|
+
resolve();
|
|
22259
|
+
},
|
|
22260
|
+
{ once: true }
|
|
22261
|
+
);
|
|
22262
|
+
});
|
|
22263
|
+
parseHyperliquidUserFillsMessage = (raw) => {
|
|
22264
|
+
try {
|
|
22265
|
+
const message = JSON.parse(raw);
|
|
22266
|
+
if (message.channel !== "userFills" || !message.data || typeof message.data !== "object" || Array.isArray(message.data)) {
|
|
22267
|
+
return null;
|
|
22268
|
+
}
|
|
22269
|
+
const data = message.data;
|
|
22270
|
+
if (typeof data.user !== "string" || !Array.isArray(data.fills)) {
|
|
22271
|
+
return null;
|
|
22272
|
+
}
|
|
22273
|
+
return {
|
|
22274
|
+
address: data.user,
|
|
22275
|
+
isSnapshot: data.isSnapshot === true,
|
|
22276
|
+
fills: data.fills
|
|
22277
|
+
};
|
|
22278
|
+
} catch {
|
|
22279
|
+
return null;
|
|
22280
|
+
}
|
|
22281
|
+
};
|
|
22282
|
+
runHyperliquidWhaleStream = async (params) => {
|
|
22283
|
+
const universe = (0, import_strategies8.getHyperliquidPerpUniverseSnapshot)();
|
|
22284
|
+
const whales = (0, import_strategies8.getHyperliquidWhaleRegistrySnapshot)();
|
|
22285
|
+
const trackedSymbols = new Set(universe.symbols);
|
|
22286
|
+
const identity = {
|
|
22287
|
+
universeFingerprint: universe.fingerprint,
|
|
22288
|
+
whaleRegistryFingerprint: whales.fingerprint
|
|
22289
|
+
};
|
|
22290
|
+
const buffer = [];
|
|
22291
|
+
let writes = Promise.resolve();
|
|
22292
|
+
let streamCoverageFromMs = null;
|
|
22293
|
+
let streamCoverageToMs = 0;
|
|
22294
|
+
const flush = () => {
|
|
22295
|
+
if (!buffer.length) return writes;
|
|
22296
|
+
const batch = buffer.splice(0, buffer.length);
|
|
22297
|
+
writes = writes.then(() => (0, import_timescale11.upsertHyperliquidWhaleTradeEvents)(batch));
|
|
22298
|
+
return writes;
|
|
22299
|
+
};
|
|
22300
|
+
const recordStreamCoverage = (toMs) => {
|
|
22301
|
+
if (streamCoverageFromMs == null) return writes;
|
|
22302
|
+
const fromMs = Math.max(streamCoverageFromMs, streamCoverageToMs);
|
|
22303
|
+
if (toMs <= fromMs) return writes;
|
|
22304
|
+
const rows = Array.from(
|
|
22305
|
+
{ length: Math.floor((toMs - fromMs) / HYPERLIQUID_WHALE_BUCKET_MS) },
|
|
22306
|
+
(_3, index) => ({
|
|
22307
|
+
ts: new Date(fromMs + index * HYPERLIQUID_WHALE_BUCKET_MS),
|
|
22308
|
+
coveredWhales: whales.addresses.length,
|
|
22309
|
+
expectedWhales: whales.addresses.length,
|
|
22310
|
+
coveragePct: 1,
|
|
22311
|
+
source: "hyperliquid_user_fills_ws",
|
|
22312
|
+
...identity
|
|
22313
|
+
})
|
|
22314
|
+
);
|
|
22315
|
+
streamCoverageToMs = toMs;
|
|
22316
|
+
writes = writes.then(() => (0, import_timescale11.upsertHyperliquidWhaleCoverageRows)(rows));
|
|
22317
|
+
return writes;
|
|
22318
|
+
};
|
|
22319
|
+
const finalize = () => {
|
|
22320
|
+
const toMs = Math.floor((Date.now() - FINALIZE_LAG_MS) / HYPERLIQUID_WHALE_BUCKET_MS) * HYPERLIQUID_WHALE_BUCKET_MS;
|
|
22321
|
+
const fromMs = Math.max(0, toMs - REBUILD_LOOKBACK_MS);
|
|
22322
|
+
recordStreamCoverage(toMs);
|
|
22323
|
+
writes = writes.then(
|
|
22324
|
+
() => (0, import_timescale11.rebuildHyperliquidWhaleFlowRows)({
|
|
22325
|
+
fromMs,
|
|
22326
|
+
toMs,
|
|
22327
|
+
deleteEventsBeforeMs: toMs - RAW_EVENT_RETENTION_MS,
|
|
22328
|
+
...identity
|
|
22329
|
+
}).then((buckets) => {
|
|
22330
|
+
params.log?.(`Hyperliquid whale-flow finalized buckets=${buckets}`);
|
|
22331
|
+
})
|
|
22332
|
+
);
|
|
22333
|
+
return writes;
|
|
22334
|
+
};
|
|
22335
|
+
const flushTimer = setInterval(() => void flush(), FLUSH_INTERVAL_MS);
|
|
22336
|
+
const finalizeTimer = setInterval(
|
|
22337
|
+
() => void flush().then(finalize),
|
|
22338
|
+
FINALIZE_INTERVAL_MS
|
|
22339
|
+
);
|
|
22340
|
+
let reconnectDelayMs = 1e3;
|
|
22341
|
+
try {
|
|
22342
|
+
while (!params.signal.aborted) {
|
|
22343
|
+
await new Promise((resolve) => {
|
|
22344
|
+
const ws = new import_ws2.WebSocket(
|
|
22345
|
+
process.env.HYPERLIQUID_WS_URL?.trim() || DEFAULT_WS_URL
|
|
22346
|
+
);
|
|
22347
|
+
let settled = false;
|
|
22348
|
+
const finish = () => {
|
|
22349
|
+
if (settled) return;
|
|
22350
|
+
settled = true;
|
|
22351
|
+
resolve();
|
|
22352
|
+
};
|
|
22353
|
+
const pingTimer = setInterval(() => {
|
|
22354
|
+
if (ws.readyState === import_ws2.WebSocket.OPEN) {
|
|
22355
|
+
ws.send(JSON.stringify({ method: "ping" }));
|
|
22356
|
+
}
|
|
22357
|
+
}, 3e4);
|
|
22358
|
+
const close = () => {
|
|
22359
|
+
clearInterval(pingTimer);
|
|
22360
|
+
if (ws.readyState === import_ws2.WebSocket.OPEN) ws.close();
|
|
22361
|
+
else if (ws.readyState === import_ws2.WebSocket.CONNECTING) ws.terminate();
|
|
22362
|
+
finish();
|
|
22363
|
+
};
|
|
22364
|
+
params.signal.addEventListener("abort", close, { once: true });
|
|
22365
|
+
ws.on("open", () => {
|
|
22366
|
+
reconnectDelayMs = 1e3;
|
|
22367
|
+
streamCoverageFromMs = Math.ceil(Date.now() / HYPERLIQUID_WHALE_BUCKET_MS) * HYPERLIQUID_WHALE_BUCKET_MS;
|
|
22368
|
+
streamCoverageToMs = streamCoverageFromMs;
|
|
22369
|
+
for (const address of whales.addresses) {
|
|
22370
|
+
ws.send(
|
|
22371
|
+
JSON.stringify({
|
|
22372
|
+
method: "subscribe",
|
|
22373
|
+
subscription: { type: "userFills", user: address }
|
|
22374
|
+
})
|
|
22375
|
+
);
|
|
22376
|
+
}
|
|
22377
|
+
params.log?.(
|
|
22378
|
+
`Hyperliquid whale stream connected userFills subscriptions=${whales.addresses.length}`
|
|
22379
|
+
);
|
|
22380
|
+
});
|
|
22381
|
+
ws.on("message", (data) => {
|
|
22382
|
+
const message = parseHyperliquidUserFillsMessage(data.toString());
|
|
22383
|
+
if (!message) return;
|
|
22384
|
+
for (const fill of message.fills) {
|
|
22385
|
+
const event = normalizeHyperliquidUserFill({
|
|
22386
|
+
fill,
|
|
22387
|
+
address: message.address,
|
|
22388
|
+
trackedSymbols,
|
|
22389
|
+
identity
|
|
22390
|
+
});
|
|
22391
|
+
if (event) buffer.push(event);
|
|
22392
|
+
}
|
|
22393
|
+
if (buffer.length >= EVENT_BATCH_SIZE) void flush();
|
|
22394
|
+
});
|
|
22395
|
+
ws.on("error", (error) => {
|
|
22396
|
+
params.log?.(`Hyperliquid whale stream error: ${String(error)}`);
|
|
22397
|
+
close();
|
|
22398
|
+
});
|
|
22399
|
+
ws.on("close", () => {
|
|
22400
|
+
recordStreamCoverage(
|
|
22401
|
+
Math.floor(Date.now() / HYPERLIQUID_WHALE_BUCKET_MS) * HYPERLIQUID_WHALE_BUCKET_MS
|
|
22402
|
+
);
|
|
22403
|
+
streamCoverageFromMs = null;
|
|
22404
|
+
clearInterval(pingTimer);
|
|
22405
|
+
params.signal.removeEventListener("abort", close);
|
|
22406
|
+
finish();
|
|
22407
|
+
});
|
|
22408
|
+
});
|
|
22409
|
+
if (!params.signal.aborted) {
|
|
22410
|
+
await sleep5(reconnectDelayMs, params.signal);
|
|
22411
|
+
reconnectDelayMs = Math.min(3e4, reconnectDelayMs * 2);
|
|
22412
|
+
}
|
|
22413
|
+
}
|
|
22414
|
+
} finally {
|
|
22415
|
+
clearInterval(flushTimer);
|
|
22416
|
+
clearInterval(finalizeTimer);
|
|
22417
|
+
await flush();
|
|
22418
|
+
await finalize();
|
|
22419
|
+
}
|
|
22420
|
+
};
|
|
22421
|
+
}
|
|
22422
|
+
});
|
|
22423
|
+
|
|
22424
|
+
// src/scripts/hyperliquidWhaleIngest.ts
|
|
22425
|
+
var hyperliquidWhaleIngest_exports = {};
|
|
22426
|
+
__export(hyperliquidWhaleIngest_exports, {
|
|
22427
|
+
main: () => main25
|
|
22428
|
+
});
|
|
22429
|
+
var main25;
|
|
22430
|
+
var init_hyperliquidWhaleIngest = __esm({
|
|
22431
|
+
"src/scripts/hyperliquidWhaleIngest.ts"() {
|
|
22432
|
+
"use strict";
|
|
22433
|
+
init_hyperliquidWhaleStream();
|
|
22434
|
+
main25 = async () => {
|
|
22435
|
+
const controller = new AbortController();
|
|
22436
|
+
const stop = () => controller.abort();
|
|
22437
|
+
process.once("SIGINT", stop);
|
|
22438
|
+
process.once("SIGTERM", stop);
|
|
22439
|
+
try {
|
|
22440
|
+
await runHyperliquidWhaleStream({
|
|
22441
|
+
signal: controller.signal,
|
|
22442
|
+
log: console.log
|
|
22443
|
+
});
|
|
22444
|
+
} finally {
|
|
22445
|
+
process.removeListener("SIGINT", stop);
|
|
22446
|
+
process.removeListener("SIGTERM", stop);
|
|
22447
|
+
}
|
|
22448
|
+
};
|
|
22449
|
+
}
|
|
22450
|
+
});
|
|
22451
|
+
|
|
22452
|
+
// src/lib/hyperliquidWhaleSelection.ts
|
|
22453
|
+
var HYPERLIQUID_WHALE_SELECTION, finiteNumber3, median, evaluateHyperliquidWhaleStructure, rankHyperliquidStructuralWhales;
|
|
22454
|
+
var init_hyperliquidWhaleSelection = __esm({
|
|
22455
|
+
"src/lib/hyperliquidWhaleSelection.ts"() {
|
|
22456
|
+
"use strict";
|
|
22457
|
+
HYPERLIQUID_WHALE_SELECTION = {
|
|
22458
|
+
minimumAccountValueUsd: 5e5,
|
|
22459
|
+
minimumActiveDays: 1,
|
|
22460
|
+
maximumRawFillsPerDay: 20,
|
|
22461
|
+
maximumDirectionalExecutionsPerDay: 10,
|
|
22462
|
+
minimumMedianNotionalUsd: 0,
|
|
22463
|
+
minimumMedianInterExecutionMinutes: 0,
|
|
22464
|
+
minimumTop30NotionalShare: 1e-3,
|
|
22465
|
+
minimumTurnoverToEquity: 0.05,
|
|
22466
|
+
maximumTurnoverToEquity: 5
|
|
22467
|
+
};
|
|
22468
|
+
finiteNumber3 = (value) => {
|
|
22469
|
+
const parsed = Number(value);
|
|
22470
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
22471
|
+
};
|
|
22472
|
+
median = (values) => {
|
|
22473
|
+
if (!values.length) return 0;
|
|
22474
|
+
const sorted = [...values].sort((left, right) => left - right);
|
|
22475
|
+
const middle = Math.floor(sorted.length / 2);
|
|
22476
|
+
return sorted.length % 2 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2;
|
|
22477
|
+
};
|
|
22478
|
+
evaluateHyperliquidWhaleStructure = (params) => {
|
|
22479
|
+
const fills = params.fills.filter((fill) => {
|
|
22480
|
+
const time = finiteNumber3(fill.time);
|
|
22481
|
+
return time >= params.calibrationFromMs && time < params.calibrationToMs;
|
|
22482
|
+
}).sort((left, right) => finiteNumber3(left.time) - finiteNumber3(right.time));
|
|
22483
|
+
const days = Math.max(
|
|
22484
|
+
1,
|
|
22485
|
+
(params.calibrationToMs - params.calibrationFromMs) / 864e5
|
|
22486
|
+
);
|
|
22487
|
+
const executions = fills.reduce((clusters, fill) => {
|
|
22488
|
+
const coin = String(fill.coin);
|
|
22489
|
+
const side = String(fill.side);
|
|
22490
|
+
const time = finiteNumber3(fill.time);
|
|
22491
|
+
const notionalUsd = Math.abs(finiteNumber3(fill.px) * finiteNumber3(fill.sz));
|
|
22492
|
+
const previous = clusters.at(-1);
|
|
22493
|
+
if (previous && previous.coin === coin && previous.side === side && time - previous.time <= 6e4) {
|
|
22494
|
+
previous.time = time;
|
|
22495
|
+
previous.notionalUsd += notionalUsd;
|
|
22496
|
+
} else {
|
|
22497
|
+
clusters.push({ coin, side, time, notionalUsd });
|
|
22498
|
+
}
|
|
22499
|
+
return clusters;
|
|
22500
|
+
}, []);
|
|
22501
|
+
const notionals = executions.map((execution) => execution.notionalUsd);
|
|
22502
|
+
const totalNotional = notionals.reduce((sum2, value) => sum2 + value, 0);
|
|
22503
|
+
const top30Notional = executions.reduce(
|
|
22504
|
+
(sum2, execution) => sum2 + (params.top30Symbols.has(execution.coin) ? execution.notionalUsd : 0),
|
|
22505
|
+
0
|
|
22506
|
+
);
|
|
22507
|
+
const interExecutionMinutes = executions.slice(1).map(
|
|
22508
|
+
(execution, index) => (execution.time - executions[index].time) / 6e4
|
|
22509
|
+
).filter((value) => value >= 0);
|
|
22510
|
+
const activeDays = new Set(
|
|
22511
|
+
fills.map(
|
|
22512
|
+
(fill) => new Date(finiteNumber3(fill.time)).toISOString().slice(0, 10)
|
|
22513
|
+
)
|
|
22514
|
+
).size;
|
|
22515
|
+
const rawFillsPerDay = fills.length / days;
|
|
22516
|
+
const directionalExecutionsPerDay = executions.length / days;
|
|
22517
|
+
const medianNotionalUsd = median(notionals);
|
|
22518
|
+
const medianInterExecutionMinutes = median(interExecutionMinutes);
|
|
22519
|
+
const top30NotionalShare = totalNotional > 0 ? top30Notional / totalNotional : 0;
|
|
22520
|
+
const eligible = params.accountValueUsd >= HYPERLIQUID_WHALE_SELECTION.minimumAccountValueUsd && activeDays >= HYPERLIQUID_WHALE_SELECTION.minimumActiveDays && rawFillsPerDay <= HYPERLIQUID_WHALE_SELECTION.maximumRawFillsPerDay && directionalExecutionsPerDay <= HYPERLIQUID_WHALE_SELECTION.maximumDirectionalExecutionsPerDay && medianNotionalUsd >= HYPERLIQUID_WHALE_SELECTION.minimumMedianNotionalUsd && medianInterExecutionMinutes >= HYPERLIQUID_WHALE_SELECTION.minimumMedianInterExecutionMinutes && top30NotionalShare >= HYPERLIQUID_WHALE_SELECTION.minimumTop30NotionalShare;
|
|
22521
|
+
const score = Math.log1p(params.accountValueUsd) * Math.log1p(medianNotionalUsd) * Math.log1p(Math.max(1, medianInterExecutionMinutes)) * Math.sqrt(Math.max(0, activeDays)) * top30NotionalShare / Math.sqrt(1 + rawFillsPerDay);
|
|
22522
|
+
return {
|
|
22523
|
+
address: params.address.toLowerCase(),
|
|
22524
|
+
accountValueUsd: params.accountValueUsd,
|
|
22525
|
+
rawFills: fills.length,
|
|
22526
|
+
rawFillsPerDay,
|
|
22527
|
+
directionalExecutions: executions.length,
|
|
22528
|
+
directionalExecutionsPerDay,
|
|
22529
|
+
activeDays,
|
|
22530
|
+
medianNotionalUsd,
|
|
22531
|
+
medianInterExecutionMinutes,
|
|
22532
|
+
top30NotionalShare,
|
|
22533
|
+
score,
|
|
22534
|
+
eligible
|
|
22535
|
+
};
|
|
22536
|
+
};
|
|
22537
|
+
rankHyperliquidStructuralWhales = (metrics, limit = 100) => metrics.filter((row) => row.eligible).sort(
|
|
22538
|
+
(left, right) => right.score - left.score || left.address.localeCompare(right.address)
|
|
22539
|
+
).slice(0, limit);
|
|
22540
|
+
}
|
|
22541
|
+
});
|
|
22542
|
+
|
|
22543
|
+
// src/scripts/hyperliquidWhalesUpdate.ts
|
|
22544
|
+
var hyperliquidWhalesUpdate_exports = {};
|
|
22545
|
+
__export(hyperliquidWhalesUpdate_exports, {
|
|
22546
|
+
main: () => main26
|
|
22547
|
+
});
|
|
22548
|
+
var import_config8, import_args19, import_node_crypto4, import_strategies9, LEADERBOARD_URL, DAY_MS8, CANDIDATE_LIMIT, flags19, fingerprint, runConcurrent2, main26;
|
|
22549
|
+
var init_hyperliquidWhalesUpdate = __esm({
|
|
22550
|
+
"src/scripts/hyperliquidWhalesUpdate.ts"() {
|
|
22551
|
+
"use strict";
|
|
22552
|
+
import_config8 = require("dotenv/config");
|
|
22553
|
+
import_args19 = __toESM(require("args"));
|
|
22554
|
+
import_node_crypto4 = require("crypto");
|
|
22555
|
+
import_strategies9 = require("@tradejs/node/strategies");
|
|
22556
|
+
init_hyperliquidWhaleBackfill();
|
|
22557
|
+
init_hyperliquidRateLimiter();
|
|
22558
|
+
init_hyperliquidWhaleSelection();
|
|
22559
|
+
LEADERBOARD_URL = "https://stats-data.hyperliquid.xyz/Mainnet/leaderboard";
|
|
22560
|
+
DAY_MS8 = 864e5;
|
|
22561
|
+
CANDIDATE_LIMIT = 400;
|
|
22562
|
+
import_args19.default.option(
|
|
22563
|
+
["t", "to"],
|
|
22564
|
+
"Exclusive calibration end (ISO); defaults to the current UTC day"
|
|
22565
|
+
);
|
|
22566
|
+
import_args19.default.option(["d", "days"], "Calibration window in days", 7);
|
|
22567
|
+
flags19 = import_args19.default.parse(process.argv);
|
|
22568
|
+
fingerprint = (addresses) => (0, import_node_crypto4.createHash)("sha256").update(JSON.stringify(addresses)).digest("hex").slice(0, 16);
|
|
22569
|
+
runConcurrent2 = async (params) => {
|
|
22570
|
+
let nextIndex = 0;
|
|
22571
|
+
await Promise.all(
|
|
22572
|
+
Array.from({ length: params.concurrency }, async () => {
|
|
22573
|
+
for (; ; ) {
|
|
22574
|
+
const index = nextIndex;
|
|
22575
|
+
nextIndex += 1;
|
|
22576
|
+
if (index >= params.items.length) return;
|
|
22577
|
+
await params.worker(params.items[index]);
|
|
22578
|
+
}
|
|
22579
|
+
})
|
|
22580
|
+
);
|
|
22581
|
+
};
|
|
22582
|
+
main26 = async () => {
|
|
22583
|
+
const explicitTo = flags19.to == null ? Number.NaN : Date.parse(String(flags19.to));
|
|
22584
|
+
const calibrationToMs = Number.isFinite(explicitTo) ? explicitTo : Math.floor(Date.now() / DAY_MS8) * DAY_MS8;
|
|
22585
|
+
const calibrationDays = Math.max(1, Number(flags19.days) || 7);
|
|
22586
|
+
const calibrationFromMs = calibrationToMs - calibrationDays * DAY_MS8;
|
|
22587
|
+
const response = await fetch(LEADERBOARD_URL);
|
|
22588
|
+
if (!response.ok) {
|
|
22589
|
+
throw new Error(`Hyperliquid leaderboard ${response.status}`);
|
|
22590
|
+
}
|
|
22591
|
+
const payload = await response.json();
|
|
22592
|
+
const candidates = (payload.leaderboardRows ?? []).map((row) => {
|
|
22593
|
+
const address = String(row.ethAddress ?? "").toLowerCase();
|
|
22594
|
+
const accountValueUsd = Number(row.accountValue);
|
|
22595
|
+
const weeklyVolumeUsd = Number(
|
|
22596
|
+
row.windowPerformances?.find(([window2]) => window2 === "week")?.[1].vlm
|
|
22597
|
+
);
|
|
22598
|
+
return {
|
|
22599
|
+
address,
|
|
22600
|
+
accountValueUsd,
|
|
22601
|
+
turnoverToEquity: accountValueUsd > 0 ? weeklyVolumeUsd / accountValueUsd : 0
|
|
22602
|
+
};
|
|
22603
|
+
}).filter(
|
|
22604
|
+
(row) => /^0x[0-9a-f]{40}$/.test(row.address) && row.accountValueUsd >= HYPERLIQUID_WHALE_SELECTION.minimumAccountValueUsd && row.turnoverToEquity >= HYPERLIQUID_WHALE_SELECTION.minimumTurnoverToEquity && row.turnoverToEquity <= HYPERLIQUID_WHALE_SELECTION.maximumTurnoverToEquity
|
|
22605
|
+
).sort(
|
|
22606
|
+
(left, right) => left.turnoverToEquity - right.turnoverToEquity || right.accountValueUsd - left.accountValueUsd || left.address.localeCompare(right.address)
|
|
22607
|
+
).slice(0, CANDIDATE_LIMIT);
|
|
22608
|
+
const top30Symbols = new Set((0, import_strategies9.getHyperliquidPerpUniverseSnapshot)().symbols);
|
|
22609
|
+
const limiter = new HyperliquidInfoRateLimiter();
|
|
22610
|
+
const metrics = [];
|
|
22611
|
+
let completed = 0;
|
|
22612
|
+
await runConcurrent2({
|
|
22613
|
+
items: candidates,
|
|
22614
|
+
concurrency: 2,
|
|
22615
|
+
worker: async (candidate) => {
|
|
22616
|
+
const result = await fetchHyperliquidUserFillsByTime({
|
|
22617
|
+
address: candidate.address,
|
|
22618
|
+
startTime: calibrationFromMs,
|
|
22619
|
+
endTime: calibrationToMs - 1,
|
|
22620
|
+
rateLimiter: limiter,
|
|
22621
|
+
maxPages: 1
|
|
22622
|
+
});
|
|
22623
|
+
metrics.push(
|
|
22624
|
+
evaluateHyperliquidWhaleStructure({
|
|
22625
|
+
address: candidate.address,
|
|
22626
|
+
accountValueUsd: candidate.accountValueUsd,
|
|
22627
|
+
fills: result.fills,
|
|
22628
|
+
calibrationFromMs,
|
|
22629
|
+
calibrationToMs,
|
|
22630
|
+
top30Symbols
|
|
22631
|
+
})
|
|
22632
|
+
);
|
|
22633
|
+
completed += 1;
|
|
22634
|
+
if (completed % 10 === 0 || completed === candidates.length) {
|
|
22635
|
+
console.log(
|
|
22636
|
+
`Hyperliquid whale selection ${completed}/${candidates.length}; eligible=${metrics.filter((row) => row.eligible).length}`
|
|
22637
|
+
);
|
|
22638
|
+
}
|
|
22639
|
+
}
|
|
22640
|
+
});
|
|
22641
|
+
const selected = rankHyperliquidStructuralWhales(metrics, 100);
|
|
22642
|
+
if (selected.length !== 100) {
|
|
22643
|
+
throw new Error(
|
|
22644
|
+
`Only ${selected.length} structural whales passed selection from ${candidates.length} candidates`
|
|
22645
|
+
);
|
|
22646
|
+
}
|
|
22647
|
+
const addresses = selected.map((row) => row.address);
|
|
22648
|
+
const timestamp = new Date(calibrationToMs).toISOString();
|
|
22649
|
+
const snapshot = {
|
|
22650
|
+
schemaVersion: 2,
|
|
22651
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22652
|
+
source: "hyperliquid_structural_fills_snapshot",
|
|
22653
|
+
selection: {
|
|
22654
|
+
calibrationFrom: new Date(calibrationFromMs).toISOString(),
|
|
22655
|
+
calibrationTo: timestamp,
|
|
22656
|
+
effectiveFrom: timestamp,
|
|
22657
|
+
candidateLimit: CANDIDATE_LIMIT,
|
|
22658
|
+
...HYPERLIQUID_WHALE_SELECTION,
|
|
22659
|
+
score: "log1p(accountValue)*log1p(medianExecutionNotional)*log1p(max(1,medianInterExecutionMinutes))*sqrt(activeDays)*top30Share/sqrt(1+rawFillsPerDay)",
|
|
22660
|
+
forbiddenSelectionMetrics: ["pnl", "roi", "winRate", "closedPnl"]
|
|
22661
|
+
},
|
|
22662
|
+
fingerprint: fingerprint(addresses),
|
|
22663
|
+
size: 100,
|
|
22664
|
+
addresses
|
|
22665
|
+
};
|
|
22666
|
+
console.log(JSON.stringify(snapshot, null, 2));
|
|
22667
|
+
return snapshot;
|
|
22668
|
+
};
|
|
22669
|
+
}
|
|
22670
|
+
});
|
|
22671
|
+
|
|
21721
22672
|
// src/scripts/mlExport.ts
|
|
21722
22673
|
var mlExport_exports = {};
|
|
21723
22674
|
__export(mlExport_exports, {
|
|
21724
|
-
main: () =>
|
|
22675
|
+
main: () => main27
|
|
21725
22676
|
});
|
|
21726
|
-
var
|
|
22677
|
+
var import_args20, import_chalk31, import_promises10, import_path7, import_ml, flags20, resolveRunScopedMlChunks, main27;
|
|
21727
22678
|
var init_mlExport = __esm({
|
|
21728
22679
|
"src/scripts/mlExport.ts"() {
|
|
21729
22680
|
"use strict";
|
|
21730
|
-
|
|
22681
|
+
import_args20 = __toESM(require("args"));
|
|
21731
22682
|
import_chalk31 = __toESM(require("chalk"));
|
|
21732
22683
|
import_promises10 = __toESM(require("fs/promises"));
|
|
21733
22684
|
import_path7 = __toESM(require("path"));
|
|
21734
22685
|
import_ml = require("@tradejs/infra/ml");
|
|
21735
22686
|
init_checkpoint();
|
|
21736
22687
|
init_resolveExportStrategy();
|
|
21737
|
-
|
|
22688
|
+
import_args20.default.example(
|
|
21738
22689
|
"yarn ts-node ./src/scripts/mlExport --strategy trendline",
|
|
21739
22690
|
"Merge per-worker ML chunks into one dataset JSONL"
|
|
21740
22691
|
);
|
|
21741
|
-
|
|
21742
|
-
|
|
21743
|
-
|
|
22692
|
+
import_args20.default.option(["o", "outDir"], "Dataset directory", "data/ml/export");
|
|
22693
|
+
import_args20.default.option(["s", "strategy"], "Strategy name", "");
|
|
22694
|
+
import_args20.default.option(
|
|
21744
22695
|
["k", "keepChunks"],
|
|
21745
22696
|
"Keep source chunk files after successful merge",
|
|
21746
22697
|
false
|
|
21747
22698
|
);
|
|
21748
|
-
|
|
21749
|
-
|
|
22699
|
+
import_args20.default.option(["R", "runId"], "Backtest run id to export");
|
|
22700
|
+
import_args20.default.option(
|
|
21750
22701
|
["u", "user"],
|
|
21751
22702
|
"Backtest user for checkpoint-filtered export",
|
|
21752
22703
|
"root"
|
|
21753
22704
|
);
|
|
21754
|
-
|
|
22705
|
+
flags20 = import_args20.default.parse(process.argv);
|
|
21755
22706
|
resolveRunScopedMlChunks = async ({
|
|
21756
22707
|
outDir,
|
|
21757
22708
|
strategyName
|
|
21758
22709
|
}) => {
|
|
21759
|
-
const requestedRunId = typeof
|
|
22710
|
+
const requestedRunId = typeof flags20.runId === "string" && flags20.runId.trim() ? flags20.runId.trim() : "";
|
|
21760
22711
|
const runIds = requestedRunId ? [requestedRunId] : await (0, import_ml.listMlChunkRunIds)({ strategyName, outDir });
|
|
21761
22712
|
const runId = runIds.at(-1) ?? "";
|
|
21762
22713
|
if (!runId) {
|
|
@@ -21770,7 +22721,7 @@ var init_mlExport = __esm({
|
|
|
21770
22721
|
completedAttemptKeys: /* @__PURE__ */ new Set()
|
|
21771
22722
|
};
|
|
21772
22723
|
}
|
|
21773
|
-
const userName2 = String(
|
|
22724
|
+
const userName2 = String(flags20.user || "root");
|
|
21774
22725
|
const completed = await loadBacktestCheckpointResults({ runId, userName: userName2 });
|
|
21775
22726
|
return {
|
|
21776
22727
|
runId,
|
|
@@ -21778,11 +22729,11 @@ var init_mlExport = __esm({
|
|
|
21778
22729
|
completedAttemptKeys: buildCompletedBacktestDatasetAttemptKeys(completed)
|
|
21779
22730
|
};
|
|
21780
22731
|
};
|
|
21781
|
-
|
|
21782
|
-
const outDir = String(
|
|
22732
|
+
main27 = async () => {
|
|
22733
|
+
const outDir = String(flags20.outDir || "data/ml/export");
|
|
21783
22734
|
await import_promises10.default.mkdir(outDir, { recursive: true });
|
|
21784
22735
|
const strategyName = await resolveExportStrategy({
|
|
21785
|
-
explicitStrategy: String(
|
|
22736
|
+
explicitStrategy: String(flags20.strategy || ""),
|
|
21786
22737
|
outDir,
|
|
21787
22738
|
datasetLabel: "ML",
|
|
21788
22739
|
promptLabel: "Select ML export strategy",
|
|
@@ -21822,7 +22773,7 @@ var init_mlExport = __esm({
|
|
|
21822
22773
|
runScopedChunks.completedAttemptKeys
|
|
21823
22774
|
) : void 0
|
|
21824
22775
|
});
|
|
21825
|
-
const shouldDeleteChunks = !Boolean(
|
|
22776
|
+
const shouldDeleteChunks = !Boolean(flags20.keepChunks);
|
|
21826
22777
|
if (shouldDeleteChunks) {
|
|
21827
22778
|
for (const filePath of chunkFiles) {
|
|
21828
22779
|
await import_promises10.default.rm(filePath, { force: true });
|
|
@@ -21844,35 +22795,35 @@ var init_mlExport = __esm({
|
|
|
21844
22795
|
// src/scripts/mlInspect.ts
|
|
21845
22796
|
var mlInspect_exports = {};
|
|
21846
22797
|
__export(mlInspect_exports, {
|
|
21847
|
-
main: () =>
|
|
22798
|
+
main: () => main28
|
|
21848
22799
|
});
|
|
21849
|
-
var
|
|
22800
|
+
var import_args21, import_chalk32, import_promises11, import_fs3, import_path8, import_readline2, import_child_process2, flags21, toMode, toInspectTool, selectInspectTool, asPositiveInt, readRowsHead, readRowsTail, readRowsSample, quantileSorted, mean2, std, findLatestDataset, buildNumericStats, formatNumber4, runQuickInspect, runYDataInspect, main28;
|
|
21850
22801
|
var init_mlInspect = __esm({
|
|
21851
22802
|
"src/scripts/mlInspect.ts"() {
|
|
21852
22803
|
"use strict";
|
|
21853
|
-
|
|
22804
|
+
import_args21 = __toESM(require("args"));
|
|
21854
22805
|
import_chalk32 = __toESM(require("chalk"));
|
|
21855
22806
|
import_promises11 = __toESM(require("fs/promises"));
|
|
21856
22807
|
import_fs3 = require("fs");
|
|
21857
22808
|
import_path8 = __toESM(require("path"));
|
|
21858
22809
|
import_readline2 = __toESM(require("readline"));
|
|
21859
22810
|
import_child_process2 = require("child_process");
|
|
21860
|
-
|
|
22811
|
+
import_args21.default.example(
|
|
21861
22812
|
"yarn ml-inspect --rows 10000 --mode sample",
|
|
21862
22813
|
"Inspect the latest ML dataset chunk and highlight problematic features"
|
|
21863
22814
|
);
|
|
21864
|
-
|
|
21865
|
-
|
|
21866
|
-
|
|
21867
|
-
|
|
21868
|
-
|
|
22815
|
+
import_args21.default.option(["d", "dir"], "Dataset directory", "data/ml/export");
|
|
22816
|
+
import_args21.default.option(["r", "rows"], "Rows to inspect", 1e4);
|
|
22817
|
+
import_args21.default.option(["m", "mode"], "head | tail | sample", "sample");
|
|
22818
|
+
import_args21.default.option(["S", "strategy"], "Strategy token in dataset filename", "");
|
|
22819
|
+
import_args21.default.option(
|
|
21869
22820
|
["f", "file"],
|
|
21870
22821
|
"Explicit dataset file path (overrides auto-select)"
|
|
21871
22822
|
);
|
|
21872
|
-
|
|
21873
|
-
|
|
21874
|
-
|
|
21875
|
-
|
|
22823
|
+
import_args21.default.option(["L", "limitIssues"], "How many fields to print in report", 25);
|
|
22824
|
+
import_args21.default.option(["M", "minFieldValues"], "Min valid values per numeric field", 50);
|
|
22825
|
+
import_args21.default.option(["T", "tool"], "quick | ydata", "");
|
|
22826
|
+
flags21 = import_args21.default.parse(process.argv);
|
|
21876
22827
|
toMode = (value) => {
|
|
21877
22828
|
const mode = String(value || "sample").toLowerCase();
|
|
21878
22829
|
if (mode === "head" || mode === "tail" || mode === "sample") {
|
|
@@ -22086,7 +23037,7 @@ var init_mlInspect = __esm({
|
|
|
22086
23037
|
}
|
|
22087
23038
|
const sorted = [...values].sort((a, b) => a - b);
|
|
22088
23039
|
const q1 = quantileSorted(sorted, 0.25);
|
|
22089
|
-
const
|
|
23040
|
+
const median2 = quantileSorted(sorted, 0.5);
|
|
22090
23041
|
const q3 = quantileSorted(sorted, 0.75);
|
|
22091
23042
|
const p95 = quantileSorted(sorted, 0.95);
|
|
22092
23043
|
const p99 = quantileSorted(sorted, 0.99);
|
|
@@ -22109,7 +23060,7 @@ var init_mlInspect = __esm({
|
|
|
22109
23060
|
max: sorted[sorted.length - 1],
|
|
22110
23061
|
mean: valuesMean,
|
|
22111
23062
|
std: valuesStd,
|
|
22112
|
-
median,
|
|
23063
|
+
median: median2,
|
|
22113
23064
|
q1,
|
|
22114
23065
|
q3,
|
|
22115
23066
|
p95,
|
|
@@ -22338,15 +23289,15 @@ var init_mlInspect = __esm({
|
|
|
22338
23289
|
}
|
|
22339
23290
|
console.log(import_chalk32.default.green(`Profile report saved: ${reportPath}`));
|
|
22340
23291
|
};
|
|
22341
|
-
|
|
22342
|
-
const dir = String(
|
|
22343
|
-
const rowsToInspect = asPositiveInt(
|
|
22344
|
-
const mode = toMode(
|
|
22345
|
-
const strategy = String(
|
|
22346
|
-
const limitIssues = asPositiveInt(
|
|
22347
|
-
const minFieldValues = asPositiveInt(
|
|
22348
|
-
const toolFromFlags = toInspectTool(
|
|
22349
|
-
const explicitFile =
|
|
23292
|
+
main28 = async () => {
|
|
23293
|
+
const dir = String(flags21.dir || "data/ml/export");
|
|
23294
|
+
const rowsToInspect = asPositiveInt(flags21.rows, 1e4);
|
|
23295
|
+
const mode = toMode(flags21.mode);
|
|
23296
|
+
const strategy = String(flags21.strategy || "");
|
|
23297
|
+
const limitIssues = asPositiveInt(flags21.limitIssues, 25);
|
|
23298
|
+
const minFieldValues = asPositiveInt(flags21.minFieldValues, 50);
|
|
23299
|
+
const toolFromFlags = toInspectTool(flags21.tool);
|
|
23300
|
+
const explicitFile = flags21.file ? String(flags21.file) : "";
|
|
22350
23301
|
const datasetPath = explicitFile || await findLatestDataset({
|
|
22351
23302
|
dir,
|
|
22352
23303
|
strategy
|
|
@@ -22380,9 +23331,9 @@ var init_mlInspect = __esm({
|
|
|
22380
23331
|
// src/scripts/mlTrainLatestSelect.ts
|
|
22381
23332
|
var mlTrainLatestSelect_exports = {};
|
|
22382
23333
|
__export(mlTrainLatestSelect_exports, {
|
|
22383
|
-
main: () =>
|
|
23334
|
+
main: () => main29
|
|
22384
23335
|
});
|
|
22385
|
-
var import_path9, import_promises12, import_fs4, import_crypto2, import_child_process3, import_events, import_readline3,
|
|
23336
|
+
var import_path9, import_promises12, import_fs4, import_crypto2, import_child_process3, import_events, import_readline3, import_args22, import_chalk33, import_ml2, toFileToken5, asBool, asInt7, asNonNegativeInt, formatBytes, getMlContainerMemUsage, MODEL_TYPES, STRATEGIES, flags22, parseStrategy, parseModelType, selectModelType, listDatasetFiles, concatFiles, pipeNormalized, DAY_MS9, SPLIT_PROGRESS_EVERY, parseTimestampMs, scanMaxLabeledTimestampMs, scanMaxTrainTimestampMs, hashFileNameSha1, prepareTrainWindowFiles, main29;
|
|
22386
23337
|
var init_mlTrainLatestSelect = __esm({
|
|
22387
23338
|
"src/scripts/mlTrainLatestSelect.ts"() {
|
|
22388
23339
|
"use strict";
|
|
@@ -22393,7 +23344,7 @@ var init_mlTrainLatestSelect = __esm({
|
|
|
22393
23344
|
import_child_process3 = require("child_process");
|
|
22394
23345
|
import_events = require("events");
|
|
22395
23346
|
import_readline3 = __toESM(require("readline"));
|
|
22396
|
-
|
|
23347
|
+
import_args22 = __toESM(require("args"));
|
|
22397
23348
|
import_chalk33 = __toESM(require("chalk"));
|
|
22398
23349
|
init_selectStrategy();
|
|
22399
23350
|
import_ml2 = require("@tradejs/infra/ml");
|
|
@@ -22457,17 +23408,17 @@ var init_mlTrainLatestSelect = __esm({
|
|
|
22457
23408
|
"VolumeDivergence",
|
|
22458
23409
|
"any"
|
|
22459
23410
|
];
|
|
22460
|
-
|
|
22461
|
-
|
|
23411
|
+
import_args22.default.option(["s", "strategy"], "Strategy name (e.g. TrendLine)");
|
|
23412
|
+
import_args22.default.option(
|
|
22462
23413
|
["m", "model"],
|
|
22463
23414
|
"Model type: catboost | random_forest | extra_trees | xgboost | lightgbm"
|
|
22464
23415
|
);
|
|
22465
|
-
|
|
23416
|
+
import_args22.default.option(
|
|
22466
23417
|
["L", "latestOnly"],
|
|
22467
23418
|
"Use only latest dataset file (overrides ML_TRAIN_USE_LATEST_ONLY)",
|
|
22468
23419
|
false
|
|
22469
23420
|
);
|
|
22470
|
-
|
|
23421
|
+
flags22 = import_args22.default.parse(process.argv);
|
|
22471
23422
|
parseStrategy = (value) => {
|
|
22472
23423
|
const raw = String(value ?? "").trim();
|
|
22473
23424
|
if (!raw) return null;
|
|
@@ -22573,7 +23524,7 @@ var init_mlTrainLatestSelect = __esm({
|
|
|
22573
23524
|
}
|
|
22574
23525
|
};
|
|
22575
23526
|
};
|
|
22576
|
-
|
|
23527
|
+
DAY_MS9 = 24 * 60 * 60 * 1e3;
|
|
22577
23528
|
SPLIT_PROGRESS_EVERY = 2e5;
|
|
22578
23529
|
parseTimestampMs = (value) => {
|
|
22579
23530
|
const numeric = Number(value);
|
|
@@ -22790,7 +23741,7 @@ var init_mlTrainLatestSelect = __esm({
|
|
|
22790
23741
|
}
|
|
22791
23742
|
console.log("[split] phase 1/3: scanning max labeled timestamp...");
|
|
22792
23743
|
const maxLabeledTs = await scanMaxLabeledTimestampMs(inputPath);
|
|
22793
|
-
const holdoutCutoffMs = maxLabeledTs - testDays *
|
|
23744
|
+
const holdoutCutoffMs = maxLabeledTs - testDays * DAY_MS9;
|
|
22794
23745
|
console.log("[split] phase 2/3: scanning max train timestamp...");
|
|
22795
23746
|
const maxTrainTs = await scanMaxTrainTimestampMs(inputPath, holdoutCutoffMs);
|
|
22796
23747
|
const {
|
|
@@ -23103,16 +24054,16 @@ var init_mlTrainLatestSelect = __esm({
|
|
|
23103
24054
|
}
|
|
23104
24055
|
};
|
|
23105
24056
|
};
|
|
23106
|
-
|
|
23107
|
-
const selectedFromCli = parseStrategy(
|
|
24057
|
+
main29 = async () => {
|
|
24058
|
+
const selectedFromCli = parseStrategy(flags22.strategy);
|
|
23108
24059
|
const selected = selectedFromCli ?? await selectStrategy();
|
|
23109
24060
|
const envModelType = (process.env.ML_MODEL_TYPE ?? "random_forest").trim().toLowerCase();
|
|
23110
24061
|
const defaultModelType = MODEL_TYPES.includes(
|
|
23111
24062
|
envModelType
|
|
23112
24063
|
) ? envModelType : "random_forest";
|
|
23113
|
-
const modelFromCli = parseModelType(
|
|
24064
|
+
const modelFromCli = parseModelType(flags22.model);
|
|
23114
24065
|
const modelType = modelFromCli ?? await selectModelType(defaultModelType);
|
|
23115
|
-
const useLatestOnly = Boolean(
|
|
24066
|
+
const useLatestOnly = Boolean(flags22.latestOnly) || asBool(process.env.ML_TRAIN_USE_LATEST_ONLY);
|
|
23116
24067
|
const exportDir = import_path9.default.join(process.cwd(), "data", "ml", "export");
|
|
23117
24068
|
const modelDirRoot = import_path9.default.join(process.cwd(), "data", "ml");
|
|
23118
24069
|
const datasetFiles = await listDatasetFiles(exportDir, selected);
|
|
@@ -23367,7 +24318,7 @@ __export(researchAuto_exports, {
|
|
|
23367
24318
|
isProcessAlive: () => isProcessAlive,
|
|
23368
24319
|
listMergedFiles: () => listMergedFiles3,
|
|
23369
24320
|
listRuntimeStrategyNames: () => listRuntimeStrategyNames,
|
|
23370
|
-
main: () =>
|
|
24321
|
+
main: () => main30,
|
|
23371
24322
|
parseJsonOutput: () => parseJsonOutput,
|
|
23372
24323
|
releaseResearchAutoLock: () => releaseResearchAutoLock,
|
|
23373
24324
|
resolveHeapMbFromEnv: () => resolveHeapMbFromEnv,
|
|
@@ -23377,11 +24328,11 @@ __export(researchAuto_exports, {
|
|
|
23377
24328
|
toStrategyConfigGrid: () => toStrategyConfigGrid2,
|
|
23378
24329
|
trimTextTail: () => trimTextTail2
|
|
23379
24330
|
});
|
|
23380
|
-
var
|
|
24331
|
+
var import_args23, import_chalk34, import_node_child_process4, import_promises13, import_node_os2, import_node_path7, import_constants13, import_ai7, import_logger5, import_redis11, import_strategies10, flags23, projectRoot8, dotenvPath, getResearchAutoLockPath, readResearchAutoLock, isProcessAlive, acquireResearchAutoLock, releaseResearchAutoLock, resolvePositiveInteger, resolveHeapMbFromEnv, buildNodeOptions, RESEARCH_AUTO_PARENT_HEAP_MB, RESEARCH_AUTO_BACKTEST_PARENT_HEAP_MB, RESEARCH_AUTO_AI_TRAIN_PARENT_HEAP_MB, RESEARCH_AUTO_AGENT_PARENT_HEAP_MB, trimTextTail2, escapeHtml2, createEmptySteps, saveRun, listMergedFiles3, getBacktestResultsPrefix, listRuntimeStrategyNames, toStrategyConfigGrid2, formatMskDateTime, buildTelegramReport, runCliCommand, resolveTarget, parseJsonOutput, logResearch, executeNonBlockingStep, main30;
|
|
23381
24332
|
var init_researchAuto = __esm({
|
|
23382
24333
|
"src/scripts/researchAuto.ts"() {
|
|
23383
24334
|
"use strict";
|
|
23384
|
-
|
|
24335
|
+
import_args23 = __toESM(require("args"));
|
|
23385
24336
|
import_chalk34 = __toESM(require("chalk"));
|
|
23386
24337
|
import_node_child_process4 = require("child_process");
|
|
23387
24338
|
import_promises13 = __toESM(require("fs/promises"));
|
|
@@ -23391,32 +24342,32 @@ var init_researchAuto = __esm({
|
|
|
23391
24342
|
import_ai7 = require("@tradejs/infra/ai");
|
|
23392
24343
|
import_logger5 = require("@tradejs/infra/logger");
|
|
23393
24344
|
import_redis11 = require("@tradejs/infra/redis");
|
|
23394
|
-
|
|
24345
|
+
import_strategies10 = require("@tradejs/strategies");
|
|
23395
24346
|
init_runtimeRedis();
|
|
23396
24347
|
init_runtimeStrategyBacktest();
|
|
23397
24348
|
init_telegramReports();
|
|
23398
24349
|
init_runtimeRedis();
|
|
23399
|
-
|
|
24350
|
+
import_args23.default.example(
|
|
23400
24351
|
"yarn cli:node8g research:auto --strategy TrendLine --days 45",
|
|
23401
24352
|
"Run the nightly backtest -> ai-export -> ai-train local research pipeline and save structured results to Redis"
|
|
23402
24353
|
);
|
|
23403
|
-
|
|
23404
|
-
|
|
23405
|
-
|
|
23406
|
-
|
|
23407
|
-
|
|
23408
|
-
|
|
23409
|
-
|
|
23410
|
-
|
|
23411
|
-
|
|
24354
|
+
import_args23.default.option(["s", "strategy"], "Strategy name");
|
|
24355
|
+
import_args23.default.option(["c", "config"], "Backtest config name");
|
|
24356
|
+
import_args23.default.option(["U", "user"], "Use user config", "root");
|
|
24357
|
+
import_args23.default.option(["o", "connector"], "Backtest connector", "bybit");
|
|
24358
|
+
import_args23.default.option(["f", "timeframe"], "Backtest timeframe", "15");
|
|
24359
|
+
import_args23.default.option(["d", "days"], "Backtest days window", 45);
|
|
24360
|
+
import_args23.default.option(["n", "recent"], "AI train recent rows window", 1e3);
|
|
24361
|
+
import_args23.default.option(["k", "skip"], "AI train skip rows from end", 0);
|
|
24362
|
+
import_args23.default.option(
|
|
23412
24363
|
["M", "minQuality"],
|
|
23413
24364
|
"Minimum AI quality required to approve entry",
|
|
23414
24365
|
4
|
|
23415
24366
|
);
|
|
23416
|
-
|
|
23417
|
-
|
|
23418
|
-
|
|
23419
|
-
|
|
24367
|
+
import_args23.default.option(["O", "outDir"], "AI export output directory", "data/ai/export");
|
|
24368
|
+
import_args23.default.option(["A", "skipAgent"], "Skip direct research agent invocation", false);
|
|
24369
|
+
import_args23.default.option(["j", "json"], "Print structured JSON summary", false);
|
|
24370
|
+
flags23 = import_args23.default.parse(process.argv);
|
|
23420
24371
|
projectRoot8 = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
|
|
23421
24372
|
dotenvPath = String(
|
|
23422
24373
|
process.env.DOTENV_CONFIG_PATH || import_node_path7.default.join(projectRoot8, ".env")
|
|
@@ -23799,8 +24750,8 @@ var init_researchAuto = __esm({
|
|
|
23799
24750
|
});
|
|
23800
24751
|
};
|
|
23801
24752
|
resolveTarget = async () => {
|
|
23802
|
-
const userName2 = String(
|
|
23803
|
-
const explicitConfig = String(
|
|
24753
|
+
const userName2 = String(flags23.user || "root").trim() || "root";
|
|
24754
|
+
const explicitConfig = String(flags23.config || "").trim();
|
|
23804
24755
|
if (explicitConfig) {
|
|
23805
24756
|
return {
|
|
23806
24757
|
strategy: explicitConfig.split(":")[0] || explicitConfig,
|
|
@@ -23808,7 +24759,7 @@ var init_researchAuto = __esm({
|
|
|
23808
24759
|
selectedBy: "config"
|
|
23809
24760
|
};
|
|
23810
24761
|
}
|
|
23811
|
-
const explicitStrategy = String(
|
|
24762
|
+
const explicitStrategy = String(flags23.strategy || "").trim();
|
|
23812
24763
|
if (explicitStrategy) {
|
|
23813
24764
|
return {
|
|
23814
24765
|
strategy: explicitStrategy,
|
|
@@ -23897,23 +24848,23 @@ var init_researchAuto = __esm({
|
|
|
23897
24848
|
);
|
|
23898
24849
|
return result;
|
|
23899
24850
|
};
|
|
23900
|
-
|
|
23901
|
-
const userName2 = String(
|
|
23902
|
-
const connector = String(
|
|
23903
|
-
const timeframe = String(
|
|
23904
|
-
const days = Math.max(1, Number.parseInt(String(
|
|
24851
|
+
main30 = async () => {
|
|
24852
|
+
const userName2 = String(flags23.user || "root").trim() || "root";
|
|
24853
|
+
const connector = String(flags23.connector || "bybit").trim() || "bybit";
|
|
24854
|
+
const timeframe = String(flags23.timeframe || "15").trim() || "15";
|
|
24855
|
+
const days = Math.max(1, Number.parseInt(String(flags23.days ?? 45), 10) || 45);
|
|
23905
24856
|
const recent = Math.max(
|
|
23906
24857
|
0,
|
|
23907
|
-
Number.parseInt(String(
|
|
24858
|
+
Number.parseInt(String(flags23.recent ?? 1e3), 10) || 1e3
|
|
23908
24859
|
);
|
|
23909
|
-
const skip = Math.max(0, Number.parseInt(String(
|
|
24860
|
+
const skip = Math.max(0, Number.parseInt(String(flags23.skip ?? 0), 10) || 0);
|
|
23910
24861
|
const minQuality = Math.max(
|
|
23911
24862
|
0,
|
|
23912
|
-
Number.parseInt(String(
|
|
24863
|
+
Number.parseInt(String(flags23.minQuality ?? 4), 10) || 4
|
|
23913
24864
|
);
|
|
23914
|
-
const jsonOutput = Boolean(
|
|
23915
|
-
const skipAgent = Boolean(
|
|
23916
|
-
const outDirArg = String(
|
|
24865
|
+
const jsonOutput = Boolean(flags23.json);
|
|
24866
|
+
const skipAgent = Boolean(flags23.skipAgent);
|
|
24867
|
+
const outDirArg = String(flags23.outDir || "data/ai/export").trim() || "data/ai/export";
|
|
23917
24868
|
const outDir = import_node_path7.default.resolve(projectRoot8, outDirArg);
|
|
23918
24869
|
const cleanDirArg = import_node_path7.default.relative(import_node_path7.default.join(projectRoot8, "data"), outDir).replace(/\\/g, "/");
|
|
23919
24870
|
const target = await resolveTarget();
|
|
@@ -24004,7 +24955,7 @@ var init_researchAuto = __esm({
|
|
|
24004
24955
|
target.strategy
|
|
24005
24956
|
);
|
|
24006
24957
|
const backtestConfigKey = import_redis11.redisKeys.backtestConfig(userName2, target.config);
|
|
24007
|
-
const defaults2 = (0,
|
|
24958
|
+
const defaults2 = (0, import_strategies10.getBuiltInStrategyDefaultConfig)(target.strategy) || {};
|
|
24008
24959
|
const userStrategyConfig = await (0, import_redis11.getData)(
|
|
24009
24960
|
strategyConfigKey,
|
|
24010
24961
|
{}
|
|
@@ -24251,31 +25202,31 @@ Telegram report failed: ${suffix}`;
|
|
|
24251
25202
|
// src/scripts/results.ts
|
|
24252
25203
|
var results_exports = {};
|
|
24253
25204
|
__export(results_exports, {
|
|
24254
|
-
main: () =>
|
|
25205
|
+
main: () => main31,
|
|
24255
25206
|
results: () => results
|
|
24256
25207
|
});
|
|
24257
|
-
var
|
|
25208
|
+
var import_args24, import_chalk35, import_connectors13, import_cli10, import_redis12, ListIt4, flags24, MIN_PROFIT, MIN_WIN_RATE, MIN_ORDERS_PER_MONTH, createListIt4, formatPercent3, getMonthlyReturn, toStrategyResultEntry, toStrategyResults, getTestConfigs, buildBestResults, getSavedProfitsBySymbol, getCoverageRow, results, main31;
|
|
24258
25209
|
var init_results = __esm({
|
|
24259
25210
|
"src/scripts/results.ts"() {
|
|
24260
25211
|
"use strict";
|
|
24261
|
-
|
|
25212
|
+
import_args24 = __toESM(require("args"));
|
|
24262
25213
|
import_chalk35 = __toESM(require("chalk"));
|
|
24263
25214
|
import_connectors13 = require("@tradejs/connectors");
|
|
24264
25215
|
import_cli10 = require("@tradejs/node/cli");
|
|
24265
25216
|
import_redis12 = require("@tradejs/infra/redis");
|
|
24266
25217
|
ListIt4 = require_list_it();
|
|
24267
|
-
|
|
25218
|
+
import_args24.default.example(
|
|
24268
25219
|
"yarn results --strategy TrendLine --coverage",
|
|
24269
25220
|
"Show best results per symbol and coverage"
|
|
24270
25221
|
);
|
|
24271
|
-
|
|
24272
|
-
|
|
24273
|
-
|
|
24274
|
-
|
|
24275
|
-
|
|
24276
|
-
|
|
24277
|
-
|
|
24278
|
-
|
|
25222
|
+
import_args24.default.option(["s", "strategy"], "Strategy name");
|
|
25223
|
+
import_args24.default.option(["C", "coverage"], "Show coverage table", false);
|
|
25224
|
+
import_args24.default.option(["u", "update"], "Update results config in redis", false);
|
|
25225
|
+
import_args24.default.option(["m", "merge"], "Merge results config in redis", false);
|
|
25226
|
+
import_args24.default.option(["c", "clear"], "Clear results config in redis", false);
|
|
25227
|
+
import_args24.default.option(["V", "verbose"], "Verbose output", false);
|
|
25228
|
+
import_args24.default.option(["U", "user"], "Use user config", "root");
|
|
25229
|
+
flags24 = import_args24.default.parse(process.argv);
|
|
24279
25230
|
MIN_PROFIT = 2;
|
|
24280
25231
|
MIN_WIN_RATE = 40;
|
|
24281
25232
|
MIN_ORDERS_PER_MONTH = 1;
|
|
@@ -24415,25 +25366,25 @@ var init_results = __esm({
|
|
|
24415
25366
|
};
|
|
24416
25367
|
};
|
|
24417
25368
|
results = async () => {
|
|
24418
|
-
if (!
|
|
25369
|
+
if (!flags24.strategy) {
|
|
24419
25370
|
console.error(import_chalk35.default.red("Missing --strategy"));
|
|
24420
25371
|
process.exit(1);
|
|
24421
25372
|
}
|
|
24422
|
-
const strategyName =
|
|
24423
|
-
const userName2 =
|
|
24424
|
-
if (
|
|
25373
|
+
const strategyName = flags24.strategy;
|
|
25374
|
+
const userName2 = flags24.user;
|
|
25375
|
+
if (flags24.clear) {
|
|
24425
25376
|
const cleared = await (0, import_redis12.delKey)(
|
|
24426
25377
|
import_redis12.redisKeys.strategyResults(userName2, strategyName)
|
|
24427
25378
|
);
|
|
24428
25379
|
console.log(
|
|
24429
25380
|
cleared ? import_chalk35.default.green(`Cleared results:${strategyName}`) : import_chalk35.default.yellow(`No results to clear for ${strategyName}`)
|
|
24430
25381
|
);
|
|
24431
|
-
if (!
|
|
25382
|
+
if (!flags24.coverage && !flags24.update && !flags24.merge) {
|
|
24432
25383
|
process.exit();
|
|
24433
25384
|
}
|
|
24434
25385
|
}
|
|
24435
25386
|
const bestBySymbol = await buildBestResults(userName2, strategyName);
|
|
24436
|
-
if (
|
|
25387
|
+
if (flags24.coverage) {
|
|
24437
25388
|
const rows = [...bestBySymbol.values()].sort((a, b) => b.profit - a.profit).map((row) => [
|
|
24438
25389
|
import_chalk35.default.yellow(row.symbol),
|
|
24439
25390
|
import_chalk35.default.green(formatPercent3(row.profit)),
|
|
@@ -24464,9 +25415,9 @@ var init_results = __esm({
|
|
|
24464
25415
|
);
|
|
24465
25416
|
console.log("");
|
|
24466
25417
|
}
|
|
24467
|
-
if (
|
|
25418
|
+
if (flags24.update || flags24.merge) {
|
|
24468
25419
|
const resultsConfig = toStrategyResults(bestBySymbol.values());
|
|
24469
|
-
if (
|
|
25420
|
+
if (flags24.merge) {
|
|
24470
25421
|
if (Object.keys(resultsConfig).length === 0) {
|
|
24471
25422
|
console.log(import_chalk35.default.yellow("No good results to merge."));
|
|
24472
25423
|
process.exit(0);
|
|
@@ -24497,7 +25448,7 @@ var init_results = __esm({
|
|
|
24497
25448
|
await (0, import_redis12.setData)(import_redis12.redisKeys.strategyResults(userName2, strategyName), merged, {
|
|
24498
25449
|
expire: 0
|
|
24499
25450
|
});
|
|
24500
|
-
if (
|
|
25451
|
+
if (flags24.verbose) {
|
|
24501
25452
|
const updateRows = updates.sort((a, b) => {
|
|
24502
25453
|
const prevA = savedProfitBySymbol.get(a.symbol) ?? -Infinity;
|
|
24503
25454
|
const prevB = savedProfitBySymbol.get(b.symbol) ?? -Infinity;
|
|
@@ -24550,7 +25501,7 @@ var init_results = __esm({
|
|
|
24550
25501
|
}
|
|
24551
25502
|
process.exit();
|
|
24552
25503
|
};
|
|
24553
|
-
|
|
25504
|
+
main31 = results;
|
|
24554
25505
|
}
|
|
24555
25506
|
});
|
|
24556
25507
|
|
|
@@ -25211,43 +26162,43 @@ var init_support = __esm({
|
|
|
25211
26162
|
// src/scripts/replayRuntimeEvidence.ts
|
|
25212
26163
|
var replayRuntimeEvidence_exports = {};
|
|
25213
26164
|
__export(replayRuntimeEvidence_exports, {
|
|
25214
|
-
main: () =>
|
|
26165
|
+
main: () => main32,
|
|
25215
26166
|
replayRuntimeEvidence: () => replayRuntimeEvidence
|
|
25216
26167
|
});
|
|
25217
|
-
var import_promises14, import_node_path8,
|
|
26168
|
+
var import_promises14, import_node_path8, import_args25, import_chalk37, import_redis15, flags25, projectRoot9, toEpochMs2, resolveWindow, parseStrategyFilter, asRecord3, asArray2, sum, groupCount, compactMatched, resolveItemStrategy, summarizeReplayComparison, loadReplayResult, getReplayStrategies, loadRuntimePayload, replayRuntimeEvidence, main32;
|
|
25218
26169
|
var init_replayRuntimeEvidence = __esm({
|
|
25219
26170
|
"src/scripts/replayRuntimeEvidence.ts"() {
|
|
25220
26171
|
"use strict";
|
|
25221
26172
|
import_promises14 = __toESM(require("fs/promises"));
|
|
25222
26173
|
import_node_path8 = __toESM(require("path"));
|
|
25223
|
-
|
|
26174
|
+
import_args25 = __toESM(require("args"));
|
|
25224
26175
|
import_chalk37 = __toESM(require("chalk"));
|
|
25225
26176
|
import_redis15 = require("@tradejs/infra/redis");
|
|
25226
26177
|
init_runtimeDebugEvidence();
|
|
25227
26178
|
init_support();
|
|
25228
|
-
|
|
25229
|
-
|
|
26179
|
+
import_args25.default.option(["u", "user"], "Use user config", "root");
|
|
26180
|
+
import_args25.default.option(
|
|
25230
26181
|
["S", "startTime"],
|
|
25231
26182
|
"Window start timestamp, seconds/ms, or ISO date"
|
|
25232
26183
|
);
|
|
25233
|
-
|
|
25234
|
-
|
|
26184
|
+
import_args25.default.option(["E", "endTime"], "Window end timestamp, seconds/ms, or ISO date");
|
|
26185
|
+
import_args25.default.option(
|
|
25235
26186
|
["H", "hours"],
|
|
25236
26187
|
"Fallback window in hours when start/end are omitted",
|
|
25237
26188
|
24
|
|
25238
26189
|
);
|
|
25239
|
-
|
|
25240
|
-
|
|
25241
|
-
|
|
26190
|
+
import_args25.default.option(["s", "strategy"], "Strategy filter, comma-separated");
|
|
26191
|
+
import_args25.default.option("replayKey", "Exact replay result Redis key");
|
|
26192
|
+
import_args25.default.option(
|
|
25242
26193
|
"runtimeEvidence",
|
|
25243
26194
|
"Runtime evidence JSON collected on the runtime server"
|
|
25244
26195
|
);
|
|
25245
|
-
|
|
26196
|
+
import_args25.default.option(
|
|
25246
26197
|
["o", "out"],
|
|
25247
26198
|
"Output JSON path",
|
|
25248
26199
|
"output/replay-runtime-evidence.json"
|
|
25249
26200
|
);
|
|
25250
|
-
|
|
26201
|
+
flags25 = import_args25.default.parse(process.argv);
|
|
25251
26202
|
projectRoot9 = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
|
|
25252
26203
|
toEpochMs2 = (value) => {
|
|
25253
26204
|
if (value == null || value === "") {
|
|
@@ -25262,12 +26213,12 @@ var init_replayRuntimeEvidence = __esm({
|
|
|
25262
26213
|
return Number.isFinite(parsed) ? parsed : null;
|
|
25263
26214
|
};
|
|
25264
26215
|
resolveWindow = () => {
|
|
25265
|
-
const endTime = toEpochMs2(
|
|
26216
|
+
const endTime = toEpochMs2(flags25.endTime) ?? Date.now();
|
|
25266
26217
|
const hours = Math.max(
|
|
25267
26218
|
1,
|
|
25268
|
-
Number.parseInt(String(
|
|
26219
|
+
Number.parseInt(String(flags25.hours ?? 24), 10) || 24
|
|
25269
26220
|
);
|
|
25270
|
-
const startTime = toEpochMs2(
|
|
26221
|
+
const startTime = toEpochMs2(flags25.startTime) ?? endTime - hours * 60 * 60 * 1e3;
|
|
25271
26222
|
if (startTime >= endTime) {
|
|
25272
26223
|
throw new Error(
|
|
25273
26224
|
`Invalid window: startTime (${startTime}) must be less than endTime (${endTime})`
|
|
@@ -25276,7 +26227,7 @@ var init_replayRuntimeEvidence = __esm({
|
|
|
25276
26227
|
return {
|
|
25277
26228
|
startTime,
|
|
25278
26229
|
endTime,
|
|
25279
|
-
source:
|
|
26230
|
+
source: flags25.startTime != null || flags25.endTime != null ? "explicit" : "hours"
|
|
25280
26231
|
};
|
|
25281
26232
|
};
|
|
25282
26233
|
parseStrategyFilter = (value) => String(value ?? "").split(",").map((item) => item.trim()).filter(Boolean);
|
|
@@ -25460,7 +26411,7 @@ var init_replayRuntimeEvidence = __esm({
|
|
|
25460
26411
|
endTime,
|
|
25461
26412
|
strategies
|
|
25462
26413
|
}) => {
|
|
25463
|
-
const runtimeEvidencePath = typeof
|
|
26414
|
+
const runtimeEvidencePath = typeof flags25.runtimeEvidence === "string" && flags25.runtimeEvidence.trim() ? import_node_path8.default.resolve(projectRoot9, flags25.runtimeEvidence.trim()) : null;
|
|
25464
26415
|
if (runtimeEvidencePath) {
|
|
25465
26416
|
const artifact = JSON.parse(
|
|
25466
26417
|
await import_promises14.default.readFile(runtimeEvidencePath, "utf8")
|
|
@@ -25472,7 +26423,7 @@ var init_replayRuntimeEvidence = __esm({
|
|
|
25472
26423
|
};
|
|
25473
26424
|
}
|
|
25474
26425
|
const runtimeEvidence2 = await collectRuntimeDebugEvidence({
|
|
25475
|
-
userName:
|
|
26426
|
+
userName: flags25.user,
|
|
25476
26427
|
startTime,
|
|
25477
26428
|
endTime,
|
|
25478
26429
|
strategies: strategies.length ? strategies : void 0
|
|
@@ -25481,7 +26432,7 @@ var init_replayRuntimeEvidence = __esm({
|
|
|
25481
26432
|
source: "local-redis",
|
|
25482
26433
|
path: null,
|
|
25483
26434
|
payload: await buildRuntimeDebugReportPayload({
|
|
25484
|
-
userName:
|
|
26435
|
+
userName: flags25.user,
|
|
25485
26436
|
startTime,
|
|
25486
26437
|
endTime,
|
|
25487
26438
|
signals: runtimeEvidence2.signals,
|
|
@@ -25495,10 +26446,10 @@ var init_replayRuntimeEvidence = __esm({
|
|
|
25495
26446
|
replayRuntimeEvidence = async () => {
|
|
25496
26447
|
const { startTime, endTime, source } = resolveWindow();
|
|
25497
26448
|
const replay = await loadReplayResult({
|
|
25498
|
-
userName:
|
|
25499
|
-
replayKey: typeof
|
|
26449
|
+
userName: flags25.user,
|
|
26450
|
+
replayKey: typeof flags25.replayKey === "string" && flags25.replayKey.trim() ? flags25.replayKey.trim() : void 0
|
|
25500
26451
|
});
|
|
25501
|
-
const explicitStrategies = parseStrategyFilter(
|
|
26452
|
+
const explicitStrategies = parseStrategyFilter(flags25.strategy);
|
|
25502
26453
|
const strategies = explicitStrategies.length ? explicitStrategies : getReplayStrategies(replay.value);
|
|
25503
26454
|
const runtime = await loadRuntimePayload({
|
|
25504
26455
|
startTime,
|
|
@@ -25509,7 +26460,7 @@ var init_replayRuntimeEvidence = __esm({
|
|
|
25509
26460
|
const artifact = {
|
|
25510
26461
|
reportType: "replay-runtime-evidence",
|
|
25511
26462
|
generatedAt: Date.now(),
|
|
25512
|
-
userName:
|
|
26463
|
+
userName: flags25.user,
|
|
25513
26464
|
window: {
|
|
25514
26465
|
startTime,
|
|
25515
26466
|
endTime,
|
|
@@ -25537,7 +26488,7 @@ var init_replayRuntimeEvidence = __esm({
|
|
|
25537
26488
|
},
|
|
25538
26489
|
runtime: runtime.payload
|
|
25539
26490
|
};
|
|
25540
|
-
const outPath = import_node_path8.default.resolve(projectRoot9, String(
|
|
26491
|
+
const outPath = import_node_path8.default.resolve(projectRoot9, String(flags25.out));
|
|
25541
26492
|
await import_promises14.default.mkdir(import_node_path8.default.dirname(outPath), { recursive: true });
|
|
25542
26493
|
await import_promises14.default.writeFile(outPath, `${JSON.stringify(artifact, null, 2)}
|
|
25543
26494
|
`, "utf8");
|
|
@@ -25557,39 +26508,39 @@ var init_replayRuntimeEvidence = __esm({
|
|
|
25557
26508
|
)
|
|
25558
26509
|
);
|
|
25559
26510
|
};
|
|
25560
|
-
|
|
26511
|
+
main32 = replayRuntimeEvidence;
|
|
25561
26512
|
}
|
|
25562
26513
|
});
|
|
25563
26514
|
|
|
25564
26515
|
// src/scripts/runtimeEvidence.ts
|
|
25565
26516
|
var runtimeEvidence_exports = {};
|
|
25566
26517
|
__export(runtimeEvidence_exports, {
|
|
25567
|
-
main: () =>
|
|
26518
|
+
main: () => main33,
|
|
25568
26519
|
runtimeEvidence: () => runtimeEvidence
|
|
25569
26520
|
});
|
|
25570
|
-
var import_promises15, import_node_path9,
|
|
26521
|
+
var import_promises15, import_node_path9, import_args26, import_chalk38, flags26, projectRoot10, toEpochMs3, resolveWindow2, parseStrategyFilter2, runtimeEvidence, main33;
|
|
25571
26522
|
var init_runtimeEvidence = __esm({
|
|
25572
26523
|
"src/scripts/runtimeEvidence.ts"() {
|
|
25573
26524
|
"use strict";
|
|
25574
26525
|
import_promises15 = __toESM(require("fs/promises"));
|
|
25575
26526
|
import_node_path9 = __toESM(require("path"));
|
|
25576
|
-
|
|
26527
|
+
import_args26 = __toESM(require("args"));
|
|
25577
26528
|
import_chalk38 = __toESM(require("chalk"));
|
|
25578
26529
|
init_runtimeDebugEvidence();
|
|
25579
|
-
|
|
25580
|
-
|
|
26530
|
+
import_args26.default.option(["u", "user"], "Use user config", "root");
|
|
26531
|
+
import_args26.default.option(
|
|
25581
26532
|
["S", "startTime"],
|
|
25582
26533
|
"Window start timestamp, seconds/ms, or ISO date"
|
|
25583
26534
|
);
|
|
25584
|
-
|
|
25585
|
-
|
|
26535
|
+
import_args26.default.option(["E", "endTime"], "Window end timestamp, seconds/ms, or ISO date");
|
|
26536
|
+
import_args26.default.option(
|
|
25586
26537
|
["H", "hours"],
|
|
25587
26538
|
"Fallback window in hours when start/end are omitted",
|
|
25588
26539
|
24
|
|
25589
26540
|
);
|
|
25590
|
-
|
|
25591
|
-
|
|
25592
|
-
|
|
26541
|
+
import_args26.default.option(["s", "strategy"], "Strategy filter, comma-separated");
|
|
26542
|
+
import_args26.default.option(["o", "out"], "Output JSON path", "output/runtime-evidence.json");
|
|
26543
|
+
flags26 = import_args26.default.parse(process.argv);
|
|
25593
26544
|
projectRoot10 = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
|
|
25594
26545
|
toEpochMs3 = (value) => {
|
|
25595
26546
|
if (value == null || value === "") {
|
|
@@ -25604,12 +26555,12 @@ var init_runtimeEvidence = __esm({
|
|
|
25604
26555
|
return Number.isFinite(parsed) ? parsed : null;
|
|
25605
26556
|
};
|
|
25606
26557
|
resolveWindow2 = () => {
|
|
25607
|
-
const endTime = toEpochMs3(
|
|
26558
|
+
const endTime = toEpochMs3(flags26.endTime) ?? Date.now();
|
|
25608
26559
|
const hours = Math.max(
|
|
25609
26560
|
1,
|
|
25610
|
-
Number.parseInt(String(
|
|
26561
|
+
Number.parseInt(String(flags26.hours ?? 24), 10) || 24
|
|
25611
26562
|
);
|
|
25612
|
-
const startTime = toEpochMs3(
|
|
26563
|
+
const startTime = toEpochMs3(flags26.startTime) ?? endTime - hours * 60 * 60 * 1e3;
|
|
25613
26564
|
if (startTime >= endTime) {
|
|
25614
26565
|
throw new Error(
|
|
25615
26566
|
`Invalid window: startTime (${startTime}) must be less than endTime (${endTime})`
|
|
@@ -25618,21 +26569,21 @@ var init_runtimeEvidence = __esm({
|
|
|
25618
26569
|
return {
|
|
25619
26570
|
startTime,
|
|
25620
26571
|
endTime,
|
|
25621
|
-
source:
|
|
26572
|
+
source: flags26.startTime != null || flags26.endTime != null ? "explicit" : "hours"
|
|
25622
26573
|
};
|
|
25623
26574
|
};
|
|
25624
26575
|
parseStrategyFilter2 = (value) => String(value ?? "").split(",").map((item) => item.trim()).filter(Boolean);
|
|
25625
26576
|
runtimeEvidence = async () => {
|
|
25626
26577
|
const { startTime, endTime, source } = resolveWindow2();
|
|
25627
|
-
const strategies = parseStrategyFilter2(
|
|
26578
|
+
const strategies = parseStrategyFilter2(flags26.strategy);
|
|
25628
26579
|
const evidence = await collectRuntimeDebugEvidence({
|
|
25629
|
-
userName:
|
|
26580
|
+
userName: flags26.user,
|
|
25630
26581
|
startTime,
|
|
25631
26582
|
endTime,
|
|
25632
26583
|
strategies: strategies.length ? strategies : void 0
|
|
25633
26584
|
});
|
|
25634
26585
|
const runtime = await buildRuntimeDebugReportPayload({
|
|
25635
|
-
userName:
|
|
26586
|
+
userName: flags26.user,
|
|
25636
26587
|
startTime,
|
|
25637
26588
|
endTime,
|
|
25638
26589
|
signals: evidence.signals,
|
|
@@ -25644,7 +26595,7 @@ var init_runtimeEvidence = __esm({
|
|
|
25644
26595
|
const artifact = {
|
|
25645
26596
|
reportType: "runtime-evidence",
|
|
25646
26597
|
generatedAt: Date.now(),
|
|
25647
|
-
userName:
|
|
26598
|
+
userName: flags26.user,
|
|
25648
26599
|
window: {
|
|
25649
26600
|
startTime,
|
|
25650
26601
|
endTime,
|
|
@@ -25655,7 +26606,7 @@ var init_runtimeEvidence = __esm({
|
|
|
25655
26606
|
strategies,
|
|
25656
26607
|
runtime
|
|
25657
26608
|
};
|
|
25658
|
-
const outPath = import_node_path9.default.resolve(projectRoot10, String(
|
|
26609
|
+
const outPath = import_node_path9.default.resolve(projectRoot10, String(flags26.out));
|
|
25659
26610
|
await import_promises15.default.mkdir(import_node_path9.default.dirname(outPath), { recursive: true });
|
|
25660
26611
|
await import_promises15.default.writeFile(outPath, `${JSON.stringify(artifact, null, 2)}
|
|
25661
26612
|
`, "utf8");
|
|
@@ -25671,7 +26622,7 @@ var init_runtimeEvidence = __esm({
|
|
|
25671
26622
|
)
|
|
25672
26623
|
);
|
|
25673
26624
|
};
|
|
25674
|
-
|
|
26625
|
+
main33 = runtimeEvidence;
|
|
25675
26626
|
}
|
|
25676
26627
|
});
|
|
25677
26628
|
|
|
@@ -25683,18 +26634,18 @@ __export(serverHealth_exports, {
|
|
|
25683
26634
|
buildServerHealthDiagnostics: () => buildServerHealthDiagnostics,
|
|
25684
26635
|
collectServerHealthSnapshot: () => collectServerHealthSnapshot,
|
|
25685
26636
|
evaluateServerHealth: () => evaluateServerHealth,
|
|
25686
|
-
main: () =>
|
|
26637
|
+
main: () => main34,
|
|
25687
26638
|
resolveServerHealthThresholds: () => resolveServerHealthThresholds
|
|
25688
26639
|
});
|
|
25689
|
-
var
|
|
26640
|
+
var import_config9, import_node_child_process5, import_node_os3, import_node_fs2, import_args27, import_redis16, DEFAULT_STATE, DEFAULT_THRESHOLDS, escapeHtml3, formatPctValue, formatLoadPerCore, formatGiB, formatDuration2, readDiskUsage, parseMeminfoBytes, readMemorySnapshot, runDiagnosticCommand, runDiagnosticCommands, collectServerHealthSnapshot, buildHumanSummaryLines, evaluateServerHealth, shouldRecover, buildAlertMessage, buildRecoveryMessage, buildServerHealthDiagnostics, getStateKey, toFiniteNumber3, resolveServerHealthThresholds, main34;
|
|
25690
26641
|
var init_serverHealth = __esm({
|
|
25691
26642
|
"src/scripts/serverHealth.ts"() {
|
|
25692
26643
|
"use strict";
|
|
25693
|
-
|
|
26644
|
+
import_config9 = require("dotenv/config");
|
|
25694
26645
|
import_node_child_process5 = require("child_process");
|
|
25695
26646
|
import_node_os3 = __toESM(require("os"));
|
|
25696
26647
|
import_node_fs2 = require("fs");
|
|
25697
|
-
|
|
26648
|
+
import_args27 = __toESM(require("args"));
|
|
25698
26649
|
import_redis16 = require("@tradejs/infra/redis");
|
|
25699
26650
|
init_telegramReports();
|
|
25700
26651
|
DEFAULT_STATE = {
|
|
@@ -25806,8 +26757,8 @@ var init_serverHealth = __esm({
|
|
|
25806
26757
|
};
|
|
25807
26758
|
}
|
|
25808
26759
|
};
|
|
25809
|
-
runDiagnosticCommand = (command,
|
|
25810
|
-
const output = (0, import_node_child_process5.execFileSync)(command,
|
|
26760
|
+
runDiagnosticCommand = (command, args33) => {
|
|
26761
|
+
const output = (0, import_node_child_process5.execFileSync)(command, args33, {
|
|
25811
26762
|
encoding: "utf8",
|
|
25812
26763
|
timeout: 5e3,
|
|
25813
26764
|
maxBuffer: 1024 * 1024,
|
|
@@ -26057,57 +27008,57 @@ var init_serverHealth = __esm({
|
|
|
26057
27008
|
diskRecoverPct: diskRecoverPct != null && diskRecoverPct > 0 ? diskRecoverPct : DEFAULT_THRESHOLDS.diskRecoverPct
|
|
26058
27009
|
};
|
|
26059
27010
|
};
|
|
26060
|
-
|
|
26061
|
-
|
|
26062
|
-
|
|
27011
|
+
import_args27.default.option(["u", "user"], "Use user config", "root");
|
|
27012
|
+
import_args27.default.option(["P", "printOnly"], "Print report instead of Telegram", false);
|
|
27013
|
+
import_args27.default.option(
|
|
26063
27014
|
["d", "diskPath"],
|
|
26064
27015
|
"Filesystem path to inspect for disk pressure",
|
|
26065
27016
|
"/"
|
|
26066
27017
|
);
|
|
26067
|
-
|
|
27018
|
+
import_args27.default.option(
|
|
26068
27019
|
["c", "cooldownSec"],
|
|
26069
27020
|
"Minimum seconds between repeated unhealthy reminders",
|
|
26070
27021
|
900
|
|
26071
27022
|
);
|
|
26072
|
-
|
|
27023
|
+
import_args27.default.option(
|
|
26073
27024
|
["l", "loadPerCpuWarn"],
|
|
26074
27025
|
"Alert when 1m load per CPU reaches this value",
|
|
26075
27026
|
String(DEFAULT_THRESHOLDS.loadPerCpuWarn)
|
|
26076
27027
|
);
|
|
26077
|
-
|
|
27028
|
+
import_args27.default.option(
|
|
26078
27029
|
["L", "loadPerCpuRecover"],
|
|
26079
27030
|
"Recovery threshold for 1m load per CPU",
|
|
26080
27031
|
String(DEFAULT_THRESHOLDS.loadPerCpuRecover)
|
|
26081
27032
|
);
|
|
26082
|
-
|
|
27033
|
+
import_args27.default.option(
|
|
26083
27034
|
["m", "memoryWarnPct"],
|
|
26084
27035
|
"Alert when memory used percent reaches this value",
|
|
26085
27036
|
String(DEFAULT_THRESHOLDS.memoryWarnPct)
|
|
26086
27037
|
);
|
|
26087
|
-
|
|
27038
|
+
import_args27.default.option(
|
|
26088
27039
|
["M", "memoryRecoverPct"],
|
|
26089
27040
|
"Recovery threshold for memory used percent",
|
|
26090
27041
|
String(DEFAULT_THRESHOLDS.memoryRecoverPct)
|
|
26091
27042
|
);
|
|
26092
|
-
|
|
27043
|
+
import_args27.default.option(
|
|
26093
27044
|
["D", "diskWarnPct"],
|
|
26094
27045
|
"Alert when disk used percent reaches this value",
|
|
26095
27046
|
String(DEFAULT_THRESHOLDS.diskWarnPct)
|
|
26096
27047
|
);
|
|
26097
|
-
|
|
27048
|
+
import_args27.default.option(
|
|
26098
27049
|
["R", "diskRecoverPct"],
|
|
26099
27050
|
"Recovery threshold for disk used percent",
|
|
26100
27051
|
String(DEFAULT_THRESHOLDS.diskRecoverPct)
|
|
26101
27052
|
);
|
|
26102
|
-
|
|
26103
|
-
const
|
|
26104
|
-
const thresholds = resolveServerHealthThresholds(
|
|
26105
|
-
const diskPath = String(
|
|
27053
|
+
main34 = async () => {
|
|
27054
|
+
const flags31 = import_args27.default.parse(process.argv);
|
|
27055
|
+
const thresholds = resolveServerHealthThresholds(flags31);
|
|
27056
|
+
const diskPath = String(flags31.diskPath || "/");
|
|
26106
27057
|
const snapshot = collectServerHealthSnapshot(diskPath);
|
|
26107
|
-
const stateKey = getStateKey(String(
|
|
27058
|
+
const stateKey = getStateKey(String(flags31.user), snapshot.hostname);
|
|
26108
27059
|
const prevState = await (0, import_redis16.getData)(stateKey, DEFAULT_STATE) ?? DEFAULT_STATE;
|
|
26109
27060
|
const evaluation = evaluateServerHealth(snapshot, thresholds);
|
|
26110
|
-
const cooldownMs = Math.max(60, Number(
|
|
27061
|
+
const cooldownMs = Math.max(60, Number(flags31.cooldownSec)) * 1e3;
|
|
26111
27062
|
const now2 = snapshot.timestamp;
|
|
26112
27063
|
if (!evaluation.isHealthy) {
|
|
26113
27064
|
const reminder = prevState.status === "alerting" && typeof prevState.lastAlertAt === "number" && now2 - prevState.lastAlertAt < cooldownMs ? false : prevState.status === "alerting";
|
|
@@ -26125,7 +27076,7 @@ var init_serverHealth = __esm({
|
|
|
26125
27076
|
prevState,
|
|
26126
27077
|
reminder
|
|
26127
27078
|
});
|
|
26128
|
-
if (
|
|
27079
|
+
if (flags31.printOnly) {
|
|
26129
27080
|
console.log(message);
|
|
26130
27081
|
console.log("");
|
|
26131
27082
|
console.log(
|
|
@@ -26135,7 +27086,7 @@ var init_serverHealth = __esm({
|
|
|
26135
27086
|
})
|
|
26136
27087
|
);
|
|
26137
27088
|
} else {
|
|
26138
|
-
await sendTelegramReport(message, { userName:
|
|
27089
|
+
await sendTelegramReport(message, { userName: flags31.user });
|
|
26139
27090
|
}
|
|
26140
27091
|
}
|
|
26141
27092
|
return;
|
|
@@ -26150,10 +27101,10 @@ var init_serverHealth = __esm({
|
|
|
26150
27101
|
await (0, import_redis16.setData)(stateKey, nextState, { expire: 0 });
|
|
26151
27102
|
if (recovered) {
|
|
26152
27103
|
const message = buildRecoveryMessage({ snapshot, prevState });
|
|
26153
|
-
if (
|
|
27104
|
+
if (flags31.printOnly) {
|
|
26154
27105
|
console.log(message);
|
|
26155
27106
|
} else {
|
|
26156
|
-
await sendTelegramReport(message, { userName:
|
|
27107
|
+
await sendTelegramReport(message, { userName: flags31.user });
|
|
26157
27108
|
}
|
|
26158
27109
|
}
|
|
26159
27110
|
};
|
|
@@ -26976,16 +27927,16 @@ var init_classification = __esm({
|
|
|
26976
27927
|
var runtimeParity_exports = {};
|
|
26977
27928
|
__export(runtimeParity_exports, {
|
|
26978
27929
|
buildRuntimeParityMessage: () => buildRuntimeParityMessage,
|
|
26979
|
-
main: () =>
|
|
27930
|
+
main: () => main35,
|
|
26980
27931
|
runtimeParity: () => runtimeParity
|
|
26981
27932
|
});
|
|
26982
|
-
var
|
|
27933
|
+
var import_config10, import_node_crypto5, import_args28, import_chalk39, import_constants15, import_time7, import_logger6, import_redis17, import_cli11, import_backtest3, import_connectors14, flags27, interval3, projectRoot11, DEFAULT_LOOKBACK_DAYS, DETAIL_LIMIT, TELEGRAM_DETAIL_LIMIT, SUMMARY_TIMEZONE, SUMMARY_TIMEZONE_LABEL, DEFAULT_REPLAY_ENV, escapeHtml4, formatMskDateTime2, resolveParityConnectorName, createConnector, buildReplayAiAnalyses, formatReplayEnvSummary, buildReplayConfig, warmReplayHistory, formatPercent4, formatMinutes2, formatEntryLabel, formatRuntimeEntriesSummary, formatSourceCountsSummary, normalizeSummaryText, pickFirstText, buildMismatchSummaryRows, buildStrategyIssueRows, toSerializableTradeParityEntry, toSerializableSignal, toSerializableRuntimeSignalEvaluation, getRuntimeOnlyLikelyCause, getBacktestOnlyLikelyCause, getRuntimeOnlyRecommendedChecks, getBacktestOnlyRecommendedChecks, buildRuntimeParityMismatchAttachment, printRuntimeDuplicateDetails, printClassifiedBacktestOnlyDetails, printClassifiedRuntimeOnlyDetails, summarizeByStrategy, buildRuntimeParityMessage, buildRuntimeParityNoTargetsMessage, runtimeParity, main35;
|
|
26983
27934
|
var init_runtimeParity2 = __esm({
|
|
26984
27935
|
"src/scripts/runtimeParity.ts"() {
|
|
26985
27936
|
"use strict";
|
|
26986
|
-
|
|
26987
|
-
|
|
26988
|
-
|
|
27937
|
+
import_config10 = require("dotenv/config");
|
|
27938
|
+
import_node_crypto5 = require("crypto");
|
|
27939
|
+
import_args28 = __toESM(require("args"));
|
|
26989
27940
|
import_chalk39 = __toESM(require("chalk"));
|
|
26990
27941
|
import_constants15 = require("@tradejs/core/constants");
|
|
26991
27942
|
import_time7 = require("@tradejs/core/time");
|
|
@@ -27007,45 +27958,45 @@ var init_runtimeParity2 = __esm({
|
|
|
27007
27958
|
init_targets();
|
|
27008
27959
|
init_classification();
|
|
27009
27960
|
init_runtimeModeConfig();
|
|
27010
|
-
|
|
27011
|
-
|
|
27961
|
+
import_args28.default.option(["u", "user"], "Use user config", "root");
|
|
27962
|
+
import_args28.default.option(
|
|
27012
27963
|
["o", "connector"],
|
|
27013
27964
|
"Connector provider or name for parity replay (e.g. bybit, binance, coinbase, custom)",
|
|
27014
27965
|
"bybit"
|
|
27015
27966
|
);
|
|
27016
|
-
|
|
27017
|
-
|
|
27967
|
+
import_args28.default.option(["d", "days"], "Replay window in days", 3);
|
|
27968
|
+
import_args28.default.option(
|
|
27018
27969
|
["b", "startTime"],
|
|
27019
27970
|
"Explicit replay start timestamp (ms or seconds)"
|
|
27020
27971
|
);
|
|
27021
|
-
|
|
27022
|
-
|
|
27023
|
-
|
|
27972
|
+
import_args28.default.option(["e", "endTime"], "Explicit replay end timestamp (ms or seconds)");
|
|
27973
|
+
import_args28.default.option(["s", "strategy"], "Only compare one strategy");
|
|
27974
|
+
import_args28.default.option(
|
|
27024
27975
|
["t", "tickers"],
|
|
27025
27976
|
"Replay comma-separated symbols for all configured strategies"
|
|
27026
27977
|
);
|
|
27027
|
-
|
|
27978
|
+
import_args28.default.option(
|
|
27028
27979
|
["C", "cacheOnly"],
|
|
27029
27980
|
"Do not refresh market history before replay",
|
|
27030
27981
|
false
|
|
27031
27982
|
);
|
|
27032
|
-
|
|
27983
|
+
import_args28.default.option(
|
|
27033
27984
|
["a", "toleranceBars"],
|
|
27034
27985
|
"Allowed entry timestamp drift in bars when matching runtime vs backtest",
|
|
27035
27986
|
1
|
|
27036
27987
|
);
|
|
27037
|
-
|
|
27988
|
+
import_args28.default.option(
|
|
27038
27989
|
"fullUniverse",
|
|
27039
27990
|
"Replay every configured strategy across the full connector universe. By default parity replays only runtime-traded and strategy-results symbols unless --tickers is provided.",
|
|
27040
27991
|
false
|
|
27041
27992
|
);
|
|
27042
|
-
|
|
27993
|
+
import_args28.default.option(
|
|
27043
27994
|
"runtimeGates",
|
|
27044
27995
|
"Force runtime AI/ML gates for all replay targets. Runtime-gated configs are replayed with gates by default.",
|
|
27045
27996
|
false
|
|
27046
27997
|
);
|
|
27047
|
-
|
|
27048
|
-
|
|
27998
|
+
import_args28.default.option(["N", "notify"], "Send parity summary to Telegram", false);
|
|
27999
|
+
import_args28.default.option(["D", "details"], "Print unmatched entry details (capped)", false);
|
|
27049
28000
|
process.argv = normalizeCliArgv(process.argv, {
|
|
27050
28001
|
"-C": "--cacheOnly",
|
|
27051
28002
|
"-D": "--details",
|
|
@@ -27054,7 +28005,7 @@ var init_runtimeParity2 = __esm({
|
|
|
27054
28005
|
"-S": "--startTime",
|
|
27055
28006
|
"-T": "--toleranceBars"
|
|
27056
28007
|
});
|
|
27057
|
-
|
|
28008
|
+
flags27 = import_args28.default.parse(process.argv);
|
|
27058
28009
|
interval3 = "15";
|
|
27059
28010
|
projectRoot11 = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
|
|
27060
28011
|
DEFAULT_LOOKBACK_DAYS = 3;
|
|
@@ -27062,7 +28013,7 @@ var init_runtimeParity2 = __esm({
|
|
|
27062
28013
|
TELEGRAM_DETAIL_LIMIT = 5;
|
|
27063
28014
|
SUMMARY_TIMEZONE = "Europe/Moscow";
|
|
27064
28015
|
SUMMARY_TIMEZONE_LABEL = "MSK";
|
|
27065
|
-
DEFAULT_REPLAY_ENV =
|
|
28016
|
+
DEFAULT_REPLAY_ENV = flags27.runtimeGates ? "PARITY" : "BACKTEST";
|
|
27066
28017
|
escapeHtml4 = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
27067
28018
|
formatMskDateTime2 = (timestamp) => new Intl.DateTimeFormat("ru-RU", {
|
|
27068
28019
|
timeZone: SUMMARY_TIMEZONE,
|
|
@@ -27185,7 +28136,7 @@ var init_runtimeParity2 = __esm({
|
|
|
27185
28136
|
strategyConfig,
|
|
27186
28137
|
env: resolveReplayStrategyEnv({
|
|
27187
28138
|
strategyConfig,
|
|
27188
|
-
forceRuntimeGates: Boolean(
|
|
28139
|
+
forceRuntimeGates: Boolean(flags27.runtimeGates)
|
|
27189
28140
|
}),
|
|
27190
28141
|
interval: interval3,
|
|
27191
28142
|
makeOrders: true,
|
|
@@ -27944,48 +28895,48 @@ var init_runtimeParity2 = __esm({
|
|
|
27944
28895
|
].join("\n");
|
|
27945
28896
|
runtimeParity = async () => {
|
|
27946
28897
|
const window2 = resolveTimeWindow({
|
|
27947
|
-
days:
|
|
27948
|
-
startTime:
|
|
27949
|
-
endTime:
|
|
28898
|
+
days: flags27.days ?? DEFAULT_LOOKBACK_DAYS,
|
|
28899
|
+
startTime: flags27.startTime,
|
|
28900
|
+
endTime: flags27.endTime,
|
|
27950
28901
|
defaultStartMs: Date.now() - DEFAULT_LOOKBACK_DAYS * 24 * 60 * 60 * 1e3,
|
|
27951
28902
|
defaultEndMs: Date.now()
|
|
27952
28903
|
});
|
|
27953
28904
|
const toleranceBars = Math.max(
|
|
27954
28905
|
0,
|
|
27955
|
-
Number.parseInt(String(
|
|
28906
|
+
Number.parseInt(String(flags27.toleranceBars ?? 1), 10) || 0
|
|
27956
28907
|
);
|
|
27957
28908
|
const toleranceMs = toleranceBars * 15 * 60 * 1e3;
|
|
27958
|
-
const connectorName = await resolveParityConnectorName(
|
|
27959
|
-
const requestedSymbols = parseSymbolsFromCLI2(String(
|
|
28909
|
+
const connectorName = await resolveParityConnectorName(flags27.connector);
|
|
28910
|
+
const requestedSymbols = parseSymbolsFromCLI2(String(flags27.tickers || ""));
|
|
27960
28911
|
const requestedSymbolSet = requestedSymbols.length ? new Set(requestedSymbols) : null;
|
|
27961
28912
|
let replayErrors = [];
|
|
27962
28913
|
try {
|
|
27963
28914
|
const parityConnector = await createConnector({
|
|
27964
|
-
userName:
|
|
28915
|
+
userName: flags27.user,
|
|
27965
28916
|
connectorName
|
|
27966
28917
|
});
|
|
27967
28918
|
const [allRuntimeTrades, allRuntimeSignals, allRuntimeSignalEvaluations] = await Promise.all([
|
|
27968
|
-
loadRuntimeTrades(
|
|
28919
|
+
loadRuntimeTrades(flags27.user, {
|
|
27969
28920
|
startTime: window2.start,
|
|
27970
28921
|
endTime: window2.end
|
|
27971
28922
|
}),
|
|
27972
|
-
loadRuntimeSignals(
|
|
28923
|
+
loadRuntimeSignals(flags27.user, {
|
|
27973
28924
|
startTime: window2.start,
|
|
27974
28925
|
endTime: window2.end
|
|
27975
28926
|
}),
|
|
27976
|
-
loadRuntimeSignalEvaluations(
|
|
28927
|
+
loadRuntimeSignalEvaluations(flags27.user, {
|
|
27977
28928
|
startTime: window2.start,
|
|
27978
28929
|
endTime: window2.end
|
|
27979
28930
|
})
|
|
27980
28931
|
]);
|
|
27981
28932
|
const runtimeTrades = allRuntimeTrades.filter(
|
|
27982
|
-
(trade) => trade.entryTimestamp >= window2.start && trade.entryTimestamp <= window2.end && (!
|
|
28933
|
+
(trade) => trade.entryTimestamp >= window2.start && trade.entryTimestamp <= window2.end && (!flags27.strategy || trade.strategy === flags27.strategy) && (!requestedSymbolSet || requestedSymbolSet.has(trade.symbol))
|
|
27983
28934
|
);
|
|
27984
28935
|
const runtimeSignals = allRuntimeSignals.filter(
|
|
27985
|
-
(signal) => signal.timestamp >= window2.start && signal.timestamp <= window2.end && (!
|
|
28936
|
+
(signal) => signal.timestamp >= window2.start && signal.timestamp <= window2.end && (!flags27.strategy || signal.strategy === flags27.strategy) && (!requestedSymbolSet || requestedSymbolSet.has(signal.symbol))
|
|
27986
28937
|
);
|
|
27987
28938
|
const runtimeSignalEvaluations = allRuntimeSignalEvaluations.filter(
|
|
27988
|
-
(evaluation) => evaluation.timestamp >= window2.start && evaluation.timestamp <= window2.end && (!
|
|
28939
|
+
(evaluation) => evaluation.timestamp >= window2.start && evaluation.timestamp <= window2.end && (!flags27.strategy || evaluation.strategy === flags27.strategy) && (!requestedSymbolSet || requestedSymbolSet.has(evaluation.symbol))
|
|
27989
28940
|
);
|
|
27990
28941
|
const connectorSymbols = requestedSymbols.length ? requestedSymbols : await (0, import_cli11.getTickers)(parityConnector);
|
|
27991
28942
|
const replayInputsIndex = buildReplayInputsIndex({
|
|
@@ -27993,29 +28944,29 @@ var init_runtimeParity2 = __esm({
|
|
|
27993
28944
|
runtimeSignalEvaluations
|
|
27994
28945
|
});
|
|
27995
28946
|
const replayTargets = await buildReplayTargets({
|
|
27996
|
-
userName:
|
|
28947
|
+
userName: flags27.user,
|
|
27997
28948
|
runtimeTrades,
|
|
27998
28949
|
connectorSymbols,
|
|
27999
|
-
strategyFilter: String(
|
|
28950
|
+
strategyFilter: String(flags27.strategy || "").trim() || void 0,
|
|
28000
28951
|
explicitSymbols: requestedSymbols,
|
|
28001
|
-
includeConnectorUniverse: Boolean(
|
|
28952
|
+
includeConnectorUniverse: Boolean(flags27.fullUniverse)
|
|
28002
28953
|
});
|
|
28003
28954
|
if (!replayTargets.length) {
|
|
28004
28955
|
console.log(
|
|
28005
28956
|
import_chalk39.default.yellow(
|
|
28006
|
-
`No replay targets found for ${
|
|
28957
|
+
`No replay targets found for ${flags27.user} in ${(0, import_time7.formatUnix)(window2.start)} -> ${(0, import_time7.formatUnix)(window2.end)}.`
|
|
28007
28958
|
)
|
|
28008
28959
|
);
|
|
28009
|
-
if (
|
|
28960
|
+
if (flags27.notify) {
|
|
28010
28961
|
await sendTelegramReport(
|
|
28011
28962
|
buildRuntimeParityNoTargetsMessage({
|
|
28012
28963
|
window: window2,
|
|
28013
28964
|
connectorName,
|
|
28014
28965
|
replayEnv: DEFAULT_REPLAY_ENV,
|
|
28015
|
-
runtimeGatesEnabled: Boolean(
|
|
28016
|
-
userName: String(
|
|
28966
|
+
runtimeGatesEnabled: Boolean(flags27.runtimeGates),
|
|
28967
|
+
userName: String(flags27.user)
|
|
28017
28968
|
}),
|
|
28018
|
-
{ userName:
|
|
28969
|
+
{ userName: flags27.user }
|
|
28019
28970
|
);
|
|
28020
28971
|
}
|
|
28021
28972
|
return;
|
|
@@ -28031,13 +28982,13 @@ var init_runtimeParity2 = __esm({
|
|
|
28031
28982
|
).size;
|
|
28032
28983
|
console.log(
|
|
28033
28984
|
import_chalk39.default.gray(
|
|
28034
|
-
`Replay queue: ${replayTargets.length} target(s), ${replaySymbolsCount} symbol(s), mode=${
|
|
28985
|
+
`Replay queue: ${replayTargets.length} target(s), ${replaySymbolsCount} symbol(s), mode=${flags27.fullUniverse ? "full-universe" : requestedSymbols.length ? "explicit-tickers" : "runtime+results"}`
|
|
28035
28986
|
)
|
|
28036
28987
|
);
|
|
28037
28988
|
const replayConfigs = /* @__PURE__ */ new Map();
|
|
28038
28989
|
for (const target of replayTargets) {
|
|
28039
28990
|
const replayConfig = await buildReplayConfig({
|
|
28040
|
-
userName:
|
|
28991
|
+
userName: flags27.user,
|
|
28041
28992
|
strategy: target.strategy,
|
|
28042
28993
|
symbol: target.symbol,
|
|
28043
28994
|
replayInputsIndex,
|
|
@@ -28054,21 +29005,22 @@ var init_runtimeParity2 = __esm({
|
|
|
28054
29005
|
);
|
|
28055
29006
|
await prepareMarketContextForRun({
|
|
28056
29007
|
mode: "parity",
|
|
28057
|
-
userName:
|
|
29008
|
+
userName: flags27.user,
|
|
28058
29009
|
projectRoot: projectRoot11,
|
|
28059
29010
|
symbols: replayTargets.map((target) => target.symbol),
|
|
28060
29011
|
interval: interval3,
|
|
28061
29012
|
startMs: window2.start,
|
|
28062
29013
|
endMs: window2.end,
|
|
28063
29014
|
preloadStartMs: preloadStart,
|
|
28064
|
-
cacheOnly: Boolean(
|
|
29015
|
+
cacheOnly: Boolean(flags27.cacheOnly),
|
|
28065
29016
|
aiEnabled: hasParityAiTargets,
|
|
28066
29017
|
mlEnabled: hasParityMlTargets,
|
|
29018
|
+
strategyNames: replayTargets.map((target) => target.strategy),
|
|
28067
29019
|
log: (message) => console.log(import_chalk39.default.gray(message))
|
|
28068
29020
|
});
|
|
28069
|
-
if (!
|
|
29021
|
+
if (!flags27.cacheOnly) {
|
|
28070
29022
|
await warmReplayHistory({
|
|
28071
|
-
userName:
|
|
29023
|
+
userName: flags27.user,
|
|
28072
29024
|
connectorName,
|
|
28073
29025
|
targets: replayTargets,
|
|
28074
29026
|
preloadStart,
|
|
@@ -28095,15 +29047,15 @@ var init_runtimeParity2 = __esm({
|
|
|
28095
29047
|
);
|
|
28096
29048
|
}
|
|
28097
29049
|
const result = await (0, import_backtest3.testing)({
|
|
28098
|
-
userName:
|
|
29050
|
+
userName: flags27.user,
|
|
28099
29051
|
symbol: target.symbol,
|
|
28100
29052
|
options: {
|
|
28101
29053
|
start: window2.start,
|
|
28102
29054
|
end: window2.end
|
|
28103
29055
|
},
|
|
28104
|
-
name: `${target.symbol}_${target.strategy}_${(0,
|
|
28105
|
-
testId: (0,
|
|
28106
|
-
testSuiteId: (0,
|
|
29056
|
+
name: `${target.symbol}_${target.strategy}_${(0, import_node_crypto5.randomUUID)().slice(0, 8)}`,
|
|
29057
|
+
testId: (0, import_node_crypto5.randomUUID)().slice(0, 8),
|
|
29058
|
+
testSuiteId: (0, import_node_crypto5.randomUUID)().slice(0, 8),
|
|
28107
29059
|
strategyName: target.strategy,
|
|
28108
29060
|
strategyConfig: replayConfig,
|
|
28109
29061
|
connectorName,
|
|
@@ -28125,7 +29077,7 @@ var init_runtimeParity2 = __esm({
|
|
|
28125
29077
|
} finally {
|
|
28126
29078
|
(0, import_backtest3.releaseTestingSymbolCache)({
|
|
28127
29079
|
cwd: projectRoot11,
|
|
28128
|
-
userName:
|
|
29080
|
+
userName: flags27.user,
|
|
28129
29081
|
connectorName,
|
|
28130
29082
|
symbol: target.symbol
|
|
28131
29083
|
});
|
|
@@ -28170,7 +29122,7 @@ var init_runtimeParity2 = __esm({
|
|
|
28170
29122
|
);
|
|
28171
29123
|
console.log(`Connector: ${connectorName}`);
|
|
28172
29124
|
console.log(
|
|
28173
|
-
`Replay env: ${replayEnvSummary}${
|
|
29125
|
+
`Replay env: ${replayEnvSummary}${flags27.runtimeGates || replayEnvSummary.includes("PARITY") ? " (runtime AI/ML gates enabled)" : " (core/backtest gates only)"}`
|
|
28174
29126
|
);
|
|
28175
29127
|
console.log(
|
|
28176
29128
|
`Tolerance: ${toleranceBars} bar(s) / ${(toleranceMs / 6e4).toFixed(0)}m`
|
|
@@ -28244,7 +29196,7 @@ var init_runtimeParity2 = __esm({
|
|
|
28244
29196
|
`Strategies: clean ${strategyRows.length}/${strategyRows.length}`
|
|
28245
29197
|
);
|
|
28246
29198
|
}
|
|
28247
|
-
if (runtimeGateWarningCounts.size && !
|
|
29199
|
+
if (runtimeGateWarningCounts.size && !flags27.runtimeGates) {
|
|
28248
29200
|
console.log("");
|
|
28249
29201
|
console.log(import_chalk39.default.yellow("Warnings"));
|
|
28250
29202
|
for (const [strategy, count] of [
|
|
@@ -28265,12 +29217,12 @@ var init_runtimeParity2 = __esm({
|
|
|
28265
29217
|
console.log(`- ... ${replayErrors.length - DETAIL_LIMIT} more`);
|
|
28266
29218
|
}
|
|
28267
29219
|
}
|
|
28268
|
-
if (
|
|
29220
|
+
if (flags27.details) {
|
|
28269
29221
|
printRuntimeDuplicateDetails(runtimeDedupe.duplicateGroups);
|
|
28270
29222
|
printClassifiedRuntimeOnlyDetails(classifiedRuntimeOnly);
|
|
28271
29223
|
printClassifiedBacktestOnlyDetails(classifiedBacktestOnly);
|
|
28272
29224
|
}
|
|
28273
|
-
if (
|
|
29225
|
+
if (flags27.notify) {
|
|
28274
29226
|
const mismatchAttachment = buildRuntimeParityMismatchAttachment({
|
|
28275
29227
|
window: window2,
|
|
28276
29228
|
connectorName,
|
|
@@ -28299,7 +29251,7 @@ var init_runtimeParity2 = __esm({
|
|
|
28299
29251
|
window: window2,
|
|
28300
29252
|
connectorName,
|
|
28301
29253
|
replayEnv: replayEnvSummary,
|
|
28302
|
-
runtimeGatesEnabled: Boolean(
|
|
29254
|
+
runtimeGatesEnabled: Boolean(flags27.runtimeGates) || replayEnvSummary.includes("PARITY"),
|
|
28303
29255
|
toleranceBars,
|
|
28304
29256
|
toleranceMs,
|
|
28305
29257
|
replayTargetsCount: replayTargets.length,
|
|
@@ -28321,7 +29273,7 @@ var init_runtimeParity2 = __esm({
|
|
|
28321
29273
|
runtimeGateWarningCounts
|
|
28322
29274
|
}),
|
|
28323
29275
|
{
|
|
28324
|
-
userName:
|
|
29276
|
+
userName: flags27.user,
|
|
28325
29277
|
attachments: mismatchAttachment ? [mismatchAttachment] : void 0
|
|
28326
29278
|
}
|
|
28327
29279
|
);
|
|
@@ -28330,7 +29282,7 @@ var init_runtimeParity2 = __esm({
|
|
|
28330
29282
|
(0, import_backtest3.resetTestingKlineCache)(projectRoot11);
|
|
28331
29283
|
}
|
|
28332
29284
|
};
|
|
28333
|
-
|
|
29285
|
+
main35 = runtimeParity;
|
|
28334
29286
|
}
|
|
28335
29287
|
});
|
|
28336
29288
|
|
|
@@ -28400,14 +29352,14 @@ var init_hooks = __esm({
|
|
|
28400
29352
|
});
|
|
28401
29353
|
|
|
28402
29354
|
// src/lib/signals/runtimeStrategies.ts
|
|
28403
|
-
var import_logger7, import_redis18, import_tradingAccounts2,
|
|
29355
|
+
var import_logger7, import_redis18, import_tradingAccounts2, import_strategies11, loadRuntimeStrategies;
|
|
28404
29356
|
var init_runtimeStrategies = __esm({
|
|
28405
29357
|
"src/lib/signals/runtimeStrategies.ts"() {
|
|
28406
29358
|
"use strict";
|
|
28407
29359
|
import_logger7 = require("@tradejs/infra/logger");
|
|
28408
29360
|
import_redis18 = require("@tradejs/infra/redis");
|
|
28409
29361
|
import_tradingAccounts2 = require("@tradejs/infra/tradingAccounts");
|
|
28410
|
-
|
|
29362
|
+
import_strategies11 = require("@tradejs/node/strategies");
|
|
28411
29363
|
init_runtimeRedis();
|
|
28412
29364
|
loadRuntimeStrategies = async ({
|
|
28413
29365
|
userName: userName2,
|
|
@@ -28451,7 +29403,7 @@ var init_runtimeStrategies = __esm({
|
|
|
28451
29403
|
});
|
|
28452
29404
|
const effectiveAccountId = resolvedAccount?.id ?? requestedAccountId;
|
|
28453
29405
|
const [strategyCreator, strategyResults] = await Promise.all([
|
|
28454
|
-
(0,
|
|
29406
|
+
(0, import_strategies11.getStrategyCreator)(strategyName, projectRoot14),
|
|
28455
29407
|
(0, import_redis18.getData)(import_redis18.redisKeys.strategyResults(userName2, strategyName), {})
|
|
28456
29408
|
]);
|
|
28457
29409
|
if (!strategyCreator) {
|
|
@@ -28554,14 +29506,16 @@ var init_skipStats = __esm({
|
|
|
28554
29506
|
});
|
|
28555
29507
|
|
|
28556
29508
|
// src/lib/runtimeLineage.ts
|
|
28557
|
-
var import_node_child_process6,
|
|
29509
|
+
var import_node_child_process6, import_node_crypto6, import_promises16, import_node_path10, import_types6, import_strategies12, RUNTIME_CONTEXT_ENV_KEYS, RUNTIME_CONTEXT_DATA_MODEL, normalizeForStableJson2, resolveRuntimeMaxLossValue, fingerprintRuntimeValue, gitLineageCache, gateFingerprintCache, binanceBreadthFingerprintCache, snapshotFingerprintCache, resolveGitLineage, readOptionalFile2, resolveBinanceBreadthFingerprint, resolveSnapshotFingerprint, resolveGateFingerprint2, buildRuntimeLineage, runtimeLineageKey, runtimeLineagesMatch;
|
|
28558
29510
|
var init_runtimeLineage = __esm({
|
|
28559
29511
|
"src/lib/runtimeLineage.ts"() {
|
|
28560
29512
|
"use strict";
|
|
28561
29513
|
import_node_child_process6 = require("child_process");
|
|
28562
|
-
|
|
29514
|
+
import_node_crypto6 = require("crypto");
|
|
28563
29515
|
import_promises16 = __toESM(require("fs/promises"));
|
|
28564
29516
|
import_node_path10 = __toESM(require("path"));
|
|
29517
|
+
import_types6 = require("@tradejs/types");
|
|
29518
|
+
import_strategies12 = require("@tradejs/node/strategies");
|
|
28565
29519
|
RUNTIME_CONTEXT_ENV_KEYS = [
|
|
28566
29520
|
"AI_MODE",
|
|
28567
29521
|
"MIN_AI_QUALITY",
|
|
@@ -28579,7 +29533,12 @@ var init_runtimeLineage = __esm({
|
|
|
28579
29533
|
"COINMARKETCAP_CONTEXT_HISTORICAL_ACCESS_MONTHS",
|
|
28580
29534
|
"COINMARKETCAP_CONTEXT_BACKFILL_ENABLED",
|
|
28581
29535
|
"COINMARKETCAP_CONTEXT_BACKFILL_MAX_DAYS",
|
|
28582
|
-
"COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS"
|
|
29536
|
+
"COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS",
|
|
29537
|
+
"HYPERLIQUID_WHALE_CONTEXT_ENABLED",
|
|
29538
|
+
"HYPERLIQUID_WHALE_BACKFILL_ENABLED",
|
|
29539
|
+
"HYPERLIQUID_WHALE_MIN_COVERAGE_PCT",
|
|
29540
|
+
"HYPERLIQUID_WHALE_CONCURRENCY",
|
|
29541
|
+
"HYPERLIQUID_WHALE_RATE_LIMIT_WEIGHT"
|
|
28583
29542
|
];
|
|
28584
29543
|
RUNTIME_CONTEXT_DATA_MODEL = {
|
|
28585
29544
|
derivativesSourceIntervals: "15m",
|
|
@@ -28589,7 +29548,11 @@ var init_runtimeLineage = __esm({
|
|
|
28589
29548
|
coinMarketCapAvailabilityModel: "closed-interval-all-daily",
|
|
28590
29549
|
coinMarketCapDataModelVersion: 3,
|
|
28591
29550
|
binanceBreadthUniverseModel: "fixed-versioned-top5-top10-top30-top50-top100",
|
|
28592
|
-
binanceBreadthDataModelVersion: 2
|
|
29551
|
+
binanceBreadthDataModelVersion: 2,
|
|
29552
|
+
hyperliquidWhaleCanonicalInterval: "1m",
|
|
29553
|
+
hyperliquidWhaleGateMinNotionalUsd: 5e4,
|
|
29554
|
+
hyperliquidWhaleMinCoveragePct: 0.8,
|
|
29555
|
+
hyperliquidWhaleDataModelVersion: import_types6.HYPERLIQUID_WHALE_DATA_MODEL_VERSION
|
|
28593
29556
|
};
|
|
28594
29557
|
normalizeForStableJson2 = (value) => {
|
|
28595
29558
|
if (Array.isArray(value)) {
|
|
@@ -28602,10 +29565,24 @@ var init_runtimeLineage = __esm({
|
|
|
28602
29565
|
}
|
|
28603
29566
|
return value;
|
|
28604
29567
|
};
|
|
28605
|
-
|
|
29568
|
+
resolveRuntimeMaxLossValue = (config) => {
|
|
29569
|
+
if (!config || typeof config !== "object" || Array.isArray(config)) {
|
|
29570
|
+
return null;
|
|
29571
|
+
}
|
|
29572
|
+
const strategyConfig = config.strategyConfig;
|
|
29573
|
+
if (!strategyConfig || typeof strategyConfig !== "object" || Array.isArray(strategyConfig)) {
|
|
29574
|
+
return null;
|
|
29575
|
+
}
|
|
29576
|
+
const maxLossValue = Number(
|
|
29577
|
+
strategyConfig.MAX_LOSS_VALUE
|
|
29578
|
+
);
|
|
29579
|
+
return Number.isFinite(maxLossValue) ? maxLossValue : null;
|
|
29580
|
+
};
|
|
29581
|
+
fingerprintRuntimeValue = (value) => (0, import_node_crypto6.createHash)("sha256").update(JSON.stringify(normalizeForStableJson2(value))).digest("hex").slice(0, 16);
|
|
28606
29582
|
gitLineageCache = /* @__PURE__ */ new Map();
|
|
28607
29583
|
gateFingerprintCache = /* @__PURE__ */ new Map();
|
|
28608
29584
|
binanceBreadthFingerprintCache = /* @__PURE__ */ new Map();
|
|
29585
|
+
snapshotFingerprintCache = /* @__PURE__ */ new Map();
|
|
28609
29586
|
resolveGitLineage = (projectRoot14) => {
|
|
28610
29587
|
const cached = gitLineageCache.get(projectRoot14);
|
|
28611
29588
|
if (cached) {
|
|
@@ -28679,6 +29656,24 @@ var init_runtimeLineage = __esm({
|
|
|
28679
29656
|
binanceBreadthFingerprintCache.set(projectRoot14, result);
|
|
28680
29657
|
return result;
|
|
28681
29658
|
};
|
|
29659
|
+
resolveSnapshotFingerprint = async (projectRoot14, relativePath) => {
|
|
29660
|
+
const cacheKey = `${projectRoot14}:${relativePath}`;
|
|
29661
|
+
if (snapshotFingerprintCache.has(cacheKey)) {
|
|
29662
|
+
return snapshotFingerprintCache.get(cacheKey) ?? null;
|
|
29663
|
+
}
|
|
29664
|
+
const content = await readOptionalFile2(import_node_path10.default.join(projectRoot14, relativePath));
|
|
29665
|
+
let result = null;
|
|
29666
|
+
if (content) {
|
|
29667
|
+
try {
|
|
29668
|
+
const parsed = JSON.parse(content.toString("utf8"));
|
|
29669
|
+
result = typeof parsed.fingerprint === "string" && parsed.fingerprint.trim() ? parsed.fingerprint.trim() : null;
|
|
29670
|
+
} catch {
|
|
29671
|
+
result = null;
|
|
29672
|
+
}
|
|
29673
|
+
}
|
|
29674
|
+
snapshotFingerprintCache.set(cacheKey, result);
|
|
29675
|
+
return result;
|
|
29676
|
+
};
|
|
28682
29677
|
resolveGateFingerprint2 = async ({
|
|
28683
29678
|
projectRoot: projectRoot14,
|
|
28684
29679
|
strategyName,
|
|
@@ -28694,7 +29689,8 @@ var init_runtimeLineage = __esm({
|
|
|
28694
29689
|
`packages/strategies/src/${strategyName}/guardrails.ts`,
|
|
28695
29690
|
`packages/strategies/src/${strategyName}/pockets.ts`,
|
|
28696
29691
|
`packages/strategies/src/${strategyName}/config.ts`,
|
|
28697
|
-
"packages/node/src/ai.ts"
|
|
29692
|
+
"packages/node/src/ai.ts",
|
|
29693
|
+
"packages/core/src/utils/strategyHelpers/signalBuilders.ts"
|
|
28698
29694
|
];
|
|
28699
29695
|
const optionalEntries = await Promise.all(
|
|
28700
29696
|
relativeCandidates.map(async (relativePath) => {
|
|
@@ -28710,7 +29706,7 @@ var init_runtimeLineage = __esm({
|
|
|
28710
29706
|
sourceEntries.push(entry);
|
|
28711
29707
|
}
|
|
28712
29708
|
}
|
|
28713
|
-
const hash = (0,
|
|
29709
|
+
const hash = (0, import_node_crypto6.createHash)("sha256");
|
|
28714
29710
|
hash.update(strategyName);
|
|
28715
29711
|
if (sourceEntries.length) {
|
|
28716
29712
|
for (const entry of sourceEntries) {
|
|
@@ -28720,9 +29716,9 @@ var init_runtimeLineage = __esm({
|
|
|
28720
29716
|
} else {
|
|
28721
29717
|
hash.update(gitSha ?? "unknown-git-sha");
|
|
28722
29718
|
}
|
|
28723
|
-
const
|
|
28724
|
-
gateFingerprintCache.set(cacheKey,
|
|
28725
|
-
return
|
|
29719
|
+
const fingerprint2 = hash.digest("hex").slice(0, 16);
|
|
29720
|
+
gateFingerprintCache.set(cacheKey, fingerprint2);
|
|
29721
|
+
return fingerprint2;
|
|
28726
29722
|
};
|
|
28727
29723
|
buildRuntimeLineage = async ({
|
|
28728
29724
|
projectRoot: projectRoot14,
|
|
@@ -28732,12 +29728,22 @@ var init_runtimeLineage = __esm({
|
|
|
28732
29728
|
env = process.env
|
|
28733
29729
|
}) => {
|
|
28734
29730
|
const git = resolveGitLineage(projectRoot14);
|
|
29731
|
+
const hyperliquidPerpUniverseFingerprint = typeof import_strategies12.getHyperliquidPerpUniverseSnapshot === "function" ? (0, import_strategies12.getHyperliquidPerpUniverseSnapshot)().fingerprint : await resolveSnapshotFingerprint(
|
|
29732
|
+
projectRoot14,
|
|
29733
|
+
"packages/node/src/config/hyperliquidPerpUniverse.json"
|
|
29734
|
+
);
|
|
29735
|
+
const hyperliquidWhaleRegistryFingerprint = typeof import_strategies12.getHyperliquidWhaleRegistrySnapshot === "function" ? (0, import_strategies12.getHyperliquidWhaleRegistrySnapshot)().fingerprint : await resolveSnapshotFingerprint(
|
|
29736
|
+
projectRoot14,
|
|
29737
|
+
"packages/node/src/config/hyperliquidWhales.json"
|
|
29738
|
+
);
|
|
28735
29739
|
const context = {
|
|
28736
29740
|
...Object.fromEntries(
|
|
28737
29741
|
RUNTIME_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
|
|
28738
29742
|
),
|
|
28739
29743
|
...RUNTIME_CONTEXT_DATA_MODEL,
|
|
28740
29744
|
binanceBreadthUniverseFingerprint: await resolveBinanceBreadthFingerprint(projectRoot14),
|
|
29745
|
+
hyperliquidPerpUniverseFingerprint,
|
|
29746
|
+
hyperliquidWhaleRegistryFingerprint,
|
|
28741
29747
|
...runContext
|
|
28742
29748
|
};
|
|
28743
29749
|
return {
|
|
@@ -28749,7 +29755,8 @@ var init_runtimeLineage = __esm({
|
|
|
28749
29755
|
gitSha: git.gitSha
|
|
28750
29756
|
}),
|
|
28751
29757
|
configFingerprint: fingerprintRuntimeValue(config),
|
|
28752
|
-
contextFingerprint: fingerprintRuntimeValue(context)
|
|
29758
|
+
contextFingerprint: fingerprintRuntimeValue(context),
|
|
29759
|
+
maxLossValue: resolveRuntimeMaxLossValue(config)
|
|
28753
29760
|
};
|
|
28754
29761
|
};
|
|
28755
29762
|
runtimeLineageKey = (lineage) => [
|
|
@@ -29139,13 +30146,13 @@ var init_daemon = __esm({
|
|
|
29139
30146
|
});
|
|
29140
30147
|
|
|
29141
30148
|
// src/lib/signals/klineFeed.ts
|
|
29142
|
-
var import_connectors15, import_logger9,
|
|
30149
|
+
var import_connectors15, import_logger9, import_timescale12, FLUSH_DELAY_MS, normalizeSymbols2, createSignalsKlineFeed;
|
|
29143
30150
|
var init_klineFeed = __esm({
|
|
29144
30151
|
"src/lib/signals/klineFeed.ts"() {
|
|
29145
30152
|
"use strict";
|
|
29146
30153
|
import_connectors15 = require("@tradejs/connectors");
|
|
29147
30154
|
import_logger9 = require("@tradejs/infra/logger");
|
|
29148
|
-
|
|
30155
|
+
import_timescale12 = require("@tradejs/infra/timescale");
|
|
29149
30156
|
init_klineEvents();
|
|
29150
30157
|
FLUSH_DELAY_MS = 100;
|
|
29151
30158
|
normalizeSymbols2 = (symbols) => [...new Set(symbols.map((symbol) => symbol.trim().toUpperCase()))].filter(
|
|
@@ -29158,9 +30165,9 @@ var init_klineFeed = __esm({
|
|
|
29158
30165
|
publish = publishMarketKlineEvent,
|
|
29159
30166
|
persist = async (rows) => {
|
|
29160
30167
|
const intervalMinutes = Number(interval5);
|
|
29161
|
-
await (0,
|
|
30168
|
+
await (0, import_timescale12.upsertCandles)(
|
|
29162
30169
|
rows.flatMap(
|
|
29163
|
-
({ symbol, candle }) => (0,
|
|
30170
|
+
({ symbol, candle }) => (0, import_timescale12.toRows)("bybit", symbol, intervalMinutes, [candle])
|
|
29164
30171
|
)
|
|
29165
30172
|
);
|
|
29166
30173
|
},
|
|
@@ -29273,25 +30280,25 @@ var init_klineFeed = __esm({
|
|
|
29273
30280
|
var signals_exports = {};
|
|
29274
30281
|
__export(signals_exports, {
|
|
29275
30282
|
createSignalsSession: () => createSignalsSession,
|
|
29276
|
-
main: () =>
|
|
30283
|
+
main: () => main36,
|
|
29277
30284
|
signals: () => signals,
|
|
29278
30285
|
signalsConfiguredScopesOnce: () => signalsConfiguredScopesOnce,
|
|
29279
30286
|
signalsDaemon: () => signalsDaemon
|
|
29280
30287
|
});
|
|
29281
|
-
var
|
|
30288
|
+
var import_config11, import_args29, import_progress10, import_chalk41, import_connectors16, import_cli12, import_async5, import_strategies13, import_constants16, import_strategies14, import_time8, import_logger10, import_redis20, import_tradingAccounts3, SLOW_SIGNALS_WARNING_MS, projectRoot12, flags28, interval4, intervalMs, formatDuration3, resolvePositiveInteger2, resolveNonNegativeInteger, resolveSignalsDaemonMaxLiveBars, isSignalsKlineWsEnabled, resolveSignalsKlineWsWaitMs, createDefaultSignalsLifecycle, timeOperation3, formatSlowSignalsWarning, resolveSignalsConnectorName, createSignalsSession, findSignals, signals, loadConfiguredSignalsScopes, hasExplicitSignalsScope, signalsConfiguredScopesOnce, signalsDaemon, main36;
|
|
29282
30289
|
var init_signals = __esm({
|
|
29283
30290
|
"src/scripts/signals.ts"() {
|
|
29284
30291
|
"use strict";
|
|
29285
|
-
|
|
29286
|
-
|
|
30292
|
+
import_config11 = require("dotenv/config");
|
|
30293
|
+
import_args29 = __toESM(require("args"));
|
|
29287
30294
|
import_progress10 = __toESM(require("progress"));
|
|
29288
30295
|
import_chalk41 = __toESM(require("chalk"));
|
|
29289
30296
|
import_connectors16 = require("@tradejs/node/connectors");
|
|
29290
30297
|
import_cli12 = require("@tradejs/node/cli");
|
|
29291
30298
|
import_async5 = require("@tradejs/core/async");
|
|
29292
|
-
|
|
30299
|
+
import_strategies13 = require("@tradejs/core/strategies");
|
|
29293
30300
|
import_constants16 = require("@tradejs/core/constants");
|
|
29294
|
-
|
|
30301
|
+
import_strategies14 = require("@tradejs/node/strategies");
|
|
29295
30302
|
import_time8 = require("@tradejs/core/time");
|
|
29296
30303
|
import_logger10 = require("@tradejs/infra/logger");
|
|
29297
30304
|
import_redis20 = require("@tradejs/infra/redis");
|
|
@@ -29311,46 +30318,46 @@ var init_signals = __esm({
|
|
|
29311
30318
|
init_runtimeLifecycle();
|
|
29312
30319
|
init_daemon();
|
|
29313
30320
|
init_klineFeed();
|
|
29314
|
-
|
|
29315
|
-
|
|
29316
|
-
|
|
29317
|
-
|
|
29318
|
-
|
|
29319
|
-
|
|
29320
|
-
|
|
29321
|
-
|
|
29322
|
-
|
|
29323
|
-
|
|
29324
|
-
|
|
30321
|
+
import_args29.default.option(["t", "tickers"], "Selected tickers");
|
|
30322
|
+
import_args29.default.option(["e", "exclude"], "Exclude tickers from tests");
|
|
30323
|
+
import_args29.default.option(["l", "tickersLimit"], "Tickers limit");
|
|
30324
|
+
import_args29.default.option(["f", "timeframe"], "Timeframe", 15);
|
|
30325
|
+
import_args29.default.option(["m", "makeOrders"], "Make orders");
|
|
30326
|
+
import_args29.default.option(["N", "notify"], "Send message in Telegram", false);
|
|
30327
|
+
import_args29.default.option(["S", "skipScreenshots"], "Skip screenshot generation", false);
|
|
30328
|
+
import_args29.default.option(["u", "updateOnly"], "Only update tickers history", false);
|
|
30329
|
+
import_args29.default.option(["C", "cacheOnly"], "Do not update tickers history", false);
|
|
30330
|
+
import_args29.default.option(["L", "showTickersList"], "Just show only ticker list", false);
|
|
30331
|
+
import_args29.default.option(
|
|
29325
30332
|
["p", "parallel"],
|
|
29326
30333
|
"Signal evaluation worker count",
|
|
29327
30334
|
Number(process.env.SIGNALS_PARALLEL || 4)
|
|
29328
30335
|
);
|
|
29329
|
-
|
|
30336
|
+
import_args29.default.option(
|
|
29330
30337
|
["R", "showSkipStats"],
|
|
29331
30338
|
"Show aggregated skip stats by strategy",
|
|
29332
30339
|
false
|
|
29333
30340
|
);
|
|
29334
|
-
|
|
29335
|
-
|
|
29336
|
-
|
|
29337
|
-
|
|
30341
|
+
import_args29.default.option(["c", "chunk"], "Split by chunks, ex. 1/3");
|
|
30342
|
+
import_args29.default.option(["U", "user"], "Use user confg", "root");
|
|
30343
|
+
import_args29.default.option(["w", "watch"], "Keep signals running on candle boundaries", false);
|
|
30344
|
+
import_args29.default.option(
|
|
29338
30345
|
["d", "settleDelayMs"],
|
|
29339
30346
|
"Delay after candle close before a daemon cycle",
|
|
29340
30347
|
Number(process.env.SIGNALS_DAEMON_SETTLE_DELAY_MS || 5e3)
|
|
29341
30348
|
);
|
|
29342
|
-
|
|
30349
|
+
import_args29.default.option(
|
|
29343
30350
|
["o", "connector"],
|
|
29344
30351
|
"Connector provider or name for signals (e.g. bybit, binance, coinbase, custom)",
|
|
29345
30352
|
"bybit"
|
|
29346
30353
|
);
|
|
29347
|
-
|
|
29348
|
-
|
|
29349
|
-
|
|
30354
|
+
import_args29.default.option(["V", "universe"], "Market universe (crypto or tradfi)", "crypto");
|
|
30355
|
+
import_args29.default.option(["A", "account"], "Trading account id");
|
|
30356
|
+
import_args29.default.option(["D", "deployment"], "Runtime deployment id");
|
|
29350
30357
|
SLOW_SIGNALS_WARNING_MS = 10 * 6e4;
|
|
29351
30358
|
projectRoot12 = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
|
|
29352
|
-
|
|
29353
|
-
interval4 =
|
|
30359
|
+
flags28 = import_args29.default.parse(process.argv);
|
|
30360
|
+
interval4 = flags28.timeframe.toString();
|
|
29354
30361
|
intervalMs = Number(interval4) * 6e4;
|
|
29355
30362
|
formatDuration3 = (durationMs) => `${(durationMs / 1e3).toFixed(1)}s`;
|
|
29356
30363
|
resolvePositiveInteger2 = (value, fallback) => {
|
|
@@ -29385,7 +30392,7 @@ var init_signals = __esm({
|
|
|
29385
30392
|
createDefaultSignalsLifecycle = (runtimeInterval = interval4) => createSignalsStrategyLifecycle({
|
|
29386
30393
|
intervalMs: Number(runtimeInterval) * 6e4,
|
|
29387
30394
|
maxLiveBars: resolveSignalsDaemonMaxLiveBars(runtimeInterval),
|
|
29388
|
-
releaseState:
|
|
30395
|
+
releaseState: import_strategies13.releaseStrategyReplayCache
|
|
29389
30396
|
});
|
|
29390
30397
|
timeOperation3 = (label, operation) => timeOperation(
|
|
29391
30398
|
label,
|
|
@@ -29412,9 +30419,9 @@ var init_signals = __esm({
|
|
|
29412
30419
|
return import_connectors16.DEFAULT_CONNECTOR_NAME;
|
|
29413
30420
|
};
|
|
29414
30421
|
createSignalsSession = async (lifecycle, scope = {}) => {
|
|
29415
|
-
const deployment =
|
|
29416
|
-
if (
|
|
29417
|
-
throw new Error(`Runtime deployment not found: ${
|
|
30422
|
+
const deployment = flags28.deployment ? await (0, import_tradingAccounts3.getRuntimeDeployment)(flags28.user, String(flags28.deployment)) : null;
|
|
30423
|
+
if (flags28.deployment && !deployment) {
|
|
30424
|
+
throw new Error(`Runtime deployment not found: ${flags28.deployment}`);
|
|
29418
30425
|
}
|
|
29419
30426
|
if (deployment && !deployment.enabled) {
|
|
29420
30427
|
throw new Error(`Runtime deployment is disabled: ${deployment.id}`);
|
|
@@ -29431,10 +30438,10 @@ var init_signals = __esm({
|
|
|
29431
30438
|
);
|
|
29432
30439
|
}
|
|
29433
30440
|
const connectorName = await resolveSignalsConnectorName(
|
|
29434
|
-
deployment?.connectorName ?? scope.connectorName ??
|
|
30441
|
+
deployment?.connectorName ?? scope.connectorName ?? flags28.connector
|
|
29435
30442
|
);
|
|
29436
|
-
const universe = deployment?.universe ?? scope.universe ??
|
|
29437
|
-
const requestedAccountId = deployment?.accountId ?? scope.accountId ??
|
|
30443
|
+
const universe = deployment?.universe ?? scope.universe ?? flags28.universe;
|
|
30444
|
+
const requestedAccountId = deployment?.accountId ?? scope.accountId ?? flags28.account;
|
|
29438
30445
|
const connectorFactory = await (0, import_connectors16.getConnectorCreatorByName)(
|
|
29439
30446
|
connectorName,
|
|
29440
30447
|
projectRoot12
|
|
@@ -29443,7 +30450,7 @@ var init_signals = __esm({
|
|
|
29443
30450
|
throw new Error(`Connector "${connectorName}" is not registered`);
|
|
29444
30451
|
}
|
|
29445
30452
|
const marketConnector = await connectorFactory({
|
|
29446
|
-
userName:
|
|
30453
|
+
userName: flags28.user,
|
|
29447
30454
|
universe,
|
|
29448
30455
|
accountId: requestedAccountId,
|
|
29449
30456
|
deploymentId: deployment?.id
|
|
@@ -29451,7 +30458,7 @@ var init_signals = __esm({
|
|
|
29451
30458
|
const btcReferences = await loadBtcReferenceConnectors({
|
|
29452
30459
|
connectorName,
|
|
29453
30460
|
marketConnector,
|
|
29454
|
-
userName:
|
|
30461
|
+
userName: flags28.user,
|
|
29455
30462
|
projectRoot: projectRoot12,
|
|
29456
30463
|
shouldUseDedicatedReferences: universe === "crypto" && connectorName.toLowerCase() === import_connectors16.DEFAULT_CONNECTOR_NAME.toLowerCase(),
|
|
29457
30464
|
warn: (message) => import_logger10.logger.warn(message)
|
|
@@ -29514,7 +30521,7 @@ var init_signals = __esm({
|
|
|
29514
30521
|
strategyConfig,
|
|
29515
30522
|
env: "CRON",
|
|
29516
30523
|
interval: interval5,
|
|
29517
|
-
makeOrders:
|
|
30524
|
+
makeOrders: flags28.makeOrders
|
|
29518
30525
|
});
|
|
29519
30526
|
const runtimeLineage = await buildRuntimeLineage({
|
|
29520
30527
|
projectRoot: projectRoot12,
|
|
@@ -29564,7 +30571,7 @@ var init_signals = __esm({
|
|
|
29564
30571
|
btcCoinbaseData: lifecycleBtcCoinbaseData,
|
|
29565
30572
|
onRuntimeClose
|
|
29566
30573
|
}) => strategyCreator({
|
|
29567
|
-
userName:
|
|
30574
|
+
userName: flags28.user,
|
|
29568
30575
|
connectorName,
|
|
29569
30576
|
runtimeConfigId: configId,
|
|
29570
30577
|
runtimeConfigSnapshot: {
|
|
@@ -29623,7 +30630,7 @@ var init_signals = __esm({
|
|
|
29623
30630
|
timestamp: lastCandle.timestamp,
|
|
29624
30631
|
runtimeConfigId: configId
|
|
29625
30632
|
}),
|
|
29626
|
-
userName:
|
|
30633
|
+
userName: flags28.user,
|
|
29627
30634
|
strategy: strategyName,
|
|
29628
30635
|
runtimeConfigId: configId,
|
|
29629
30636
|
runtimeLineage,
|
|
@@ -29649,7 +30656,7 @@ var init_signals = __esm({
|
|
|
29649
30656
|
if (configId && configId !== "config" && !signal.signalId.endsWith(`:${configId}`)) {
|
|
29650
30657
|
signal.signalId = `${signal.signalId}:${configId}`;
|
|
29651
30658
|
}
|
|
29652
|
-
await (0,
|
|
30659
|
+
await (0, import_strategies14.enrichSignalWithBinanceMarketContext)({
|
|
29653
30660
|
signal,
|
|
29654
30661
|
env: "CRON"
|
|
29655
30662
|
});
|
|
@@ -29672,7 +30679,7 @@ var init_signals = __esm({
|
|
|
29672
30679
|
);
|
|
29673
30680
|
await (0, import_redis20.setHashJsonField)(
|
|
29674
30681
|
import_redis20.redisKeys.runtimeSignalBucket(
|
|
29675
|
-
|
|
30682
|
+
flags28.user,
|
|
29676
30683
|
(0, import_time6.getRuntimeStorageDayKey)(signal.timestamp),
|
|
29677
30684
|
signal.strategy
|
|
29678
30685
|
),
|
|
@@ -29689,7 +30696,7 @@ var init_signals = __esm({
|
|
|
29689
30696
|
timestamp: lastCandle.timestamp,
|
|
29690
30697
|
runtimeConfigId: configId
|
|
29691
30698
|
}),
|
|
29692
|
-
userName:
|
|
30699
|
+
userName: flags28.user,
|
|
29693
30700
|
strategy: strategyName,
|
|
29694
30701
|
runtimeConfigId: configId,
|
|
29695
30702
|
runtimeLineage,
|
|
@@ -29744,10 +30751,10 @@ var init_signals = __esm({
|
|
|
29744
30751
|
const tickers = await timeOperation3("tickers load", () => {
|
|
29745
30752
|
const baseArgs = [
|
|
29746
30753
|
marketConnector,
|
|
29747
|
-
|
|
29748
|
-
|
|
29749
|
-
|
|
29750
|
-
|
|
30754
|
+
flags28.tickers || deployment?.tickers?.join(","),
|
|
30755
|
+
flags28.exclude,
|
|
30756
|
+
flags28.tickersLimit,
|
|
30757
|
+
flags28.chunk
|
|
29751
30758
|
];
|
|
29752
30759
|
return sessionUniverse || accountId || deployment ? (0, import_cli12.getTickers)(...baseArgs, {
|
|
29753
30760
|
universe,
|
|
@@ -29765,7 +30772,7 @@ var init_signals = __esm({
|
|
|
29765
30772
|
const instrumentsBySymbol = new Map(
|
|
29766
30773
|
instruments.map((instrument) => [instrument.symbol, instrument])
|
|
29767
30774
|
);
|
|
29768
|
-
if (
|
|
30775
|
+
if (flags28.showTickersList) {
|
|
29769
30776
|
console.log(import_chalk41.default.gray(JSON.stringify(tickers.sort(), null, 2)));
|
|
29770
30777
|
return;
|
|
29771
30778
|
}
|
|
@@ -29773,7 +30780,7 @@ var init_signals = __esm({
|
|
|
29773
30780
|
projectHooks = projectConfig.hooks;
|
|
29774
30781
|
const currentTimestamp = (0, import_time8.getTimestamp)();
|
|
29775
30782
|
const preloadStart = (0, import_time8.getTimestamp)(import_constants16.SIGNALS_CLI_PRELOAD_DAYS);
|
|
29776
|
-
if (!
|
|
30783
|
+
if (!flags28.cacheOnly && session.klineFeed) {
|
|
29777
30784
|
const expectedSymbols = universe === "crypto" ? [.../* @__PURE__ */ new Set([...tickers, "BTCUSDT", "ETHUSDT"])] : tickers;
|
|
29778
30785
|
session.klineFeed.setSubscriptions(expectedSymbols);
|
|
29779
30786
|
const targetTimestamp = Math.floor(currentTimestamp / intervalMs2) * intervalMs2 - intervalMs2;
|
|
@@ -29808,7 +30815,7 @@ var init_signals = __esm({
|
|
|
29808
30815
|
universe,
|
|
29809
30816
|
log: (message) => import_logger10.logger.info(import_chalk41.default.gray(message))
|
|
29810
30817
|
});
|
|
29811
|
-
} else if (!
|
|
30818
|
+
} else if (!flags28.cacheOnly) {
|
|
29812
30819
|
await updateMarketHistoryWithBtcReferences({
|
|
29813
30820
|
marketConnector,
|
|
29814
30821
|
connectorName,
|
|
@@ -29822,7 +30829,7 @@ var init_signals = __esm({
|
|
|
29822
30829
|
}
|
|
29823
30830
|
await prepareMarketContextForRun({
|
|
29824
30831
|
mode: "signals",
|
|
29825
|
-
userName:
|
|
30832
|
+
userName: flags28.user,
|
|
29826
30833
|
projectRoot: projectRoot12,
|
|
29827
30834
|
symbols: tickers,
|
|
29828
30835
|
universe,
|
|
@@ -29830,10 +30837,10 @@ var init_signals = __esm({
|
|
|
29830
30837
|
startMs: currentTimestamp,
|
|
29831
30838
|
endMs: currentTimestamp,
|
|
29832
30839
|
preloadStartMs: preloadStart,
|
|
29833
|
-
cacheOnly: Boolean(
|
|
30840
|
+
cacheOnly: Boolean(flags28.cacheOnly),
|
|
29834
30841
|
log: (message) => import_logger10.logger.info(import_chalk41.default.gray(message))
|
|
29835
30842
|
});
|
|
29836
|
-
if (
|
|
30843
|
+
if (flags28.updateOnly) {
|
|
29837
30844
|
return;
|
|
29838
30845
|
}
|
|
29839
30846
|
const [
|
|
@@ -29888,7 +30895,7 @@ var init_signals = __esm({
|
|
|
29888
30895
|
primaryEthClosedData.map((candle) => [candle.timestamp, candle])
|
|
29889
30896
|
);
|
|
29890
30897
|
const runtimeStrategies = await loadRuntimeStrategies({
|
|
29891
|
-
userName:
|
|
30898
|
+
userName: flags28.user,
|
|
29892
30899
|
projectRoot: projectRoot12,
|
|
29893
30900
|
deployment,
|
|
29894
30901
|
connectorName,
|
|
@@ -29900,7 +30907,7 @@ var init_signals = __esm({
|
|
|
29900
30907
|
lifecycle.clear();
|
|
29901
30908
|
import_logger10.logger.warn(
|
|
29902
30909
|
"No strategy configs found by users:%s:strategies:*:config",
|
|
29903
|
-
|
|
30910
|
+
flags28.user
|
|
29904
30911
|
);
|
|
29905
30912
|
return;
|
|
29906
30913
|
}
|
|
@@ -29924,13 +30931,13 @@ var init_signals = __esm({
|
|
|
29924
30931
|
);
|
|
29925
30932
|
import_logger10.logger.info(
|
|
29926
30933
|
import_chalk41.default.yellow(
|
|
29927
|
-
`loaded strategies (user=${
|
|
30934
|
+
`loaded strategies (user=${flags28.user}): ${runtimeStrategies.map((s) => s.strategyName).join(", ")}`
|
|
29928
30935
|
)
|
|
29929
30936
|
);
|
|
29930
30937
|
afterSignalsHookContext = {
|
|
29931
30938
|
connector: marketConnector,
|
|
29932
30939
|
connectorName,
|
|
29933
|
-
userName:
|
|
30940
|
+
userName: flags28.user,
|
|
29934
30941
|
interval: interval5,
|
|
29935
30942
|
tickers: [...tickers],
|
|
29936
30943
|
runtimeStrategies: runtimeStrategies.map(
|
|
@@ -29960,7 +30967,7 @@ var init_signals = __esm({
|
|
|
29960
30967
|
}
|
|
29961
30968
|
);
|
|
29962
30969
|
import_logger10.logger.info(import_chalk41.default.yellow(`tickers: ${tickers.length}`));
|
|
29963
|
-
const signalsParallel = resolvePositiveInteger2(
|
|
30970
|
+
const signalsParallel = resolvePositiveInteger2(flags28.parallel, 4);
|
|
29964
30971
|
import_logger10.logger.info(import_chalk41.default.yellow(`signal workers: ${signalsParallel}`));
|
|
29965
30972
|
await timeOperation3("strategy evaluation", async () => {
|
|
29966
30973
|
await (0, import_async5.runWithConcurrency)(tickers, signalsParallel, async (symbol) => {
|
|
@@ -29998,15 +31005,15 @@ var init_signals = __esm({
|
|
|
29998
31005
|
});
|
|
29999
31006
|
});
|
|
30000
31007
|
});
|
|
30001
|
-
if (
|
|
31008
|
+
if (flags28.notify) {
|
|
30002
31009
|
const telegramSignals = getTelegramDeliverableSignals(signals2);
|
|
30003
|
-
if (!
|
|
30004
|
-
await (0, import_cli12.makeScreenshots)(telegramSignals, interval5,
|
|
31010
|
+
if (!flags28.skipScreenshots && telegramSignals.length > 0) {
|
|
31011
|
+
await (0, import_cli12.makeScreenshots)(telegramSignals, interval5, flags28.user);
|
|
30005
31012
|
}
|
|
30006
|
-
await (0, import_cli12.sendToTG)(telegramSignals, interval5,
|
|
31013
|
+
await (0, import_cli12.sendToTG)(telegramSignals, interval5, flags28.user);
|
|
30007
31014
|
await (0, import_cli12.sendRuntimeCloseNotificationsToTG)(
|
|
30008
31015
|
runtimeCloseNotifications,
|
|
30009
|
-
|
|
31016
|
+
flags28.user
|
|
30010
31017
|
);
|
|
30011
31018
|
}
|
|
30012
31019
|
import_logger10.logger.info(
|
|
@@ -30016,7 +31023,7 @@ var init_signals = __esm({
|
|
|
30016
31023
|
2
|
|
30017
31024
|
)
|
|
30018
31025
|
);
|
|
30019
|
-
if (
|
|
31026
|
+
if (flags28.showSkipStats) {
|
|
30020
31027
|
logStrategySkipStats(runtimeStrategies, strategyStats);
|
|
30021
31028
|
}
|
|
30022
31029
|
} catch (error) {
|
|
@@ -30038,11 +31045,11 @@ var init_signals = __esm({
|
|
|
30038
31045
|
}
|
|
30039
31046
|
const durationMs = Date.now() - startedAt;
|
|
30040
31047
|
if (activeSession?.deployment) {
|
|
30041
|
-
await (0, import_tradingAccounts3.saveRuntimeDeploymentHeartbeat)(
|
|
31048
|
+
await (0, import_tradingAccounts3.saveRuntimeDeploymentHeartbeat)(flags28.user, {
|
|
30042
31049
|
deploymentId: activeSession.deployment.id,
|
|
30043
31050
|
status: getSignalsHeartbeatStatus({
|
|
30044
31051
|
cycleStatus: status,
|
|
30045
|
-
continuous: options.session != null || Boolean(
|
|
31052
|
+
continuous: options.session != null || Boolean(flags28.watch)
|
|
30046
31053
|
}),
|
|
30047
31054
|
pid: process.pid,
|
|
30048
31055
|
startedAt: activeSession.startedAt,
|
|
@@ -30065,7 +31072,7 @@ var init_signals = __esm({
|
|
|
30065
31072
|
);
|
|
30066
31073
|
}
|
|
30067
31074
|
}
|
|
30068
|
-
if (
|
|
31075
|
+
if (flags28.notify && durationMs > SLOW_SIGNALS_WARNING_MS) {
|
|
30069
31076
|
try {
|
|
30070
31077
|
await (0, import_cli12.sendTextToTG)(
|
|
30071
31078
|
formatSlowSignalsWarning({
|
|
@@ -30073,7 +31080,7 @@ var init_signals = __esm({
|
|
|
30073
31080
|
status,
|
|
30074
31081
|
found: signals2.length
|
|
30075
31082
|
}),
|
|
30076
|
-
{ userName:
|
|
31083
|
+
{ userName: flags28.user }
|
|
30077
31084
|
);
|
|
30078
31085
|
} catch (error) {
|
|
30079
31086
|
import_logger10.logger.error(
|
|
@@ -30094,13 +31101,13 @@ var init_signals = __esm({
|
|
|
30094
31101
|
};
|
|
30095
31102
|
loadConfiguredSignalsScopes = async (deployment) => {
|
|
30096
31103
|
const configuredStrategies = await loadRuntimeStrategies({
|
|
30097
|
-
userName:
|
|
31104
|
+
userName: flags28.user,
|
|
30098
31105
|
projectRoot: projectRoot12,
|
|
30099
31106
|
deployment,
|
|
30100
|
-
connectorName: String(
|
|
31107
|
+
connectorName: String(flags28.connector)
|
|
30101
31108
|
});
|
|
30102
31109
|
const connectorName = await resolveSignalsConnectorName(
|
|
30103
|
-
deployment?.connectorName ??
|
|
31110
|
+
deployment?.connectorName ?? flags28.connector
|
|
30104
31111
|
);
|
|
30105
31112
|
const scopes = /* @__PURE__ */ new Map();
|
|
30106
31113
|
for (const strategy of configuredStrategies) {
|
|
@@ -30134,7 +31141,7 @@ var init_signals = __esm({
|
|
|
30134
31141
|
)
|
|
30135
31142
|
);
|
|
30136
31143
|
signalsConfiguredScopesOnce = async () => {
|
|
30137
|
-
if (
|
|
31144
|
+
if (flags28.updateOnly || flags28.showTickersList || flags28.deployment || hasExplicitSignalsScope()) {
|
|
30138
31145
|
return signals();
|
|
30139
31146
|
}
|
|
30140
31147
|
const scopes = await loadConfiguredSignalsScopes();
|
|
@@ -30153,15 +31160,15 @@ var init_signals = __esm({
|
|
|
30153
31160
|
}
|
|
30154
31161
|
};
|
|
30155
31162
|
signalsDaemon = async () => {
|
|
30156
|
-
if (
|
|
31163
|
+
if (flags28.updateOnly || flags28.showTickersList) {
|
|
30157
31164
|
throw new Error(
|
|
30158
31165
|
"Signals daemon does not support updateOnly or showTickersList mode"
|
|
30159
31166
|
);
|
|
30160
31167
|
}
|
|
30161
31168
|
const abortController = new AbortController();
|
|
30162
|
-
const daemonDeployment =
|
|
30163
|
-
if (
|
|
30164
|
-
throw new Error(`Runtime deployment not found: ${
|
|
31169
|
+
const daemonDeployment = flags28.deployment ? await (0, import_tradingAccounts3.getRuntimeDeployment)(flags28.user, String(flags28.deployment)) : null;
|
|
31170
|
+
if (flags28.deployment && !daemonDeployment) {
|
|
31171
|
+
throw new Error(`Runtime deployment not found: ${flags28.deployment}`);
|
|
30165
31172
|
}
|
|
30166
31173
|
const sessions = /* @__PURE__ */ new Map();
|
|
30167
31174
|
const lastBoundaryByScope = /* @__PURE__ */ new Map();
|
|
@@ -30180,7 +31187,7 @@ var init_signals = __esm({
|
|
|
30180
31187
|
try {
|
|
30181
31188
|
await runSignalsDaemon({
|
|
30182
31189
|
intervalMs: 6e4,
|
|
30183
|
-
settleDelayMs: resolveNonNegativeInteger(
|
|
31190
|
+
settleDelayMs: resolveNonNegativeInteger(flags28.settleDelayMs, 5e3),
|
|
30184
31191
|
signal: abortController.signal,
|
|
30185
31192
|
runCycle: async () => {
|
|
30186
31193
|
const scopes = await loadConfiguredSignalsScopes(daemonDeployment);
|
|
@@ -30206,7 +31213,7 @@ var init_signals = __esm({
|
|
|
30206
31213
|
if (!session.klineFeed && isSignalsKlineWsEnabled() && session.connectorName.toLowerCase() === "bybit") {
|
|
30207
31214
|
session.klineFeed = await createSignalsKlineFeed({
|
|
30208
31215
|
config: {
|
|
30209
|
-
userName:
|
|
31216
|
+
userName: flags28.user,
|
|
30210
31217
|
universe: session.universe,
|
|
30211
31218
|
accountId: session.accountId,
|
|
30212
31219
|
deploymentId: session.deployment?.id
|
|
@@ -30259,7 +31266,7 @@ var init_signals = __esm({
|
|
|
30259
31266
|
await session.klineFeed?.close();
|
|
30260
31267
|
session.lifecycle.clear();
|
|
30261
31268
|
if (session.deployment) {
|
|
30262
|
-
await (0, import_tradingAccounts3.saveRuntimeDeploymentHeartbeat)(
|
|
31269
|
+
await (0, import_tradingAccounts3.saveRuntimeDeploymentHeartbeat)(flags28.user, {
|
|
30263
31270
|
deploymentId: session.deployment.id,
|
|
30264
31271
|
status: "stopped",
|
|
30265
31272
|
pid: process.pid,
|
|
@@ -30270,30 +31277,30 @@ var init_signals = __esm({
|
|
|
30270
31277
|
}
|
|
30271
31278
|
}
|
|
30272
31279
|
};
|
|
30273
|
-
|
|
31280
|
+
main36 = () => flags28.watch ? signalsDaemon() : signalsConfiguredScopesOnce();
|
|
30274
31281
|
}
|
|
30275
31282
|
});
|
|
30276
31283
|
|
|
30277
31284
|
// src/scripts/signalsDaemon.ts
|
|
30278
31285
|
var signalsDaemon_exports = {};
|
|
30279
31286
|
__export(signalsDaemon_exports, {
|
|
30280
|
-
main: () =>
|
|
31287
|
+
main: () => main37
|
|
30281
31288
|
});
|
|
30282
|
-
var
|
|
31289
|
+
var main37;
|
|
30283
31290
|
var init_signalsDaemon = __esm({
|
|
30284
31291
|
"src/scripts/signalsDaemon.ts"() {
|
|
30285
31292
|
"use strict";
|
|
30286
31293
|
init_signals();
|
|
30287
|
-
|
|
31294
|
+
main37 = signalsDaemon;
|
|
30288
31295
|
}
|
|
30289
31296
|
});
|
|
30290
31297
|
|
|
30291
31298
|
// src/lib/replay/cliConfig.ts
|
|
30292
|
-
var
|
|
31299
|
+
var import_args30, REPLAY_UNSUPPORTED_FLAGS, hasCliFlag2, assertNoUnsupportedReplayFlags, replayNormalizedArgv, replayFlags, replayInterval, replayUserName, replayProjectRoot, isReplayUpdateOnlyRun;
|
|
30293
31300
|
var init_cliConfig2 = __esm({
|
|
30294
31301
|
"src/lib/replay/cliConfig.ts"() {
|
|
30295
31302
|
"use strict";
|
|
30296
|
-
|
|
31303
|
+
import_args30 = __toESM(require("args"));
|
|
30297
31304
|
init_cliArgs();
|
|
30298
31305
|
REPLAY_UNSUPPORTED_FLAGS = [
|
|
30299
31306
|
["--live", "`yarn replay` is already the replacement for legacy `--live`."],
|
|
@@ -30327,27 +31334,27 @@ var init_cliConfig2 = __esm({
|
|
|
30327
31334
|
}
|
|
30328
31335
|
}
|
|
30329
31336
|
};
|
|
30330
|
-
|
|
31337
|
+
import_args30.default.example(
|
|
30331
31338
|
"yarn replay --days 7 --cacheOnly",
|
|
30332
31339
|
"Replay active runtime strategies on historical data"
|
|
30333
31340
|
);
|
|
30334
|
-
|
|
30335
|
-
|
|
30336
|
-
|
|
30337
|
-
|
|
30338
|
-
|
|
30339
|
-
|
|
30340
|
-
|
|
30341
|
-
|
|
30342
|
-
|
|
30343
|
-
|
|
31341
|
+
import_args30.default.option(["t", "tickers"], "Selected tickers");
|
|
31342
|
+
import_args30.default.option(["e", "exclude"], "Exclude tickers from replay");
|
|
31343
|
+
import_args30.default.option(["l", "tickersLimit"], "Tickers limit");
|
|
31344
|
+
import_args30.default.option(["f", "timeframe"], "Timeframe", 15);
|
|
31345
|
+
import_args30.default.option(["d", "days"], "Run replay only for the last N days");
|
|
31346
|
+
import_args30.default.option("startTime", "Explicit replay start timestamp (ms or seconds)");
|
|
31347
|
+
import_args30.default.option("endTime", "Explicit replay end timestamp (ms or seconds)");
|
|
31348
|
+
import_args30.default.option(["u", "updateOnly"], "Only update tickers history", false);
|
|
31349
|
+
import_args30.default.option(["C", "cacheOnly"], "Do not update tickers history", false);
|
|
31350
|
+
import_args30.default.option(
|
|
30344
31351
|
"chart",
|
|
30345
31352
|
"Save compact replay chart data for the strategies UI",
|
|
30346
31353
|
false
|
|
30347
31354
|
);
|
|
30348
|
-
|
|
30349
|
-
|
|
30350
|
-
|
|
31355
|
+
import_args30.default.option(["L", "showTickersList"], "Just show only ticker list", false);
|
|
31356
|
+
import_args30.default.option(["U", "user"], "Use user config", "root");
|
|
31357
|
+
import_args30.default.option(
|
|
30351
31358
|
["o", "connector"],
|
|
30352
31359
|
"Connector provider or name for replay (e.g. bybit, binance, coinbase, custom)",
|
|
30353
31360
|
"bybit"
|
|
@@ -30359,7 +31366,7 @@ var init_cliConfig2 = __esm({
|
|
|
30359
31366
|
"-U": "--user"
|
|
30360
31367
|
});
|
|
30361
31368
|
assertNoUnsupportedReplayFlags(replayNormalizedArgv);
|
|
30362
|
-
replayFlags =
|
|
31369
|
+
replayFlags = import_args30.default.parse(replayNormalizedArgv);
|
|
30363
31370
|
replayInterval = String(replayFlags.timeframe ?? 15);
|
|
30364
31371
|
replayUserName = String(replayFlags.user ?? "root");
|
|
30365
31372
|
replayProjectRoot = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
|
|
@@ -30778,20 +31785,20 @@ var init_portfolioReplayConnector = __esm({
|
|
|
30778
31785
|
});
|
|
30779
31786
|
|
|
30780
31787
|
// src/lib/replay/historicalSignalsReplay.ts
|
|
30781
|
-
var import_chalk42, import_progress11, import_backtest4,
|
|
31788
|
+
var import_chalk42, import_progress11, import_backtest4, import_strategies15, import_time9, import_logger11, import_redis21, import_connectors17, import_cli13, import_strategies16, loadRuntimeStrategies2, loadReferenceConnector, buildPreparedData, buildAfterSignalsContext, buildSymbolSharedReplayKey, runHistoricalSignalsReplay;
|
|
30782
31789
|
var init_historicalSignalsReplay = __esm({
|
|
30783
31790
|
"src/lib/replay/historicalSignalsReplay.ts"() {
|
|
30784
31791
|
"use strict";
|
|
30785
31792
|
import_chalk42 = __toESM(require("chalk"));
|
|
30786
31793
|
import_progress11 = __toESM(require("progress"));
|
|
30787
31794
|
import_backtest4 = require("@tradejs/core/backtest");
|
|
30788
|
-
|
|
31795
|
+
import_strategies15 = require("@tradejs/core/strategies");
|
|
30789
31796
|
import_time9 = require("@tradejs/core/time");
|
|
30790
31797
|
import_logger11 = require("@tradejs/infra/logger");
|
|
30791
31798
|
import_redis21 = require("@tradejs/infra/redis");
|
|
30792
31799
|
import_connectors17 = require("@tradejs/node/connectors");
|
|
30793
31800
|
import_cli13 = require("@tradejs/node/cli");
|
|
30794
|
-
|
|
31801
|
+
import_strategies16 = require("@tradejs/node/strategies");
|
|
30795
31802
|
init_cliConfig2();
|
|
30796
31803
|
init_support();
|
|
30797
31804
|
init_portfolioReplayConnector();
|
|
@@ -30801,7 +31808,7 @@ var init_historicalSignalsReplay = __esm({
|
|
|
30801
31808
|
loadRuntimeStrategies2 = async (runtimeStrategies) => {
|
|
30802
31809
|
const strategies = await Promise.all(
|
|
30803
31810
|
runtimeStrategies.map(async ({ strategyName, strategyConfig }) => {
|
|
30804
|
-
const strategyCreator = await (0,
|
|
31811
|
+
const strategyCreator = await (0, import_strategies16.getStrategyCreator)(
|
|
30805
31812
|
strategyName,
|
|
30806
31813
|
replayProjectRoot
|
|
30807
31814
|
);
|
|
@@ -31147,7 +32154,7 @@ var init_historicalSignalsReplay = __esm({
|
|
|
31147
32154
|
);
|
|
31148
32155
|
if (result && typeof result !== "string") {
|
|
31149
32156
|
result.runtimeLineage = strategyRuntime.runtimeLineage;
|
|
31150
|
-
await (0,
|
|
32157
|
+
await (0, import_strategies16.enrichSignalWithBinanceMarketContext)({
|
|
31151
32158
|
signal: result,
|
|
31152
32159
|
env: "PARITY"
|
|
31153
32160
|
});
|
|
@@ -31171,8 +32178,8 @@ var init_historicalSignalsReplay = __esm({
|
|
|
31171
32178
|
}
|
|
31172
32179
|
} finally {
|
|
31173
32180
|
for (const keyPrefix of sharedReplayKeyPrefixes) {
|
|
31174
|
-
(0,
|
|
31175
|
-
(0,
|
|
32181
|
+
(0, import_strategies15.releaseStrategyIndicatorsReplayCache)(keyPrefix);
|
|
32182
|
+
(0, import_strategies15.releaseStrategyReplayCache)(keyPrefix);
|
|
31176
32183
|
}
|
|
31177
32184
|
}
|
|
31178
32185
|
const artifacts = replayConnector.getReplayArtifacts();
|
|
@@ -33846,6 +34853,7 @@ var init_replayRunner = __esm({
|
|
|
33846
34853
|
cacheOnly: Boolean(replayFlags.cacheOnly),
|
|
33847
34854
|
aiEnabled: preparedRun.aiEnabled,
|
|
33848
34855
|
mlEnabled: preparedRun.mlEnabled,
|
|
34856
|
+
strategyNames: preparedRun.strategyNames,
|
|
33849
34857
|
log: (message) => console.log(import_chalk45.default.gray(message))
|
|
33850
34858
|
});
|
|
33851
34859
|
};
|
|
@@ -33983,7 +34991,8 @@ var init_replayRunner = __esm({
|
|
|
33983
34991
|
),
|
|
33984
34992
|
mlEnabled: replayStrategies.some(
|
|
33985
34993
|
({ strategyConfig }) => Boolean(strategyConfig.ML_ENABLED)
|
|
33986
|
-
)
|
|
34994
|
+
),
|
|
34995
|
+
strategyNames: replayStrategies.map(({ strategyName }) => strategyName)
|
|
33987
34996
|
});
|
|
33988
34997
|
console.log(import_chalk45.default.yellow(`tickers: ${preparedRun.tickers.length}`));
|
|
33989
34998
|
console.log(
|
|
@@ -34013,14 +35022,14 @@ var init_replayRunner = __esm({
|
|
|
34013
35022
|
// src/scripts/replay.ts
|
|
34014
35023
|
var replay_exports = {};
|
|
34015
35024
|
__export(replay_exports, {
|
|
34016
|
-
main: () =>
|
|
35025
|
+
main: () => main38
|
|
34017
35026
|
});
|
|
34018
|
-
var
|
|
35027
|
+
var main38;
|
|
34019
35028
|
var init_replay = __esm({
|
|
34020
35029
|
"src/scripts/replay.ts"() {
|
|
34021
35030
|
"use strict";
|
|
34022
35031
|
init_replayRunner();
|
|
34023
|
-
|
|
35032
|
+
main38 = async () => {
|
|
34024
35033
|
await replayBacktest();
|
|
34025
35034
|
};
|
|
34026
35035
|
}
|
|
@@ -34029,15 +35038,15 @@ var init_replay = __esm({
|
|
|
34029
35038
|
// src/scripts/signalsSummary.ts
|
|
34030
35039
|
var signalsSummary_exports = {};
|
|
34031
35040
|
__export(signalsSummary_exports, {
|
|
34032
|
-
main: () =>
|
|
35041
|
+
main: () => main39,
|
|
34033
35042
|
signalsSummary: () => signalsSummary
|
|
34034
35043
|
});
|
|
34035
|
-
var
|
|
35044
|
+
var import_config12, import_args31, import_logger12, import_connectors18, import_tradingAccounts4, flags29, 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, main39;
|
|
34036
35045
|
var init_signalsSummary = __esm({
|
|
34037
35046
|
"src/scripts/signalsSummary.ts"() {
|
|
34038
35047
|
"use strict";
|
|
34039
|
-
|
|
34040
|
-
|
|
35048
|
+
import_config12 = require("dotenv/config");
|
|
35049
|
+
import_args31 = __toESM(require("args"));
|
|
34041
35050
|
import_logger12 = require("@tradejs/infra/logger");
|
|
34042
35051
|
import_connectors18 = require("@tradejs/node/connectors");
|
|
34043
35052
|
init_runtimeRedis();
|
|
@@ -34047,20 +35056,20 @@ var init_signalsSummary = __esm({
|
|
|
34047
35056
|
init_runtimeSignalsStorage();
|
|
34048
35057
|
init_runtimeDebugEvidence();
|
|
34049
35058
|
import_tradingAccounts4 = require("@tradejs/infra/tradingAccounts");
|
|
34050
|
-
|
|
34051
|
-
|
|
35059
|
+
import_args31.default.option(["u", "user"], "Use user config", "root");
|
|
35060
|
+
import_args31.default.option(
|
|
34052
35061
|
"connector",
|
|
34053
35062
|
"Connector provider or name for summary (e.g. bybit, binance, coinbase, custom)",
|
|
34054
35063
|
"bybit"
|
|
34055
35064
|
);
|
|
34056
|
-
|
|
34057
|
-
|
|
34058
|
-
|
|
35065
|
+
import_args31.default.option(["H", "hours"], "Summary window in hours", 24);
|
|
35066
|
+
import_args31.default.option(["P", "printOnly"], "Print summary instead of Telegram", false);
|
|
35067
|
+
import_args31.default.option(
|
|
34059
35068
|
"debugAttachment",
|
|
34060
35069
|
"Attach runtime Redis debug JSON for replay diagnostics",
|
|
34061
35070
|
true
|
|
34062
35071
|
);
|
|
34063
|
-
|
|
35072
|
+
flags29 = import_args31.default.parse(process.argv);
|
|
34064
35073
|
projectRoot13 = String(process.env.PROJECT_CWD || process.cwd()).trim() || process.cwd();
|
|
34065
35074
|
SUMMARY_TIMEZONE2 = "Europe/Moscow";
|
|
34066
35075
|
SUMMARY_TIMEZONE_LABEL2 = "MSK";
|
|
@@ -34566,12 +35575,12 @@ none`
|
|
|
34566
35575
|
signalsSummary = async () => {
|
|
34567
35576
|
const hours = Math.max(
|
|
34568
35577
|
1,
|
|
34569
|
-
Number.parseInt(String(
|
|
35578
|
+
Number.parseInt(String(flags29.hours ?? 24), 10) || 24
|
|
34570
35579
|
);
|
|
34571
35580
|
const endTime = Date.now();
|
|
34572
35581
|
const startTime = endTime - hours * 60 * 60 * 1e3;
|
|
34573
35582
|
const fallbackConnectorName = await resolveSummaryConnectorName(
|
|
34574
|
-
|
|
35583
|
+
flags29.connector
|
|
34575
35584
|
);
|
|
34576
35585
|
const [
|
|
34577
35586
|
configuredStrategyNames,
|
|
@@ -34580,25 +35589,25 @@ none`
|
|
|
34580
35589
|
trades,
|
|
34581
35590
|
runtimeDeployments
|
|
34582
35591
|
] = await Promise.all([
|
|
34583
|
-
loadRuntimeStrategyNames(
|
|
34584
|
-
loadRuntimeSignals(
|
|
34585
|
-
loadRuntimeSignalEvaluationStatsBuckets(
|
|
34586
|
-
loadRuntimeTrades(
|
|
34587
|
-
(0, import_tradingAccounts4.listRuntimeDeployments)(
|
|
35592
|
+
loadRuntimeStrategyNames(flags29.user),
|
|
35593
|
+
loadRuntimeSignals(flags29.user, { startTime, endTime }),
|
|
35594
|
+
loadRuntimeSignalEvaluationStatsBuckets(flags29.user),
|
|
35595
|
+
loadRuntimeTrades(flags29.user),
|
|
35596
|
+
(0, import_tradingAccounts4.listRuntimeDeployments)(flags29.user)
|
|
34588
35597
|
]);
|
|
34589
35598
|
const {
|
|
34590
35599
|
trades: syncedTrades,
|
|
34591
35600
|
connectorNames,
|
|
34592
35601
|
scopesCount
|
|
34593
35602
|
} = await syncRuntimeTradeScopes({
|
|
34594
|
-
userName:
|
|
35603
|
+
userName: flags29.user,
|
|
34595
35604
|
startTime,
|
|
34596
35605
|
endTime,
|
|
34597
35606
|
fallbackConnectorName,
|
|
34598
35607
|
trades,
|
|
34599
35608
|
deployments: runtimeDeployments
|
|
34600
35609
|
});
|
|
34601
|
-
const activeTradeOrderIds = await loadRuntimeActiveTradeOrderIds(
|
|
35610
|
+
const activeTradeOrderIds = await loadRuntimeActiveTradeOrderIds(flags29.user);
|
|
34602
35611
|
const windowSignals = signals2.filter(
|
|
34603
35612
|
(signal) => signal.timestamp >= startTime && signal.timestamp < endTime
|
|
34604
35613
|
);
|
|
@@ -34646,10 +35655,10 @@ none`
|
|
|
34646
35655
|
windowClosedTrades.length,
|
|
34647
35656
|
connectorNames.join(",") || fallbackConnectorName,
|
|
34648
35657
|
scopesCount,
|
|
34649
|
-
|
|
35658
|
+
flags29.user
|
|
34650
35659
|
);
|
|
34651
|
-
const debugAttachment = shouldAttachDebugReport(
|
|
34652
|
-
userName:
|
|
35660
|
+
const debugAttachment = shouldAttachDebugReport(flags29.debugAttachment) ? await buildRuntimeDebugReportAttachment({
|
|
35661
|
+
userName: flags29.user,
|
|
34653
35662
|
startTime,
|
|
34654
35663
|
endTime,
|
|
34655
35664
|
signals: debugSignals,
|
|
@@ -34657,7 +35666,7 @@ none`
|
|
|
34657
35666
|
trades: windowTrades
|
|
34658
35667
|
}) : null;
|
|
34659
35668
|
const signalsAttachment = buildSignalsSummaryAttachment({
|
|
34660
|
-
userName:
|
|
35669
|
+
userName: flags29.user,
|
|
34661
35670
|
endTime,
|
|
34662
35671
|
content: signalsMessage
|
|
34663
35672
|
});
|
|
@@ -34673,7 +35682,7 @@ none`
|
|
|
34673
35682
|
signalsAttachment,
|
|
34674
35683
|
...debugAttachment ? [debugAttachment] : []
|
|
34675
35684
|
];
|
|
34676
|
-
if (
|
|
35685
|
+
if (flags29.printOnly) {
|
|
34677
35686
|
console.log(tradesMessageWithAttachmentSummary);
|
|
34678
35687
|
console.log("");
|
|
34679
35688
|
console.log(`Signals attachment: ${signalsAttachment.filename}`);
|
|
@@ -34686,27 +35695,27 @@ none`
|
|
|
34686
35695
|
return;
|
|
34687
35696
|
}
|
|
34688
35697
|
await sendTelegramReport(tradesMessageWithAttachmentSummary, {
|
|
34689
|
-
userName:
|
|
35698
|
+
userName: flags29.user,
|
|
34690
35699
|
attachments
|
|
34691
35700
|
});
|
|
34692
35701
|
};
|
|
34693
|
-
|
|
35702
|
+
main39 = signalsSummary;
|
|
34694
35703
|
}
|
|
34695
35704
|
});
|
|
34696
35705
|
|
|
34697
35706
|
// src/scripts/test-ml.ts
|
|
34698
35707
|
var test_ml_exports = {};
|
|
34699
35708
|
__export(test_ml_exports, {
|
|
34700
|
-
main: () =>
|
|
35709
|
+
main: () => main40
|
|
34701
35710
|
});
|
|
34702
|
-
var
|
|
35711
|
+
var import_config13, import_constants20, import_ml3, import_strategies17, now, INTERVAL_MIN, CANDLES, makeSeries, makeCandles, buildTrendline, main40;
|
|
34703
35712
|
var init_test_ml = __esm({
|
|
34704
35713
|
"src/scripts/test-ml.ts"() {
|
|
34705
35714
|
"use strict";
|
|
34706
|
-
|
|
35715
|
+
import_config13 = require("dotenv/config");
|
|
34707
35716
|
import_constants20 = require("@tradejs/core/constants");
|
|
34708
35717
|
import_ml3 = require("@tradejs/infra/ml");
|
|
34709
|
-
|
|
35718
|
+
import_strategies17 = require("@tradejs/strategies");
|
|
34710
35719
|
now = Date.now();
|
|
34711
35720
|
INTERVAL_MIN = 15;
|
|
34712
35721
|
CANDLES = import_constants20.ML_BASE_CANDLES_WINDOW;
|
|
@@ -34749,7 +35758,7 @@ var init_test_ml = __esm({
|
|
|
34749
35758
|
{ timestamp: entryTs - 2 * INTERVAL_MIN * 6e4, value: price * 0.995 }
|
|
34750
35759
|
]
|
|
34751
35760
|
});
|
|
34752
|
-
|
|
35761
|
+
main40 = async () => {
|
|
34753
35762
|
const startTs = now - CANDLES * INTERVAL_MIN * 6e4;
|
|
34754
35763
|
const candles = makeCandles(120, startTs);
|
|
34755
35764
|
const btcCandles = makeCandles(42e3, startTs);
|
|
@@ -34800,7 +35809,7 @@ var init_test_ml = __esm({
|
|
|
34800
35809
|
volume24h: makeSeries(10, 8e3, 200)
|
|
34801
35810
|
}
|
|
34802
35811
|
};
|
|
34803
|
-
const { TRENDLINE, HIGHS, LOWS, ML_THRESHOLD } =
|
|
35812
|
+
const { TRENDLINE, HIGHS, LOWS, ML_THRESHOLD } = import_strategies17.trendLineDefaultConfig;
|
|
34804
35813
|
const fullRow = (0, import_ml3.buildMlTrainingRow)(
|
|
34805
35814
|
{
|
|
34806
35815
|
signal,
|
|
@@ -34836,19 +35845,19 @@ var init_test_ml = __esm({
|
|
|
34836
35845
|
// src/scripts/test.ts
|
|
34837
35846
|
var test_exports = {};
|
|
34838
35847
|
__export(test_exports, {
|
|
34839
|
-
main: () =>
|
|
35848
|
+
main: () => main41
|
|
34840
35849
|
});
|
|
34841
|
-
var
|
|
35850
|
+
var import_config14, import_connectors19, getPositions, main41;
|
|
34842
35851
|
var init_test = __esm({
|
|
34843
35852
|
"src/scripts/test.ts"() {
|
|
34844
35853
|
"use strict";
|
|
34845
|
-
|
|
35854
|
+
import_config14 = require("dotenv/config");
|
|
34846
35855
|
import_connectors19 = require("@tradejs/connectors");
|
|
34847
35856
|
getPositions = async (connector) => {
|
|
34848
35857
|
const res = await connector.getPositions();
|
|
34849
35858
|
console.log("res", res);
|
|
34850
35859
|
};
|
|
34851
|
-
|
|
35860
|
+
main41 = async () => {
|
|
34852
35861
|
const byBitConnector = await import_connectors19.connectors.ByBit({
|
|
34853
35862
|
userName: "root"
|
|
34854
35863
|
});
|
|
@@ -34860,23 +35869,23 @@ var init_test = __esm({
|
|
|
34860
35869
|
// src/scripts/user-add.ts
|
|
34861
35870
|
var user_add_exports = {};
|
|
34862
35871
|
__export(user_add_exports, {
|
|
34863
|
-
main: () =>
|
|
35872
|
+
main: () => main42
|
|
34864
35873
|
});
|
|
34865
|
-
var
|
|
35874
|
+
var import_args32, import_chalk46, import_bcryptjs, import_redis24, flags30, main42;
|
|
34866
35875
|
var init_user_add = __esm({
|
|
34867
35876
|
"src/scripts/user-add.ts"() {
|
|
34868
35877
|
"use strict";
|
|
34869
|
-
|
|
35878
|
+
import_args32 = __toESM(require("args"));
|
|
34870
35879
|
import_chalk46 = __toESM(require("chalk"));
|
|
34871
35880
|
import_bcryptjs = __toESM(require("bcryptjs"));
|
|
34872
35881
|
import_redis24 = require("@tradejs/infra/redis");
|
|
34873
|
-
|
|
34874
|
-
|
|
34875
|
-
|
|
34876
|
-
|
|
34877
|
-
|
|
34878
|
-
const userName2 = String(
|
|
34879
|
-
const password = String(
|
|
35882
|
+
import_args32.default.example("yarn user-add -u myname -p 123456", "Create or update user");
|
|
35883
|
+
import_args32.default.option(["u", "user"], "Username", "");
|
|
35884
|
+
import_args32.default.option(["p", "password"], "Password (plain text)", "");
|
|
35885
|
+
flags30 = import_args32.default.parse(process.argv);
|
|
35886
|
+
main42 = async () => {
|
|
35887
|
+
const userName2 = String(flags30.user || "").trim();
|
|
35888
|
+
const password = String(flags30.password || "");
|
|
34880
35889
|
if (!userName2 || !password) {
|
|
34881
35890
|
console.error(import_chalk46.default.red("Missing -U <user> or -P <password>"));
|
|
34882
35891
|
process.exit(1);
|
|
@@ -34902,14 +35911,14 @@ var init_user_add = __esm({
|
|
|
34902
35911
|
var cli_exports = {};
|
|
34903
35912
|
__export(cli_exports, {
|
|
34904
35913
|
cleanupCliResources: () => cleanupCliResources,
|
|
34905
|
-
main: () =>
|
|
35914
|
+
main: () => main43,
|
|
34906
35915
|
printUsage: () => printUsage,
|
|
34907
35916
|
runLoadedModule: () => runLoadedModule
|
|
34908
35917
|
});
|
|
34909
35918
|
module.exports = __toCommonJS(cli_exports);
|
|
34910
|
-
var
|
|
35919
|
+
var import_config15 = require("dotenv/config");
|
|
34911
35920
|
var import_redis25 = require("@tradejs/infra/redis");
|
|
34912
|
-
var
|
|
35921
|
+
var import_timescale13 = require("@tradejs/infra/timescale");
|
|
34913
35922
|
var scriptLoaders = {
|
|
34914
35923
|
"agent-run": () => Promise.resolve().then(() => (init_agentRun(), agentRun_exports)),
|
|
34915
35924
|
"ai-export": () => Promise.resolve().then(() => (init_aiExport2(), aiExport_exports)),
|
|
@@ -34934,6 +35943,9 @@ var scriptLoaders = {
|
|
|
34934
35943
|
"maintenance:cleanup-market-context": () => Promise.resolve().then(() => (init_cleanupMarketContext(), cleanupMarketContext_exports)),
|
|
34935
35944
|
migration: () => Promise.resolve().then(() => (init_migration(), migration_exports)),
|
|
34936
35945
|
"market-ws": () => Promise.resolve().then(() => (init_marketWs(), marketWs_exports)),
|
|
35946
|
+
"hyperliquid:whale-backfill": () => Promise.resolve().then(() => (init_hyperliquidWhaleBackfill2(), hyperliquidWhaleBackfill_exports)),
|
|
35947
|
+
"hyperliquid:whale-ingest": () => Promise.resolve().then(() => (init_hyperliquidWhaleIngest(), hyperliquidWhaleIngest_exports)),
|
|
35948
|
+
"hyperliquid:whales:update": () => Promise.resolve().then(() => (init_hyperliquidWhalesUpdate(), hyperliquidWhalesUpdate_exports)),
|
|
34937
35949
|
"ml-export": () => Promise.resolve().then(() => (init_mlExport(), mlExport_exports)),
|
|
34938
35950
|
"ml-inspect": () => Promise.resolve().then(() => (init_mlInspect(), mlInspect_exports)),
|
|
34939
35951
|
"ml-train:latest": () => Promise.resolve().then(() => (init_mlTrainLatestSelect(), mlTrainLatestSelect_exports)),
|
|
@@ -34965,16 +35977,16 @@ var runLoadedModule = async (loaded) => {
|
|
|
34965
35977
|
}
|
|
34966
35978
|
};
|
|
34967
35979
|
var cleanupCliResources = async () => {
|
|
34968
|
-
await Promise.allSettled([(0, import_redis25.closeRedisConnection)(), (0,
|
|
35980
|
+
await Promise.allSettled([(0, import_redis25.closeRedisConnection)(), (0, import_timescale13.closeTimescalePool)()]);
|
|
34969
35981
|
};
|
|
34970
|
-
var
|
|
34971
|
-
const [, scriptPath, command = "", ...
|
|
35982
|
+
var main43 = async () => {
|
|
35983
|
+
const [, scriptPath, command = "", ...args33] = process.argv;
|
|
34972
35984
|
const loader = scriptLoaders[command];
|
|
34973
35985
|
if (!loader) {
|
|
34974
35986
|
printUsage();
|
|
34975
35987
|
process.exit(command ? 1 : 0);
|
|
34976
35988
|
}
|
|
34977
|
-
const nextArgs = [...
|
|
35989
|
+
const nextArgs = [...args33];
|
|
34978
35990
|
if (command === "spread:ingest") {
|
|
34979
35991
|
nextArgs.push("--provider", "binance_coinbase_spread");
|
|
34980
35992
|
}
|
|
@@ -34987,7 +35999,7 @@ var main40 = async () => {
|
|
|
34987
35999
|
}
|
|
34988
36000
|
};
|
|
34989
36001
|
if (require.main === module) {
|
|
34990
|
-
|
|
36002
|
+
main43().catch((error) => {
|
|
34991
36003
|
console.error(error);
|
|
34992
36004
|
process.exit(1);
|
|
34993
36005
|
});
|