@tradejs/node 2.0.13 → 2.0.15
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/ai.js +1 -1
- package/dist/ai.mjs +1 -1
- package/dist/backtest.js +580 -14
- package/dist/backtest.mjs +18 -3
- package/dist/{chunk-PD25CABK.mjs → chunk-DPHKRDD5.mjs} +1 -1
- package/dist/{chunk-76TEMQKX.mjs → chunk-FEQR4XGR.mjs} +576 -9
- package/dist/cli.js +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/strategies.d.mts +50 -1
- package/dist/strategies.d.ts +50 -1
- package/dist/strategies.js +633 -37
- package/dist/strategies.mjs +45 -4
- package/package.json +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getStrategyProfileMlAdapter
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DPHKRDD5.mjs";
|
|
4
4
|
import {
|
|
5
5
|
getStrategyManifest
|
|
6
6
|
} from "./chunk-QVSMINLG.mjs";
|
|
@@ -1077,27 +1077,585 @@ var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
|
1077
1077
|
}
|
|
1078
1078
|
};
|
|
1079
1079
|
|
|
1080
|
+
// src/hyperliquidWhaleUniverse.ts
|
|
1081
|
+
import { createHash as createHash2 } from "crypto";
|
|
1082
|
+
|
|
1083
|
+
// src/config/hyperliquidPerpUniverse.json
|
|
1084
|
+
var hyperliquidPerpUniverse_default = {
|
|
1085
|
+
schemaVersion: 1,
|
|
1086
|
+
updatedAt: "2026-08-04T11:46:43.476Z",
|
|
1087
|
+
source: "hyperliquid_main_perp_day_notional_volume",
|
|
1088
|
+
fingerprint: "58f7e024b19158dc",
|
|
1089
|
+
size: 30,
|
|
1090
|
+
symbols: [
|
|
1091
|
+
"BTC",
|
|
1092
|
+
"ETH",
|
|
1093
|
+
"HYPE",
|
|
1094
|
+
"SOL",
|
|
1095
|
+
"ZEC",
|
|
1096
|
+
"PUMP",
|
|
1097
|
+
"XRP",
|
|
1098
|
+
"CASHCAT",
|
|
1099
|
+
"WLD",
|
|
1100
|
+
"GRAM",
|
|
1101
|
+
"UNI",
|
|
1102
|
+
"ADA",
|
|
1103
|
+
"LIT",
|
|
1104
|
+
"ENA",
|
|
1105
|
+
"NEAR",
|
|
1106
|
+
"kPEPE",
|
|
1107
|
+
"FARTCOIN",
|
|
1108
|
+
"KAITO",
|
|
1109
|
+
"AAVE",
|
|
1110
|
+
"SUI",
|
|
1111
|
+
"ONDO",
|
|
1112
|
+
"AVAX",
|
|
1113
|
+
"DOGE",
|
|
1114
|
+
"VVV",
|
|
1115
|
+
"BNB",
|
|
1116
|
+
"ALGO",
|
|
1117
|
+
"XMR",
|
|
1118
|
+
"MEGA",
|
|
1119
|
+
"LINK",
|
|
1120
|
+
"TAO"
|
|
1121
|
+
]
|
|
1122
|
+
};
|
|
1123
|
+
|
|
1124
|
+
// src/config/hyperliquidWhales.json
|
|
1125
|
+
var hyperliquidWhales_default = {
|
|
1126
|
+
schemaVersion: 2,
|
|
1127
|
+
updatedAt: "2026-08-04T15:10:21.316Z",
|
|
1128
|
+
source: "hyperliquid_structural_fills_snapshot",
|
|
1129
|
+
selection: {
|
|
1130
|
+
calibrationFrom: "2026-07-28T00:00:00.000Z",
|
|
1131
|
+
calibrationTo: "2026-08-04T00:00:00.000Z",
|
|
1132
|
+
effectiveFrom: "2026-08-04T00:00:00.000Z",
|
|
1133
|
+
candidateLimit: 400,
|
|
1134
|
+
minimumAccountValueUsd: 5e5,
|
|
1135
|
+
minimumActiveDays: 1,
|
|
1136
|
+
maximumRawFillsPerDay: 20,
|
|
1137
|
+
maximumDirectionalExecutionsPerDay: 10,
|
|
1138
|
+
minimumMedianNotionalUsd: 0,
|
|
1139
|
+
minimumMedianInterExecutionMinutes: 0,
|
|
1140
|
+
minimumTop30NotionalShare: 1e-3,
|
|
1141
|
+
minimumTurnoverToEquity: 0.05,
|
|
1142
|
+
maximumTurnoverToEquity: 5,
|
|
1143
|
+
score: "log1p(accountValue)*log1p(medianExecutionNotional)*log1p(max(1,medianInterExecutionMinutes))*sqrt(activeDays)*top30Share/sqrt(1+rawFillsPerDay)",
|
|
1144
|
+
forbiddenSelectionMetrics: ["pnl", "roi", "winRate", "closedPnl"]
|
|
1145
|
+
},
|
|
1146
|
+
fingerprint: "77a6e9d41222efdb",
|
|
1147
|
+
size: 100,
|
|
1148
|
+
addresses: [
|
|
1149
|
+
"0xacb66d1996501bcc82a0e7819c9e041599f5efbd",
|
|
1150
|
+
"0x90dbb196cacf24bd212b377a0dd62eaad5e8151b",
|
|
1151
|
+
"0xde5b8c8e713f7c29ec72c22a326d61e5ac76e402",
|
|
1152
|
+
"0x22edc38749b59c31c9319eae7fc62143ac2fcc6c",
|
|
1153
|
+
"0x4bc9a771cd2c96cb2a4501f2c049402b4e7e11b3",
|
|
1154
|
+
"0x9a0825ca6c4c577a1202a8fae3b8f044c1d5711d",
|
|
1155
|
+
"0xb6ca5d1ed0ca4bf8444c9dee2d068a9f4c5e2e92",
|
|
1156
|
+
"0xc3610d02548cdb1a30ede87c5dc0d2ffe5b9b99f",
|
|
1157
|
+
"0x48c6aaa647ad5197e85797f8eb95d7b9ce1ec5b6",
|
|
1158
|
+
"0x8c1ab015da7e5319649afca4a551924f89eff883",
|
|
1159
|
+
"0x64bddab5f13a99576fca6747f0aa205422c5f708",
|
|
1160
|
+
"0x98785f2a43fe32f972476b86ed5a5cfaf5240f00",
|
|
1161
|
+
"0x9836e286fbf4f71998827d4bd6ea3b470c26457e",
|
|
1162
|
+
"0x8f8d2d2565bfb10608a7ce64b48e2aea7875a344",
|
|
1163
|
+
"0xe6df4ed7a6ccb8426cbc87bdcdd3cc6d0e6c0690",
|
|
1164
|
+
"0xab04d040dec929a204298d44701476968b238b69",
|
|
1165
|
+
"0x870f718a84843abc3e22412a9b4dc2924466d448",
|
|
1166
|
+
"0x12c206d3e2f0d0059420c54d33fe604ebbecc230",
|
|
1167
|
+
"0x7d9e91a8a47eb553cbfd323e7633c0c213327124",
|
|
1168
|
+
"0x2057d4f2b6ea957b63371980775b0a5505a438f0",
|
|
1169
|
+
"0xead5b7d86c681c036c59cd00a0390541061c69f2",
|
|
1170
|
+
"0x3005fade4c0df5e1cd187d7062da359416f0eb8e",
|
|
1171
|
+
"0xe83b5e3644b50e802a25b34b38b91a061a0d3ca8",
|
|
1172
|
+
"0x525232cb6ed5030d2b052b36bfc0baec96986ee3",
|
|
1173
|
+
"0xa251520154ca342f0b1d702bf5a56f78c982405b",
|
|
1174
|
+
"0xa9184bb57fd75c7c63e4c074d6319eceac6be53b",
|
|
1175
|
+
"0xa20fb0c9e04063eec5be286e9269028d966646fa",
|
|
1176
|
+
"0x169ba96d963e77a87dbe58922ecdcdd0f0cf3315",
|
|
1177
|
+
"0x115849ce84370f25cadcf0d348510d73837e1aa5",
|
|
1178
|
+
"0x37b81ab9e3ab04b9e3738e4891621205aaa31fd5",
|
|
1179
|
+
"0x90f57d90a7dd3114312d4c31803ad5732baf5935",
|
|
1180
|
+
"0xad227f63d34e7251c1d0ab65e64eeea07aee4e44",
|
|
1181
|
+
"0xd38e2b0b3c85ae138532f9fe1a1d5f5455866b82",
|
|
1182
|
+
"0xf5f4c3aa62a0f903a8529905031b1536d1cf16f8",
|
|
1183
|
+
"0x75632aaf83120eae9b0618f8f50773ce347de2ad",
|
|
1184
|
+
"0x3927af597c09774f91ef84bf765ded4109e9f8ea",
|
|
1185
|
+
"0xf1863bd6d60a8f4b5f583ec54009af6a044877bf",
|
|
1186
|
+
"0xe7b34f28c54f85533896f8827edc92edac28b17f",
|
|
1187
|
+
"0x1cda20f9f6a23c209d54940af3ef63b96c9b2c31",
|
|
1188
|
+
"0x9e55b0e43b7f7f3909af841d326b2dadfda9392e",
|
|
1189
|
+
"0x610b8dd6b392d010c2b6ad8d121dfc09524cb613",
|
|
1190
|
+
"0x1d639a688b78f3d7e57166e59d9a8886b1bfb5a5",
|
|
1191
|
+
"0x34d3a17095ef7def39a489d63c7c916e673c9ca2",
|
|
1192
|
+
"0x5d0bde4017530ed43370e332a5d30194941f73c6",
|
|
1193
|
+
"0xaa7afc250041e2c56a06d3b7a5295bd01929e11b",
|
|
1194
|
+
"0x325edd95bb016c36027ce3b9f7595af7094a9564",
|
|
1195
|
+
"0x47f5a6397b79deaaca5c456503e7c1a49357b942",
|
|
1196
|
+
"0xf5e7ffe523d0491709958490b4877e1d9de6c6f7",
|
|
1197
|
+
"0x64702807a1f0f9888ea22b858099d7f481227b55",
|
|
1198
|
+
"0xd08a4557671ca465e7bba41110a93148c90f9931",
|
|
1199
|
+
"0x0423b4b2acb4f219ae4a270abd5dde2360e455bb",
|
|
1200
|
+
"0x7b29f8c0ea709b96b72214b3de5d85c3d9570794",
|
|
1201
|
+
"0xeb8ebad162a9ce4cb7e2325c92b960b9df63365c",
|
|
1202
|
+
"0x939f95036d2e7b6d7419ec072bf9d967352204d2",
|
|
1203
|
+
"0x2490aa2b48022e857d44e738911787c8e2296aed",
|
|
1204
|
+
"0x7622f628135378f1b8d1876952de9bab80245f1c",
|
|
1205
|
+
"0x9d677028e0e592d48fc8a8ddc910693301a4a450",
|
|
1206
|
+
"0x85e410e4c535cb1709dbd36468f132037a5b2087",
|
|
1207
|
+
"0xd6f52e2ab58a678eced4a4d1693757b0677631c8",
|
|
1208
|
+
"0x24d8969e2bd6aabb6c4dfe042ef8b2cd8085217e",
|
|
1209
|
+
"0xda51323fe9800c8365646ad5c7ade0dd17fdc167",
|
|
1210
|
+
"0x53f8f390fd4f70941c5d160a964f6893c8dbceff",
|
|
1211
|
+
"0x72dae3560e7f4c59a305e2e739d6bd6777f738b5",
|
|
1212
|
+
"0x36f5e448c9cf0032551e58919cf4eabefcd742c1",
|
|
1213
|
+
"0x5b43b7162694de766c164202df0f0c716e4292ab",
|
|
1214
|
+
"0xae551d73161bac3315c5ade0e2d499a44ebe2236",
|
|
1215
|
+
"0x4eca8d62e07b3f3e78a8ae8725d4666da324c5a8",
|
|
1216
|
+
"0x40a632026febd007b0e4431a29170507f7aa3896",
|
|
1217
|
+
"0xd453e91f692eb5437cf345e09545827576e3225d",
|
|
1218
|
+
"0xbe2c69eb3d7b8ee35cbe3a28293472be31179d75",
|
|
1219
|
+
"0xfdf891f2b214a4c9374d26595ec6d4080262e381",
|
|
1220
|
+
"0xfd97600ac44b3c4e20ac1a5f23e3b18d10fa5912",
|
|
1221
|
+
"0x98e073b579fd483eac8f10d5bd0b32c8c3bbd7e0",
|
|
1222
|
+
"0xb56f19e917546a60d658110a611cf62a6877b517",
|
|
1223
|
+
"0xbd334a55db91617545e8d1b09941e036ad8e4200",
|
|
1224
|
+
"0x9f1920d0cbb63ed03376a1e09fd2851d601234c8",
|
|
1225
|
+
"0xe9cef9fd872a03cedf9d3e6e90309782b3724ea6",
|
|
1226
|
+
"0xa43682a1f129e4faf0152175cc03c44e4957e8cf",
|
|
1227
|
+
"0x6fbb33de5f39c690d2decb95b9ab46ed0c83e3bc",
|
|
1228
|
+
"0x81d4163289cf61a0eaaac4862cb88ef0f4215c29",
|
|
1229
|
+
"0xd248d2f09bfbe04e67fc7fea08828d6ad6d95b6d",
|
|
1230
|
+
"0x01ffcc7285866e5cee14c8e807cb2e8d5f61079f",
|
|
1231
|
+
"0xc69ae428f6049e78d445f053d2c1df879c59b34c",
|
|
1232
|
+
"0xaab4dfe6d735c4ac46217216fe883a39fbfe8284",
|
|
1233
|
+
"0xe912cac1a6641004a8803687ee7699227fdb0550",
|
|
1234
|
+
"0x5fdbf83c3d95d525704f61401db68cbec1b2a775",
|
|
1235
|
+
"0xebd7c4b3677a4480fb5f69ac2f51c51d66a19990",
|
|
1236
|
+
"0xe4d1fa7e6141f9131345117bdb8b5701bca67662",
|
|
1237
|
+
"0x685feceec46dd4e5c9b5b726f5d7550fd0eda526",
|
|
1238
|
+
"0x3ea8b09da72972e953541500721803a187526da6",
|
|
1239
|
+
"0xdbe0780a5c5a0e3b345101bbc01ac2218196d6d5",
|
|
1240
|
+
"0x18cd4597e06b7fe0a8cd33dda499121b3a145a8b",
|
|
1241
|
+
"0xb8243756956e722bff5c763971b562ac551bfbd2",
|
|
1242
|
+
"0xe0bf8c96795d2df0b61b3b17e7cfd7d3ba13d4be",
|
|
1243
|
+
"0x218a65e21eddeece7a9df38c6bbdd89f692b7da2",
|
|
1244
|
+
"0x53b63a30a688beb53b5dc7bd731c661d678c555c",
|
|
1245
|
+
"0xfce053a5e461683454bf37ad66d20344c0e3f4c0",
|
|
1246
|
+
"0x3e516cf3c9d4f29fae6c1324c2414dc872fc9c09",
|
|
1247
|
+
"0xe5f1486dc84706d2509885fe6bbc46d21ba81286",
|
|
1248
|
+
"0x38042d713af713f0275b00640a83654db91ea543"
|
|
1249
|
+
]
|
|
1250
|
+
};
|
|
1251
|
+
|
|
1252
|
+
// src/hyperliquidWhaleUniverse.ts
|
|
1253
|
+
var fingerprint2 = (value) => createHash2("sha256").update(JSON.stringify(value)).digest("hex").slice(0, 16);
|
|
1254
|
+
var validateUnique = (values, label) => {
|
|
1255
|
+
if (new Set(values).size !== values.length) {
|
|
1256
|
+
throw new Error(`Hyperliquid ${label} snapshot contains duplicates`);
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
var validatePerpSnapshot = (value) => {
|
|
1260
|
+
const symbols = value.symbols.map((symbol) => symbol.trim()).filter(Boolean);
|
|
1261
|
+
validateUnique(symbols, "perp universe");
|
|
1262
|
+
if (value.schemaVersion !== 1 || value.source !== "hyperliquid_main_perp_day_notional_volume" || value.size !== 30 || symbols.length !== 30 || value.fingerprint !== fingerprint2(symbols)) {
|
|
1263
|
+
throw new Error("Invalid Hyperliquid perp universe snapshot");
|
|
1264
|
+
}
|
|
1265
|
+
return value;
|
|
1266
|
+
};
|
|
1267
|
+
var validateWhaleSnapshot = (value) => {
|
|
1268
|
+
const addresses = value.addresses.map((address) => address.toLowerCase());
|
|
1269
|
+
validateUnique(addresses, "whale registry");
|
|
1270
|
+
if (value.schemaVersion !== 2 || value.source !== "hyperliquid_structural_fills_snapshot" || value.size !== 100 || addresses.length !== 100 || addresses.some((address) => !/^0x[0-9a-f]{40}$/.test(address)) || value.fingerprint !== fingerprint2(addresses)) {
|
|
1271
|
+
throw new Error("Invalid Hyperliquid whale registry snapshot");
|
|
1272
|
+
}
|
|
1273
|
+
return value;
|
|
1274
|
+
};
|
|
1275
|
+
var perpSnapshot = validatePerpSnapshot(hyperliquidPerpUniverse_default);
|
|
1276
|
+
var whaleSnapshot = validateWhaleSnapshot(hyperliquidWhales_default);
|
|
1277
|
+
var perpSymbolSet = new Set(perpSnapshot.symbols);
|
|
1278
|
+
var whaleAddressSet = new Set(whaleSnapshot.addresses);
|
|
1279
|
+
var getHyperliquidPerpUniverseSnapshot = () => perpSnapshot;
|
|
1280
|
+
var getHyperliquidWhaleRegistrySnapshot = () => whaleSnapshot;
|
|
1281
|
+
var getHyperliquidPerpSymbols = () => [...perpSnapshot.symbols];
|
|
1282
|
+
var getHyperliquidWhaleAddresses = () => [...whaleSnapshot.addresses];
|
|
1283
|
+
var isTrackedHyperliquidPerp = (symbol) => perpSymbolSet.has(symbol.trim());
|
|
1284
|
+
var isTrackedHyperliquidWhale = (address) => whaleAddressSet.has(address.trim().toLowerCase());
|
|
1285
|
+
var SIGNAL_TO_HYPERLIQUID_ALIASES = {
|
|
1286
|
+
PEPE: "kPEPE",
|
|
1287
|
+
"1000PEPE": "kPEPE",
|
|
1288
|
+
PUMPFUN: "PUMP",
|
|
1289
|
+
SHIB: "kSHIB",
|
|
1290
|
+
BONK: "kBONK"
|
|
1291
|
+
};
|
|
1292
|
+
var resolveHyperliquidPerpFromSignalSymbol = (symbol) => {
|
|
1293
|
+
const normalized = symbol.trim().toUpperCase().replace(/(?:USDT|USDC)$/, "");
|
|
1294
|
+
const candidate = SIGNAL_TO_HYPERLIQUID_ALIASES[normalized] ?? normalized;
|
|
1295
|
+
return perpSymbolSet.has(candidate) ? candidate : null;
|
|
1296
|
+
};
|
|
1297
|
+
|
|
1298
|
+
// src/strategyHelpers/hyperliquidWhaleContext.ts
|
|
1299
|
+
import { intervalToMs } from "@tradejs/core/data";
|
|
1300
|
+
import { refreshSignalBaseContextGateFeatures as refreshSignalBaseContextGateFeatures3 } from "@tradejs/core/strategies";
|
|
1301
|
+
import {
|
|
1302
|
+
getHyperliquidWhaleCoverageSeriesRows,
|
|
1303
|
+
getHyperliquidWhaleFlowAggregate,
|
|
1304
|
+
getHyperliquidWhaleFlowSeriesRows
|
|
1305
|
+
} from "@tradejs/infra/timescale";
|
|
1306
|
+
import { logger as logger3 } from "@tradejs/infra/logger";
|
|
1307
|
+
var MAX_CACHE_ENTRIES = 2048;
|
|
1308
|
+
var MAX_COVERAGE_SERIES_CACHE_ENTRIES = 20;
|
|
1309
|
+
var MAX_FLOW_SERIES_CACHE_ENTRIES = 64;
|
|
1310
|
+
var SERIES_CHUNK_MS = 30 * 24 * 60 * 6e4;
|
|
1311
|
+
var MAX_SERIES_LOOKBACK_MS = 60 * 6e4;
|
|
1312
|
+
var DEFAULT_MIN_COVERAGE_PCT = 0.8;
|
|
1313
|
+
var INTERVAL_MS = {
|
|
1314
|
+
"1m": 6e4,
|
|
1315
|
+
"5m": 5 * 6e4,
|
|
1316
|
+
"15m": 15 * 6e4,
|
|
1317
|
+
"1h": 60 * 6e4
|
|
1318
|
+
};
|
|
1319
|
+
var DEFAULT_MAX_AGE_BY_INTERVAL2 = {
|
|
1320
|
+
"1m": 2 * 6e4,
|
|
1321
|
+
"5m": 10 * 6e4,
|
|
1322
|
+
"15m": 30 * 6e4,
|
|
1323
|
+
"1h": 2 * 60 * 6e4
|
|
1324
|
+
};
|
|
1325
|
+
var hyperliquidWhaleContextUnavailable = false;
|
|
1326
|
+
var contextCache = /* @__PURE__ */ new Map();
|
|
1327
|
+
var coverageSeriesCache = /* @__PURE__ */ new Map();
|
|
1328
|
+
var flowSeriesCache = /* @__PURE__ */ new Map();
|
|
1329
|
+
var parseEnabledFlag3 = (value, env) => {
|
|
1330
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
1331
|
+
if (!normalized) return env !== "TEST";
|
|
1332
|
+
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
1333
|
+
if (["0", "false", "no", "off"].includes(normalized)) return false;
|
|
1334
|
+
if (normalized === "backtest") return env === "BACKTEST";
|
|
1335
|
+
if (normalized === "live") return env !== "BACKTEST";
|
|
1336
|
+
return false;
|
|
1337
|
+
};
|
|
1338
|
+
var getMinimumCoveragePct = () => {
|
|
1339
|
+
const parsed = Number(process.env.HYPERLIQUID_WHALE_MIN_COVERAGE_PCT);
|
|
1340
|
+
return Number.isFinite(parsed) ? Math.max(0, Math.min(1, parsed)) : DEFAULT_MIN_COVERAGE_PCT;
|
|
1341
|
+
};
|
|
1342
|
+
var signalIntervalToMarketInterval2 = (value) => {
|
|
1343
|
+
const normalized = String(value).trim().toLowerCase();
|
|
1344
|
+
if (normalized === "1" || normalized === "1m") return "1m";
|
|
1345
|
+
if (normalized === "5" || normalized === "5m") return "5m";
|
|
1346
|
+
if (normalized === "60" || normalized === "1h") return "1h";
|
|
1347
|
+
return "15m";
|
|
1348
|
+
};
|
|
1349
|
+
var hasBaseContext3 = (signal) => Boolean(
|
|
1350
|
+
signal.additionalIndicators?.baseContext && typeof signal.additionalIndicators.baseContext === "object" && !Array.isArray(signal.additionalIndicators.baseContext)
|
|
1351
|
+
);
|
|
1352
|
+
var setBoundedCache = (key, value) => {
|
|
1353
|
+
if (contextCache.size >= MAX_CACHE_ENTRIES) {
|
|
1354
|
+
const oldestKey = contextCache.keys().next().value;
|
|
1355
|
+
if (oldestKey != null) contextCache.delete(oldestKey);
|
|
1356
|
+
}
|
|
1357
|
+
contextCache.set(key, value);
|
|
1358
|
+
};
|
|
1359
|
+
var setBoundedSeriesCache = (cache, maxEntries, key, value) => {
|
|
1360
|
+
if (cache.size >= maxEntries) {
|
|
1361
|
+
const oldestKey = cache.keys().next().value;
|
|
1362
|
+
if (oldestKey != null) cache.delete(oldestKey);
|
|
1363
|
+
}
|
|
1364
|
+
cache.set(key, value);
|
|
1365
|
+
};
|
|
1366
|
+
var uniqueAddressCount = (rows, field) => {
|
|
1367
|
+
const addresses = /* @__PURE__ */ new Set();
|
|
1368
|
+
for (const row of rows) {
|
|
1369
|
+
for (const address of row[field]) addresses.add(address);
|
|
1370
|
+
}
|
|
1371
|
+
return addresses.size;
|
|
1372
|
+
};
|
|
1373
|
+
var sumFlowField = (rows, field) => rows.reduce((sum, row) => sum + row[field], 0);
|
|
1374
|
+
var sliceSeriesWindow = (rows, fromMs, toMs) => {
|
|
1375
|
+
const lowerBound = (timestamp) => {
|
|
1376
|
+
let low = 0;
|
|
1377
|
+
let high = rows.length;
|
|
1378
|
+
while (low < high) {
|
|
1379
|
+
const middle = low + Math.floor((high - low) / 2);
|
|
1380
|
+
if (rows[middle].ts.getTime() < timestamp) low = middle + 1;
|
|
1381
|
+
else high = middle;
|
|
1382
|
+
}
|
|
1383
|
+
return low;
|
|
1384
|
+
};
|
|
1385
|
+
return rows.slice(lowerBound(fromMs), lowerBound(toMs));
|
|
1386
|
+
};
|
|
1387
|
+
var aggregateHyperliquidWhaleFlowSeries = (params) => {
|
|
1388
|
+
const intervalMs = INTERVAL_MS[params.interval];
|
|
1389
|
+
const windowStartMs = params.decisionTimeMs - intervalMs;
|
|
1390
|
+
const coverageRows = sliceSeriesWindow(
|
|
1391
|
+
params.coverageRows,
|
|
1392
|
+
windowStartMs,
|
|
1393
|
+
params.decisionTimeMs
|
|
1394
|
+
);
|
|
1395
|
+
const expectedBuckets = Math.ceil(intervalMs / 6e4);
|
|
1396
|
+
if (coverageRows.length !== expectedBuckets || coverageRows.some((row) => row.coveredWhales <= 0)) {
|
|
1397
|
+
return null;
|
|
1398
|
+
}
|
|
1399
|
+
const flowRows = sliceSeriesWindow(
|
|
1400
|
+
params.flowRows,
|
|
1401
|
+
windowStartMs,
|
|
1402
|
+
params.decisionTimeMs
|
|
1403
|
+
);
|
|
1404
|
+
const asOfTs = new Date(
|
|
1405
|
+
Math.max(...coverageRows.map((row) => row.ts.getTime()))
|
|
1406
|
+
);
|
|
1407
|
+
const coveredWhales = Math.min(
|
|
1408
|
+
...coverageRows.map((row) => row.coveredWhales)
|
|
1409
|
+
);
|
|
1410
|
+
const expectedWhales = Math.max(
|
|
1411
|
+
...coverageRows.map((row) => row.expectedWhales)
|
|
1412
|
+
);
|
|
1413
|
+
const coveragePct = Math.min(...coverageRows.map((row) => row.coveragePct));
|
|
1414
|
+
const trades = sumFlowField(flowRows, "trades");
|
|
1415
|
+
const whaleSides = sumFlowField(flowRows, "whaleSides");
|
|
1416
|
+
const buyNotionalUsd = sumFlowField(flowRows, "buyNotionalUsd");
|
|
1417
|
+
const sellNotionalUsd = sumFlowField(flowRows, "sellNotionalUsd");
|
|
1418
|
+
const positionAwareWhaleSides = sumFlowField(
|
|
1419
|
+
flowRows,
|
|
1420
|
+
"positionAwareWhaleSides"
|
|
1421
|
+
);
|
|
1422
|
+
const longEntryNotionalUsd = sumFlowField(flowRows, "longEntryNotionalUsd");
|
|
1423
|
+
const shortEntryNotionalUsd = sumFlowField(flowRows, "shortEntryNotionalUsd");
|
|
1424
|
+
const longExitNotionalUsd = sumFlowField(flowRows, "longExitNotionalUsd");
|
|
1425
|
+
const shortExitNotionalUsd = sumFlowField(flowRows, "shortExitNotionalUsd");
|
|
1426
|
+
const totalNotionalUsd = buyNotionalUsd + sellNotionalUsd;
|
|
1427
|
+
const totalEntryNotionalUsd = longEntryNotionalUsd + shortEntryNotionalUsd;
|
|
1428
|
+
const ageMs = params.decisionTimeMs - (asOfTs.getTime() + 6e4);
|
|
1429
|
+
return {
|
|
1430
|
+
symbol: params.symbol,
|
|
1431
|
+
interval: params.interval,
|
|
1432
|
+
asOfTs,
|
|
1433
|
+
windowEndTs: new Date(params.decisionTimeMs),
|
|
1434
|
+
trades,
|
|
1435
|
+
whaleSides,
|
|
1436
|
+
uniqueWhales: uniqueAddressCount(flowRows, "whaleAddresses"),
|
|
1437
|
+
coveredWhales,
|
|
1438
|
+
expectedWhales,
|
|
1439
|
+
coveragePct,
|
|
1440
|
+
buyNotionalUsd,
|
|
1441
|
+
sellNotionalUsd,
|
|
1442
|
+
netNotionalUsd: buyNotionalUsd - sellNotionalUsd,
|
|
1443
|
+
buySharePct: totalNotionalUsd > 0 ? buyNotionalUsd / totalNotionalUsd : null,
|
|
1444
|
+
positionAwareWhaleSides,
|
|
1445
|
+
positionAwarePct: whaleSides > 0 ? positionAwareWhaleSides / whaleSides : 0,
|
|
1446
|
+
longEntryWhales: uniqueAddressCount(flowRows, "longEntryWhaleAddresses"),
|
|
1447
|
+
shortEntryWhales: uniqueAddressCount(flowRows, "shortEntryWhaleAddresses"),
|
|
1448
|
+
longExitWhales: uniqueAddressCount(flowRows, "longExitWhaleAddresses"),
|
|
1449
|
+
shortExitWhales: uniqueAddressCount(flowRows, "shortExitWhaleAddresses"),
|
|
1450
|
+
longEntryNotionalUsd,
|
|
1451
|
+
shortEntryNotionalUsd,
|
|
1452
|
+
longExitNotionalUsd,
|
|
1453
|
+
shortExitNotionalUsd,
|
|
1454
|
+
entryNetNotionalUsd: longEntryNotionalUsd - shortEntryNotionalUsd,
|
|
1455
|
+
entryLongSharePct: totalEntryNotionalUsd > 0 ? longEntryNotionalUsd / totalEntryNotionalUsd : null,
|
|
1456
|
+
universeFingerprint: params.universeFingerprint,
|
|
1457
|
+
whaleRegistryFingerprint: params.whaleRegistryFingerprint,
|
|
1458
|
+
source: positionAwareWhaleSides > 0 ? "hyperliquid_user_fills" : null,
|
|
1459
|
+
ageMs,
|
|
1460
|
+
stale: ageMs < 0 || (params.maxAgeMs != null && Number.isFinite(params.maxAgeMs) ? ageMs > params.maxAgeMs : false)
|
|
1461
|
+
};
|
|
1462
|
+
};
|
|
1463
|
+
var loadHyperliquidWhaleFlowAggregateFromSeries = async (params) => {
|
|
1464
|
+
const chunkStartMs = Math.floor(params.decisionTimeMs / SERIES_CHUNK_MS) * SERIES_CHUNK_MS;
|
|
1465
|
+
const fromMs = chunkStartMs - MAX_SERIES_LOOKBACK_MS;
|
|
1466
|
+
const toMs = chunkStartMs + SERIES_CHUNK_MS;
|
|
1467
|
+
const sharedKey = [
|
|
1468
|
+
fromMs,
|
|
1469
|
+
toMs,
|
|
1470
|
+
params.universeFingerprint,
|
|
1471
|
+
params.whaleRegistryFingerprint
|
|
1472
|
+
].join(":");
|
|
1473
|
+
let pendingCoverage = coverageSeriesCache.get(sharedKey);
|
|
1474
|
+
if (!pendingCoverage) {
|
|
1475
|
+
pendingCoverage = getHyperliquidWhaleCoverageSeriesRows({
|
|
1476
|
+
fromMs,
|
|
1477
|
+
toMs,
|
|
1478
|
+
universeFingerprint: params.universeFingerprint,
|
|
1479
|
+
whaleRegistryFingerprint: params.whaleRegistryFingerprint
|
|
1480
|
+
});
|
|
1481
|
+
setBoundedSeriesCache(
|
|
1482
|
+
coverageSeriesCache,
|
|
1483
|
+
MAX_COVERAGE_SERIES_CACHE_ENTRIES,
|
|
1484
|
+
sharedKey,
|
|
1485
|
+
pendingCoverage
|
|
1486
|
+
);
|
|
1487
|
+
}
|
|
1488
|
+
const flowKey = `${params.symbol}:${sharedKey}`;
|
|
1489
|
+
let pendingFlow = flowSeriesCache.get(flowKey);
|
|
1490
|
+
if (!pendingFlow) {
|
|
1491
|
+
pendingFlow = getHyperliquidWhaleFlowSeriesRows({
|
|
1492
|
+
symbol: params.symbol,
|
|
1493
|
+
fromMs,
|
|
1494
|
+
toMs,
|
|
1495
|
+
universeFingerprint: params.universeFingerprint,
|
|
1496
|
+
whaleRegistryFingerprint: params.whaleRegistryFingerprint
|
|
1497
|
+
});
|
|
1498
|
+
setBoundedSeriesCache(
|
|
1499
|
+
flowSeriesCache,
|
|
1500
|
+
MAX_FLOW_SERIES_CACHE_ENTRIES,
|
|
1501
|
+
flowKey,
|
|
1502
|
+
pendingFlow
|
|
1503
|
+
);
|
|
1504
|
+
}
|
|
1505
|
+
const [coverageRows, flowRows] = await Promise.all([
|
|
1506
|
+
pendingCoverage,
|
|
1507
|
+
pendingFlow
|
|
1508
|
+
]);
|
|
1509
|
+
return aggregateHyperliquidWhaleFlowSeries({
|
|
1510
|
+
...params,
|
|
1511
|
+
coverageRows,
|
|
1512
|
+
flowRows
|
|
1513
|
+
});
|
|
1514
|
+
};
|
|
1515
|
+
var toBaseHyperliquidWhaleFlowContext = (row, minimumCoveragePct) => ({
|
|
1516
|
+
source: row.positionAwareWhaleSides > 0 ? "hyperliquid_user_fills" : "hyperliquid_trades",
|
|
1517
|
+
interval: row.interval,
|
|
1518
|
+
asOfTs: row.asOfTs.getTime(),
|
|
1519
|
+
windowEndTs: row.windowEndTs.getTime(),
|
|
1520
|
+
ageMs: row.ageMs,
|
|
1521
|
+
stale: row.stale,
|
|
1522
|
+
symbol: row.symbol,
|
|
1523
|
+
trades: row.trades,
|
|
1524
|
+
whaleSides: row.whaleSides,
|
|
1525
|
+
uniqueWhales: row.uniqueWhales,
|
|
1526
|
+
coveredWhales: row.coveredWhales,
|
|
1527
|
+
expectedWhales: row.expectedWhales,
|
|
1528
|
+
coveragePct: row.coveragePct,
|
|
1529
|
+
coverageSufficient: row.coveragePct >= minimumCoveragePct,
|
|
1530
|
+
buyNotionalUsd: row.buyNotionalUsd,
|
|
1531
|
+
sellNotionalUsd: row.sellNotionalUsd,
|
|
1532
|
+
netNotionalUsd: row.netNotionalUsd,
|
|
1533
|
+
buySharePct: row.buySharePct,
|
|
1534
|
+
positionAwareWhaleSides: row.positionAwareWhaleSides,
|
|
1535
|
+
positionAwarePct: row.positionAwarePct,
|
|
1536
|
+
longEntryWhales: row.longEntryWhales,
|
|
1537
|
+
shortEntryWhales: row.shortEntryWhales,
|
|
1538
|
+
longExitWhales: row.longExitWhales,
|
|
1539
|
+
shortExitWhales: row.shortExitWhales,
|
|
1540
|
+
longEntryNotionalUsd: row.longEntryNotionalUsd,
|
|
1541
|
+
shortEntryNotionalUsd: row.shortEntryNotionalUsd,
|
|
1542
|
+
longExitNotionalUsd: row.longExitNotionalUsd,
|
|
1543
|
+
shortExitNotionalUsd: row.shortExitNotionalUsd,
|
|
1544
|
+
entryNetNotionalUsd: row.entryNetNotionalUsd,
|
|
1545
|
+
entryLongSharePct: row.entryLongSharePct,
|
|
1546
|
+
universeFingerprint: row.universeFingerprint,
|
|
1547
|
+
whaleRegistryFingerprint: row.whaleRegistryFingerprint
|
|
1548
|
+
});
|
|
1549
|
+
var isHyperliquidWhaleContextEnabled = (env) => parseEnabledFlag3(process.env.HYPERLIQUID_WHALE_CONTEXT_ENABLED, env);
|
|
1550
|
+
var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
1551
|
+
if (!(params.enabled ?? isHyperliquidWhaleContextEnabled(params.env)) || hyperliquidWhaleContextUnavailable || params.interval == null) {
|
|
1552
|
+
return null;
|
|
1553
|
+
}
|
|
1554
|
+
const symbol = resolveHyperliquidPerpFromSignalSymbol(params.symbol);
|
|
1555
|
+
if (!symbol) return null;
|
|
1556
|
+
const interval = params.marketInterval ?? signalIntervalToMarketInterval2(params.interval);
|
|
1557
|
+
const decisionTimeMs = params.timestamp + intervalToMs(params.interval);
|
|
1558
|
+
const maxAgeMs = params.maxAgeMs ?? DEFAULT_MAX_AGE_BY_INTERVAL2[interval];
|
|
1559
|
+
const universe = getHyperliquidPerpUniverseSnapshot();
|
|
1560
|
+
const whales = getHyperliquidWhaleRegistrySnapshot();
|
|
1561
|
+
const minimumCoveragePct = getMinimumCoveragePct();
|
|
1562
|
+
const cacheKey = [
|
|
1563
|
+
symbol,
|
|
1564
|
+
interval,
|
|
1565
|
+
decisionTimeMs,
|
|
1566
|
+
maxAgeMs,
|
|
1567
|
+
universe.fingerprint,
|
|
1568
|
+
whales.fingerprint,
|
|
1569
|
+
minimumCoveragePct
|
|
1570
|
+
].join(":");
|
|
1571
|
+
try {
|
|
1572
|
+
let row;
|
|
1573
|
+
if (params.useSeriesCache) {
|
|
1574
|
+
row = await loadHyperliquidWhaleFlowAggregateFromSeries({
|
|
1575
|
+
symbol,
|
|
1576
|
+
interval,
|
|
1577
|
+
decisionTimeMs,
|
|
1578
|
+
maxAgeMs,
|
|
1579
|
+
universeFingerprint: universe.fingerprint,
|
|
1580
|
+
whaleRegistryFingerprint: whales.fingerprint
|
|
1581
|
+
});
|
|
1582
|
+
} else {
|
|
1583
|
+
let pending = contextCache.get(cacheKey);
|
|
1584
|
+
if (!pending) {
|
|
1585
|
+
pending = getHyperliquidWhaleFlowAggregate({
|
|
1586
|
+
symbol,
|
|
1587
|
+
interval,
|
|
1588
|
+
decisionTimeMs,
|
|
1589
|
+
maxAgeMs,
|
|
1590
|
+
universeFingerprint: universe.fingerprint,
|
|
1591
|
+
whaleRegistryFingerprint: whales.fingerprint
|
|
1592
|
+
});
|
|
1593
|
+
setBoundedCache(cacheKey, pending);
|
|
1594
|
+
}
|
|
1595
|
+
row = await pending;
|
|
1596
|
+
}
|
|
1597
|
+
if (!row) return null;
|
|
1598
|
+
return toBaseHyperliquidWhaleFlowContext(row, minimumCoveragePct);
|
|
1599
|
+
} catch (error) {
|
|
1600
|
+
hyperliquidWhaleContextUnavailable = true;
|
|
1601
|
+
logger3.warn(
|
|
1602
|
+
"Hyperliquid whale context disabled after Timescale read failure: %s",
|
|
1603
|
+
String(error)
|
|
1604
|
+
);
|
|
1605
|
+
return null;
|
|
1606
|
+
}
|
|
1607
|
+
};
|
|
1608
|
+
var enrichSignalWithHyperliquidWhaleContext = async (params) => {
|
|
1609
|
+
const { signal, env } = params;
|
|
1610
|
+
if (signal.universe === "tradfi" || signal.interval == null || !hasBaseContext3(signal)) {
|
|
1611
|
+
return false;
|
|
1612
|
+
}
|
|
1613
|
+
const hyperliquidWhales = await loadHyperliquidWhaleFlowContext({
|
|
1614
|
+
symbol: signal.symbol,
|
|
1615
|
+
interval: signal.interval,
|
|
1616
|
+
timestamp: signal.timestamp,
|
|
1617
|
+
env,
|
|
1618
|
+
enabled: params.enabled,
|
|
1619
|
+
marketInterval: params.interval,
|
|
1620
|
+
maxAgeMs: params.maxAgeMs
|
|
1621
|
+
});
|
|
1622
|
+
if (!hyperliquidWhales) return false;
|
|
1623
|
+
const baseContext = signal.additionalIndicators.baseContext;
|
|
1624
|
+
signal.additionalIndicators = {
|
|
1625
|
+
...signal.additionalIndicators,
|
|
1626
|
+
baseContext: {
|
|
1627
|
+
...baseContext,
|
|
1628
|
+
participation: {
|
|
1629
|
+
...baseContext.participation,
|
|
1630
|
+
hyperliquidWhales
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
};
|
|
1634
|
+
refreshSignalBaseContextGateFeatures3(signal);
|
|
1635
|
+
return true;
|
|
1636
|
+
};
|
|
1637
|
+
|
|
1080
1638
|
// src/strategyHelpers/derivativesContext.ts
|
|
1081
1639
|
import {
|
|
1082
1640
|
buildDerivativesContext,
|
|
1083
1641
|
buildCoinalyzeHourlyRowsWithFallback,
|
|
1084
1642
|
getLastClosedDerivativesBarStartMs
|
|
1085
1643
|
} from "@tradejs/core/indicators";
|
|
1086
|
-
import { intervalToMs } from "@tradejs/core/data";
|
|
1087
|
-
import { refreshSignalBaseContextGateFeatures as
|
|
1644
|
+
import { intervalToMs as intervalToMs2 } from "@tradejs/core/data";
|
|
1645
|
+
import { refreshSignalBaseContextGateFeatures as refreshSignalBaseContextGateFeatures4 } from "@tradejs/core/strategies";
|
|
1088
1646
|
import {
|
|
1089
1647
|
DERIVATIVES_CONTEXT_BASE_REFERENCE_SYMBOLS,
|
|
1090
1648
|
resolveDerivativesContextReferenceSymbols
|
|
1091
1649
|
} from "@tradejs/core/constants";
|
|
1092
1650
|
import { getDerivativesWindow } from "@tradejs/infra/timescale";
|
|
1093
|
-
import { logger as
|
|
1651
|
+
import { logger as logger4 } from "@tradejs/infra/logger";
|
|
1094
1652
|
var STORED_INTERVALS = ["15m", "1h"];
|
|
1095
1653
|
var CONTEXT_INTERVALS = ["15m", "1h"];
|
|
1096
1654
|
var DEFAULT_LOOKBACK_HOURS = 48;
|
|
1097
1655
|
var PRIMARY_DERIVATIVES_REFERENCE_SYMBOL = DERIVATIVES_CONTEXT_BASE_REFERENCE_SYMBOLS[0];
|
|
1098
1656
|
var SECONDARY_DERIVATIVES_REFERENCE_SYMBOL = DERIVATIVES_CONTEXT_BASE_REFERENCE_SYMBOLS[1];
|
|
1099
1657
|
var derivativesContextUnavailable = false;
|
|
1100
|
-
var
|
|
1658
|
+
var parseEnabledFlag4 = (value, env) => {
|
|
1101
1659
|
const normalized = String(value ?? "").trim().toLowerCase();
|
|
1102
1660
|
if (!normalized) return true;
|
|
1103
1661
|
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
@@ -1235,7 +1793,7 @@ var buildReferenceDerivativesContext = (params) => {
|
|
|
1235
1793
|
} : {}
|
|
1236
1794
|
};
|
|
1237
1795
|
};
|
|
1238
|
-
var isDerivativesContextEnabled = (env) =>
|
|
1796
|
+
var isDerivativesContextEnabled = (env) => parseEnabledFlag4(process.env.DERIVATIVES_CONTEXT_ENABLED, env);
|
|
1239
1797
|
var isDerivativesTargetContextEnabled = () => parseBooleanFlag(process.env.DERIVATIVES_CONTEXT_TARGET_ENABLED, false);
|
|
1240
1798
|
var enrichSignalWithDerivativesContext = async (params) => {
|
|
1241
1799
|
const { signal, env, enabled = isDerivativesContextEnabled(env) } = params;
|
|
@@ -1246,7 +1804,7 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
1246
1804
|
const referenceSymbols = getDerivativesContextReferenceSymbols();
|
|
1247
1805
|
const targetSymbol = normalizeSymbol(signal.symbol);
|
|
1248
1806
|
const lookbackMs = parseLookbackMs();
|
|
1249
|
-
const decisionTimeMs = signal.timestamp +
|
|
1807
|
+
const decisionTimeMs = signal.timestamp + intervalToMs2(signal.interval);
|
|
1250
1808
|
const derivativesEndMs = getLastClosedDerivativesBarStartMs(
|
|
1251
1809
|
decisionTimeMs,
|
|
1252
1810
|
"15m"
|
|
@@ -1313,11 +1871,11 @@ var enrichSignalWithDerivativesContext = async (params) => {
|
|
|
1313
1871
|
derivatives: derivativesContext
|
|
1314
1872
|
} : signal.additionalIndicators?.baseContext
|
|
1315
1873
|
};
|
|
1316
|
-
|
|
1874
|
+
refreshSignalBaseContextGateFeatures4(signal);
|
|
1317
1875
|
return true;
|
|
1318
1876
|
} catch (error) {
|
|
1319
1877
|
derivativesContextUnavailable = true;
|
|
1320
|
-
|
|
1878
|
+
logger4.warn(
|
|
1321
1879
|
"Derivatives context disabled after Timescale read failure: %s",
|
|
1322
1880
|
String(error)
|
|
1323
1881
|
);
|
|
@@ -1382,5 +1940,14 @@ export {
|
|
|
1382
1940
|
enrichSignalWithBinanceMarketContext,
|
|
1383
1941
|
enrichSignalWithCoinMarketCapContext,
|
|
1384
1942
|
enrichSignalWithDerivativesContext,
|
|
1943
|
+
getHyperliquidPerpUniverseSnapshot,
|
|
1944
|
+
getHyperliquidWhaleRegistrySnapshot,
|
|
1945
|
+
getHyperliquidPerpSymbols,
|
|
1946
|
+
getHyperliquidWhaleAddresses,
|
|
1947
|
+
isTrackedHyperliquidPerp,
|
|
1948
|
+
isTrackedHyperliquidWhale,
|
|
1949
|
+
resolveHyperliquidPerpFromSignalSymbol,
|
|
1950
|
+
loadHyperliquidWhaleFlowContext,
|
|
1951
|
+
enrichSignalWithHyperliquidWhaleContext,
|
|
1385
1952
|
buildMlPayload
|
|
1386
1953
|
};
|
package/dist/cli.js
CHANGED
|
@@ -6810,7 +6810,7 @@ Input payload structure:
|
|
|
6810
6810
|
\u2022 \`baseContext.raw\`: current MA, ATR, BB, OBV, price stats, levels, BTC correlation.
|
|
6811
6811
|
\u2022 \`baseContext.regime\`: derived trend / volatility / momentum / session regime fields.
|
|
6812
6812
|
\u2022 \`baseContext.structure\`: local range position, breakout freshness/quality, level-touch counts, rejection wick context.
|
|
6813
|
-
\u2022 \`baseContext.participation\`: volume/turnover participation, effort-vs-result context,
|
|
6813
|
+
\u2022 \`baseContext.participation\`: volume/turnover participation, effort-vs-result context, Binance aggTrades, and fingerprinted Hyperliquid whale-flow when available.
|
|
6814
6814
|
\u2022 \`baseContext.relative\`: BTC/ETH relative-strength, benchmark MA bias context, Binance alt-basket breadth, and CoinMarketCap historical global/exchange/index context when available.
|
|
6815
6815
|
\u2022 \`baseContext.derivatives\`: Coinalyze-aligned derivatives summary when available.
|
|
6816
6816
|
\u2022 \`baseContext.mtf\`: compact multi-timeframe summary plus only the latest few candles for each timeframe.
|