@tradejs/node 2.0.6 → 2.0.8
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 +47 -1
- package/dist/ai.mjs +1 -1
- package/dist/backtest.js +486 -84
- package/dist/backtest.mjs +30 -5
- package/dist/{chunk-2DZCHRP6.mjs → chunk-76TEMQKX.mjs} +418 -80
- package/dist/{chunk-IUZML4RK.mjs → chunk-PD25CABK.mjs} +47 -1
- package/dist/cli.js +47 -1
- package/dist/cli.mjs +1 -1
- package/dist/strategies.d.mts +49 -1
- package/dist/strategies.d.ts +49 -1
- package/dist/strategies.js +541 -90
- package/dist/strategies.mjs +78 -8
- package/package.json +4 -4
package/dist/backtest.js
CHANGED
|
@@ -43,6 +43,7 @@ module.exports = __toCommonJS(backtest_exports);
|
|
|
43
43
|
__reExport(backtest_exports, require("@tradejs/core/backtest"), module.exports);
|
|
44
44
|
|
|
45
45
|
// src/testing.ts
|
|
46
|
+
var import_types = require("@tradejs/types");
|
|
46
47
|
var import_indicators3 = require("@tradejs/core/indicators");
|
|
47
48
|
var import_constants4 = require("@tradejs/core/constants");
|
|
48
49
|
var import_strategies4 = require("@tradejs/core/strategies");
|
|
@@ -234,6 +235,51 @@ var buildMarketBreadthContextFromSignal = (signal) => {
|
|
|
234
235
|
dispersion: toFiniteNumber(breadth.dispersion)
|
|
235
236
|
};
|
|
236
237
|
};
|
|
238
|
+
var buildMarketBreadthsContextFromSignal = (signal) => {
|
|
239
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
240
|
+
const relative = toRecord(baseContext?.relative);
|
|
241
|
+
const breadths = toRecord(relative?.marketBreadths);
|
|
242
|
+
const build = (key) => {
|
|
243
|
+
const breadth = toRecord(breadths?.[key]);
|
|
244
|
+
if (!breadth) {
|
|
245
|
+
return {
|
|
246
|
+
source: null,
|
|
247
|
+
available: false,
|
|
248
|
+
universe: null,
|
|
249
|
+
interval: null,
|
|
250
|
+
stale: null,
|
|
251
|
+
symbolsCount: null,
|
|
252
|
+
advanceDeclineRatio: null,
|
|
253
|
+
pctAboveMa20: null,
|
|
254
|
+
pctAboveMa50: null,
|
|
255
|
+
equalWeightedReturn: null,
|
|
256
|
+
volumeWeightedReturn: null,
|
|
257
|
+
dispersion: null
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
source: String(breadth.source ?? ""),
|
|
262
|
+
available: true,
|
|
263
|
+
universe: String(breadth.universe ?? ""),
|
|
264
|
+
interval: String(breadth.interval ?? ""),
|
|
265
|
+
stale: typeof breadth.stale === "boolean" ? breadth.stale : null,
|
|
266
|
+
symbolsCount: toFiniteNumber(breadth.symbolsCount),
|
|
267
|
+
advanceDeclineRatio: toFiniteNumber(breadth.advanceDeclineRatio),
|
|
268
|
+
pctAboveMa20: toFiniteNumber(breadth.pctAboveMa20),
|
|
269
|
+
pctAboveMa50: toFiniteNumber(breadth.pctAboveMa50),
|
|
270
|
+
equalWeightedReturn: toFiniteNumber(breadth.equalWeightedReturn),
|
|
271
|
+
volumeWeightedReturn: toFiniteNumber(breadth.volumeWeightedReturn),
|
|
272
|
+
dispersion: toFiniteNumber(breadth.dispersion)
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
return {
|
|
276
|
+
top5: build("top5"),
|
|
277
|
+
top10: build("top10"),
|
|
278
|
+
top30: build("top30"),
|
|
279
|
+
top50: build("top50"),
|
|
280
|
+
top100: build("top100")
|
|
281
|
+
};
|
|
282
|
+
};
|
|
237
283
|
var buildTargetVsBtcContextFromSignal = (signal) => {
|
|
238
284
|
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
239
285
|
const relative = toRecord(baseContext?.relative);
|
|
@@ -571,6 +617,7 @@ var buildAiMarketContext = (signal) => ({
|
|
|
571
617
|
},
|
|
572
618
|
relative: {
|
|
573
619
|
marketBreadth: buildMarketBreadthContextFromSignal(signal),
|
|
620
|
+
marketBreadths: buildMarketBreadthsContextFromSignal(signal),
|
|
574
621
|
targetVsBtc: buildTargetVsBtcContextFromSignal(signal),
|
|
575
622
|
btcAltRegime: buildBtcAltRegimeContextFromSignal(signal),
|
|
576
623
|
cmcGlobal: buildCmcGlobalContextFromSignal(signal),
|
|
@@ -1110,6 +1157,311 @@ var buildAiPayload = (signal) => buildAiPayloadByStrategy(signal);
|
|
|
1110
1157
|
var import_timescale = require("@tradejs/infra/timescale");
|
|
1111
1158
|
var import_logger3 = require("@tradejs/infra/logger");
|
|
1112
1159
|
var import_strategies = require("@tradejs/core/strategies");
|
|
1160
|
+
|
|
1161
|
+
// src/binanceBreadthUniverses.ts
|
|
1162
|
+
var import_node_crypto = require("crypto");
|
|
1163
|
+
|
|
1164
|
+
// src/config/binanceBreadthUniverses.json
|
|
1165
|
+
var binanceBreadthUniverses_default = {
|
|
1166
|
+
schemaVersion: 1,
|
|
1167
|
+
updatedAt: "2026-07-24T15:28:27.234Z",
|
|
1168
|
+
source: "binance_spot_usdt_turnover24h",
|
|
1169
|
+
fingerprint: "43df006e5510bb99",
|
|
1170
|
+
universes: {
|
|
1171
|
+
top5: {
|
|
1172
|
+
size: 5,
|
|
1173
|
+
fingerprint: "ba5b73456b18",
|
|
1174
|
+
symbols: ["ETHUSDT", "USD1USDT", "SOLUSDT", "OPNUSDT", "VANAUSDT"]
|
|
1175
|
+
},
|
|
1176
|
+
top10: {
|
|
1177
|
+
size: 10,
|
|
1178
|
+
fingerprint: "370eb5af9778",
|
|
1179
|
+
symbols: [
|
|
1180
|
+
"ETHUSDT",
|
|
1181
|
+
"USD1USDT",
|
|
1182
|
+
"SOLUSDT",
|
|
1183
|
+
"OPNUSDT",
|
|
1184
|
+
"VANAUSDT",
|
|
1185
|
+
"DEXEUSDT",
|
|
1186
|
+
"BANKUSDT",
|
|
1187
|
+
"ZECUSDT",
|
|
1188
|
+
"XRPUSDT",
|
|
1189
|
+
"RLUSDUSDT"
|
|
1190
|
+
]
|
|
1191
|
+
},
|
|
1192
|
+
top30: {
|
|
1193
|
+
size: 30,
|
|
1194
|
+
fingerprint: "685a7e7f5fce",
|
|
1195
|
+
symbols: [
|
|
1196
|
+
"ETHUSDT",
|
|
1197
|
+
"USD1USDT",
|
|
1198
|
+
"SOLUSDT",
|
|
1199
|
+
"OPNUSDT",
|
|
1200
|
+
"VANAUSDT",
|
|
1201
|
+
"DEXEUSDT",
|
|
1202
|
+
"BANKUSDT",
|
|
1203
|
+
"ZECUSDT",
|
|
1204
|
+
"XRPUSDT",
|
|
1205
|
+
"RLUSDUSDT",
|
|
1206
|
+
"BNBUSDT",
|
|
1207
|
+
"DOGEUSDT",
|
|
1208
|
+
"REUSDT",
|
|
1209
|
+
"RIFUSDT",
|
|
1210
|
+
"ZAMAUSDT",
|
|
1211
|
+
"TRXUSDT",
|
|
1212
|
+
"EURUSDT",
|
|
1213
|
+
"AEROUSDT",
|
|
1214
|
+
"UUSDT",
|
|
1215
|
+
"SUIUSDT",
|
|
1216
|
+
"XAUTUSDT",
|
|
1217
|
+
"NEARUSDT",
|
|
1218
|
+
"LTCUSDT",
|
|
1219
|
+
"SPCXBUSDT",
|
|
1220
|
+
"LAUSDT",
|
|
1221
|
+
"AVAXUSDT",
|
|
1222
|
+
"WLDUSDT",
|
|
1223
|
+
"ONDOUSDT",
|
|
1224
|
+
"XLMUSDT",
|
|
1225
|
+
"UNIUSDT"
|
|
1226
|
+
]
|
|
1227
|
+
},
|
|
1228
|
+
top50: {
|
|
1229
|
+
size: 50,
|
|
1230
|
+
fingerprint: "cb9725e24141",
|
|
1231
|
+
symbols: [
|
|
1232
|
+
"ETHUSDT",
|
|
1233
|
+
"USD1USDT",
|
|
1234
|
+
"SOLUSDT",
|
|
1235
|
+
"OPNUSDT",
|
|
1236
|
+
"VANAUSDT",
|
|
1237
|
+
"DEXEUSDT",
|
|
1238
|
+
"BANKUSDT",
|
|
1239
|
+
"ZECUSDT",
|
|
1240
|
+
"XRPUSDT",
|
|
1241
|
+
"RLUSDUSDT",
|
|
1242
|
+
"BNBUSDT",
|
|
1243
|
+
"DOGEUSDT",
|
|
1244
|
+
"REUSDT",
|
|
1245
|
+
"RIFUSDT",
|
|
1246
|
+
"ZAMAUSDT",
|
|
1247
|
+
"TRXUSDT",
|
|
1248
|
+
"EURUSDT",
|
|
1249
|
+
"AEROUSDT",
|
|
1250
|
+
"UUSDT",
|
|
1251
|
+
"SUIUSDT",
|
|
1252
|
+
"XAUTUSDT",
|
|
1253
|
+
"NEARUSDT",
|
|
1254
|
+
"LTCUSDT",
|
|
1255
|
+
"SPCXBUSDT",
|
|
1256
|
+
"LAUSDT",
|
|
1257
|
+
"AVAXUSDT",
|
|
1258
|
+
"WLDUSDT",
|
|
1259
|
+
"ONDOUSDT",
|
|
1260
|
+
"XLMUSDT",
|
|
1261
|
+
"UNIUSDT",
|
|
1262
|
+
"ADAUSDT",
|
|
1263
|
+
"SNDKBUSDT",
|
|
1264
|
+
"KAITOUSDT",
|
|
1265
|
+
"UTKUSDT",
|
|
1266
|
+
"PEPEUSDT",
|
|
1267
|
+
"WLFIUSDT",
|
|
1268
|
+
"PAXGUSDT",
|
|
1269
|
+
"ENAUSDT",
|
|
1270
|
+
"TAOUSDT",
|
|
1271
|
+
"AAVEUSDT",
|
|
1272
|
+
"CRCLBUSDT",
|
|
1273
|
+
"LINKUSDT",
|
|
1274
|
+
"TONUSDT",
|
|
1275
|
+
"ALLOUSDT",
|
|
1276
|
+
"PUMPUSDT",
|
|
1277
|
+
"INJUSDT",
|
|
1278
|
+
"HOMEUSDT",
|
|
1279
|
+
"MUBUSDT",
|
|
1280
|
+
"TSLABUSDT",
|
|
1281
|
+
"GRAMUSDT"
|
|
1282
|
+
]
|
|
1283
|
+
},
|
|
1284
|
+
top100: {
|
|
1285
|
+
size: 100,
|
|
1286
|
+
fingerprint: "f397883a81cf",
|
|
1287
|
+
symbols: [
|
|
1288
|
+
"ETHUSDT",
|
|
1289
|
+
"USD1USDT",
|
|
1290
|
+
"SOLUSDT",
|
|
1291
|
+
"OPNUSDT",
|
|
1292
|
+
"VANAUSDT",
|
|
1293
|
+
"DEXEUSDT",
|
|
1294
|
+
"BANKUSDT",
|
|
1295
|
+
"ZECUSDT",
|
|
1296
|
+
"XRPUSDT",
|
|
1297
|
+
"RLUSDUSDT",
|
|
1298
|
+
"BNBUSDT",
|
|
1299
|
+
"DOGEUSDT",
|
|
1300
|
+
"REUSDT",
|
|
1301
|
+
"RIFUSDT",
|
|
1302
|
+
"ZAMAUSDT",
|
|
1303
|
+
"TRXUSDT",
|
|
1304
|
+
"EURUSDT",
|
|
1305
|
+
"AEROUSDT",
|
|
1306
|
+
"UUSDT",
|
|
1307
|
+
"SUIUSDT",
|
|
1308
|
+
"XAUTUSDT",
|
|
1309
|
+
"NEARUSDT",
|
|
1310
|
+
"LTCUSDT",
|
|
1311
|
+
"SPCXBUSDT",
|
|
1312
|
+
"LAUSDT",
|
|
1313
|
+
"AVAXUSDT",
|
|
1314
|
+
"WLDUSDT",
|
|
1315
|
+
"ONDOUSDT",
|
|
1316
|
+
"XLMUSDT",
|
|
1317
|
+
"UNIUSDT",
|
|
1318
|
+
"ADAUSDT",
|
|
1319
|
+
"SNDKBUSDT",
|
|
1320
|
+
"KAITOUSDT",
|
|
1321
|
+
"UTKUSDT",
|
|
1322
|
+
"PEPEUSDT",
|
|
1323
|
+
"WLFIUSDT",
|
|
1324
|
+
"PAXGUSDT",
|
|
1325
|
+
"ENAUSDT",
|
|
1326
|
+
"TAOUSDT",
|
|
1327
|
+
"AAVEUSDT",
|
|
1328
|
+
"CRCLBUSDT",
|
|
1329
|
+
"LINKUSDT",
|
|
1330
|
+
"TONUSDT",
|
|
1331
|
+
"ALLOUSDT",
|
|
1332
|
+
"PUMPUSDT",
|
|
1333
|
+
"INJUSDT",
|
|
1334
|
+
"HOMEUSDT",
|
|
1335
|
+
"MUBUSDT",
|
|
1336
|
+
"TSLABUSDT",
|
|
1337
|
+
"GRAMUSDT",
|
|
1338
|
+
"\u5E01\u5B89\u4EBA\u751FUSDT",
|
|
1339
|
+
"SYNUSDT",
|
|
1340
|
+
"TRUMPUSDT",
|
|
1341
|
+
"PROMUSDT",
|
|
1342
|
+
"XPLUSDT",
|
|
1343
|
+
"HBARUSDT",
|
|
1344
|
+
"ERAUSDT",
|
|
1345
|
+
"BCHUSDT",
|
|
1346
|
+
"SOXLBUSDT",
|
|
1347
|
+
"DOTUSDT",
|
|
1348
|
+
"EPICUSDT",
|
|
1349
|
+
"KITEUSDT",
|
|
1350
|
+
"NFPUSDT",
|
|
1351
|
+
"KGSTUSDT",
|
|
1352
|
+
"FILUSDT",
|
|
1353
|
+
"FETUSDT",
|
|
1354
|
+
"KORUBUSDT",
|
|
1355
|
+
"ARBUSDT",
|
|
1356
|
+
"STXUSDT",
|
|
1357
|
+
"HEIUSDT",
|
|
1358
|
+
"BARDUSDT",
|
|
1359
|
+
"DASHUSDT",
|
|
1360
|
+
"ASTERUSDT",
|
|
1361
|
+
"VANRYUSDT",
|
|
1362
|
+
"INTCBUSDT",
|
|
1363
|
+
"ETCUSDT",
|
|
1364
|
+
"EIGENUSDT",
|
|
1365
|
+
"PENGUUSDT",
|
|
1366
|
+
"LRCUSDT",
|
|
1367
|
+
"VIRTUALUSDT",
|
|
1368
|
+
"POLUSDT",
|
|
1369
|
+
"ICPUSDT",
|
|
1370
|
+
"ZBTUSDT",
|
|
1371
|
+
"SKHYBUSDT",
|
|
1372
|
+
"SHIBUSDT",
|
|
1373
|
+
"LDOUSDT",
|
|
1374
|
+
"APTUSDT",
|
|
1375
|
+
"JTOUSDT",
|
|
1376
|
+
"NOMUSDT",
|
|
1377
|
+
"ZROUSDT",
|
|
1378
|
+
"OPUSDT",
|
|
1379
|
+
"BONKUSDT",
|
|
1380
|
+
"SKLUSDT",
|
|
1381
|
+
"HEMIUSDT",
|
|
1382
|
+
"TNSRUSDT",
|
|
1383
|
+
"INTWBUSDT",
|
|
1384
|
+
"BIOUSDT",
|
|
1385
|
+
"TIAUSDT",
|
|
1386
|
+
"ORDIUSDT",
|
|
1387
|
+
"XUSDUSDT"
|
|
1388
|
+
]
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
};
|
|
1392
|
+
|
|
1393
|
+
// src/binanceBreadthUniverses.ts
|
|
1394
|
+
var BINANCE_BREADTH_UNIVERSE_KEYS = [
|
|
1395
|
+
"top5",
|
|
1396
|
+
"top10",
|
|
1397
|
+
"top30",
|
|
1398
|
+
"top50",
|
|
1399
|
+
"top100"
|
|
1400
|
+
];
|
|
1401
|
+
var fingerprint = (value, length) => (0, import_node_crypto.createHash)("sha256").update(JSON.stringify(value)).digest("hex").slice(0, length);
|
|
1402
|
+
var normalizeSymbols = (symbols) => symbols.map((symbol) => symbol.trim().toUpperCase()).filter(Boolean);
|
|
1403
|
+
var buildBinanceBreadthUniverseSnapshot = ({
|
|
1404
|
+
rankedSymbols,
|
|
1405
|
+
updatedAt = (/* @__PURE__ */ new Date()).toISOString()
|
|
1406
|
+
}) => {
|
|
1407
|
+
const uniqueSymbols = [...new Set(normalizeSymbols(rankedSymbols))];
|
|
1408
|
+
if (uniqueSymbols.length < 100) {
|
|
1409
|
+
throw new Error(
|
|
1410
|
+
`Binance breadth snapshot requires at least 100 symbols, got ${uniqueSymbols.length}`
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
const universes = Object.fromEntries(
|
|
1414
|
+
BINANCE_BREADTH_UNIVERSE_KEYS.map((key) => {
|
|
1415
|
+
const size = Number(key.slice(3));
|
|
1416
|
+
const symbols = uniqueSymbols.slice(0, size);
|
|
1417
|
+
return [
|
|
1418
|
+
key,
|
|
1419
|
+
{
|
|
1420
|
+
size,
|
|
1421
|
+
fingerprint: fingerprint(symbols, 12),
|
|
1422
|
+
symbols
|
|
1423
|
+
}
|
|
1424
|
+
];
|
|
1425
|
+
})
|
|
1426
|
+
);
|
|
1427
|
+
return {
|
|
1428
|
+
schemaVersion: 1,
|
|
1429
|
+
updatedAt,
|
|
1430
|
+
source: "binance_spot_usdt_turnover24h",
|
|
1431
|
+
fingerprint: fingerprint(universes, 16),
|
|
1432
|
+
universes
|
|
1433
|
+
};
|
|
1434
|
+
};
|
|
1435
|
+
var validateSnapshot = (value) => {
|
|
1436
|
+
const rebuilt = buildBinanceBreadthUniverseSnapshot({
|
|
1437
|
+
rankedSymbols: value.universes.top100.symbols,
|
|
1438
|
+
updatedAt: value.updatedAt
|
|
1439
|
+
});
|
|
1440
|
+
if (value.schemaVersion !== 1 || value.source !== rebuilt.source || value.fingerprint !== rebuilt.fingerprint) {
|
|
1441
|
+
throw new Error("Invalid Binance breadth universe snapshot fingerprint");
|
|
1442
|
+
}
|
|
1443
|
+
for (const key of BINANCE_BREADTH_UNIVERSE_KEYS) {
|
|
1444
|
+
const actual = value.universes[key];
|
|
1445
|
+
const expected = rebuilt.universes[key];
|
|
1446
|
+
if (actual.size !== expected.size || actual.fingerprint !== expected.fingerprint || JSON.stringify(actual.symbols) !== JSON.stringify(expected.symbols)) {
|
|
1447
|
+
throw new Error(`Invalid Binance breadth universe snapshot: ${key}`);
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
return value;
|
|
1451
|
+
};
|
|
1452
|
+
var snapshot = validateSnapshot(binanceBreadthUniverses_default);
|
|
1453
|
+
var getBinanceBreadthUniverses = () => BINANCE_BREADTH_UNIVERSE_KEYS.map((key) => {
|
|
1454
|
+
const definition = snapshot.universes[key];
|
|
1455
|
+
return {
|
|
1456
|
+
key,
|
|
1457
|
+
size: definition.size,
|
|
1458
|
+
fingerprint: definition.fingerprint,
|
|
1459
|
+
universe: `binance_${key}_usdt_${definition.fingerprint}`,
|
|
1460
|
+
symbols: [...definition.symbols]
|
|
1461
|
+
};
|
|
1462
|
+
});
|
|
1463
|
+
|
|
1464
|
+
// src/strategyHelpers/binanceMarketContext.ts
|
|
1113
1465
|
var DEFAULT_MAX_AGE_BY_INTERVAL = {
|
|
1114
1466
|
"1m": 3 * 6e4,
|
|
1115
1467
|
"5m": 10 * 6e4,
|
|
@@ -1141,7 +1493,6 @@ var signalIntervalToMarketInterval = (value) => {
|
|
|
1141
1493
|
return "15m";
|
|
1142
1494
|
};
|
|
1143
1495
|
var resolveMarketInterval = (signal, override) => override ?? signalIntervalToMarketInterval(signal.interval);
|
|
1144
|
-
var resolveBreadthUniverse = () => (process.env.BINANCE_MARKET_CONTEXT_BREADTH_UNIVERSE || "binance_top30_usdt").trim().toLowerCase();
|
|
1145
1496
|
var getReferenceSymbols = () => {
|
|
1146
1497
|
const symbols = (process.env.BINANCE_MARKET_CONTEXT_REFERENCE_SYMBOLS || "BTCUSDT,ETHUSDT").split(",").map((item) => item.trim().toUpperCase()).filter(Boolean);
|
|
1147
1498
|
return symbols.length ? [...new Set(symbols)] : ["BTCUSDT", "ETHUSDT"];
|
|
@@ -1214,13 +1565,30 @@ var getCachedBreadth = ({
|
|
|
1214
1565
|
breadthCache.set(key, promise);
|
|
1215
1566
|
return promise;
|
|
1216
1567
|
};
|
|
1568
|
+
var toMarketBreadthContext = (breadth, interval) => ({
|
|
1569
|
+
source: "binance_klines",
|
|
1570
|
+
universe: breadth.universe,
|
|
1571
|
+
interval,
|
|
1572
|
+
asOfTs: breadth.ts.getTime(),
|
|
1573
|
+
ageMs: breadth.ageMs,
|
|
1574
|
+
stale: breadth.stale,
|
|
1575
|
+
symbolsCount: toFiniteNumberOrNull(breadth.symbolsCount),
|
|
1576
|
+
advancers: toFiniteNumberOrNull(breadth.advancers),
|
|
1577
|
+
decliners: toFiniteNumberOrNull(breadth.decliners),
|
|
1578
|
+
unchanged: toFiniteNumberOrNull(breadth.unchanged),
|
|
1579
|
+
advanceDeclineRatio: toFiniteNumberOrNull(breadth.advanceDeclineRatio),
|
|
1580
|
+
pctAboveMa20: toFiniteNumberOrNull(breadth.pctAboveMa20),
|
|
1581
|
+
pctAboveMa50: toFiniteNumberOrNull(breadth.pctAboveMa50),
|
|
1582
|
+
equalWeightedReturn: toFiniteNumberOrNull(breadth.equalWeightedReturn),
|
|
1583
|
+
volumeWeightedReturn: toFiniteNumberOrNull(breadth.volumeWeightedReturn),
|
|
1584
|
+
dispersion: toFiniteNumberOrNull(breadth.dispersion)
|
|
1585
|
+
});
|
|
1217
1586
|
var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
1218
1587
|
const {
|
|
1219
1588
|
signal,
|
|
1220
1589
|
env,
|
|
1221
1590
|
enabled = isBinanceMarketContextEnabled(env),
|
|
1222
1591
|
interval = resolveMarketInterval(signal, params.interval),
|
|
1223
|
-
breadthUniverse = resolveBreadthUniverse(),
|
|
1224
1592
|
maxAgeMs = DEFAULT_MAX_AGE_BY_INTERVAL[interval]
|
|
1225
1593
|
} = params;
|
|
1226
1594
|
if (signal.universe === "tradfi" || !enabled || binanceMarketContextUnavailable || !hasBaseContext(signal)) {
|
|
@@ -1229,26 +1597,49 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
1229
1597
|
try {
|
|
1230
1598
|
const referenceSymbols = getReferenceSymbols();
|
|
1231
1599
|
const primaryReferenceSymbol = resolvePrimaryReferenceSymbol(signal.symbol);
|
|
1232
|
-
const
|
|
1600
|
+
const breadthUniverses = params.breadthUniverse ? [
|
|
1601
|
+
{
|
|
1602
|
+
key: "top30",
|
|
1603
|
+
universe: params.breadthUniverse
|
|
1604
|
+
}
|
|
1605
|
+
] : getBinanceBreadthUniverses();
|
|
1606
|
+
const [referenceRows, breadthRows] = await Promise.all([
|
|
1233
1607
|
getCachedReferenceRows({
|
|
1234
1608
|
referenceSymbols,
|
|
1235
1609
|
interval,
|
|
1236
1610
|
timestamp: signal.timestamp,
|
|
1237
1611
|
maxAgeMs
|
|
1238
1612
|
}),
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1613
|
+
Promise.all(
|
|
1614
|
+
breadthUniverses.map(async ({ key, universe }) => ({
|
|
1615
|
+
key,
|
|
1616
|
+
breadth: await getCachedBreadth({
|
|
1617
|
+
breadthUniverse: universe,
|
|
1618
|
+
interval,
|
|
1619
|
+
timestamp: signal.timestamp,
|
|
1620
|
+
maxAgeMs
|
|
1621
|
+
})
|
|
1622
|
+
}))
|
|
1623
|
+
)
|
|
1245
1624
|
]);
|
|
1625
|
+
const availableBreadths = breadthRows.filter(
|
|
1626
|
+
(row) => row.breadth != null
|
|
1627
|
+
);
|
|
1628
|
+
const marketBreadths = Object.fromEntries(
|
|
1629
|
+
availableBreadths.map(({ key, breadth }) => [
|
|
1630
|
+
key,
|
|
1631
|
+
toMarketBreadthContext(breadth, interval)
|
|
1632
|
+
])
|
|
1633
|
+
);
|
|
1634
|
+
const primaryBreadth = availableBreadths.find(
|
|
1635
|
+
({ key }) => key === "top30"
|
|
1636
|
+
)?.breadth;
|
|
1246
1637
|
const tradeFlowBySymbol = Object.fromEntries(
|
|
1247
1638
|
referenceRows.filter((row) => row.tradeFlow).map((row) => [row.symbol, row.tradeFlow])
|
|
1248
1639
|
);
|
|
1249
1640
|
const targetReferenceSymbol = signal.symbol.trim().toUpperCase();
|
|
1250
1641
|
const targetTradeFlow = tradeFlowBySymbol[targetReferenceSymbol];
|
|
1251
|
-
if (!Object.keys(tradeFlowBySymbol).length && !
|
|
1642
|
+
if (!Object.keys(tradeFlowBySymbol).length && !availableBreadths.length) {
|
|
1252
1643
|
return false;
|
|
1253
1644
|
}
|
|
1254
1645
|
const baseContext = signal.additionalIndicators.baseContext;
|
|
@@ -1275,76 +1666,65 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
1275
1666
|
tradeFlowBySymbol
|
|
1276
1667
|
}
|
|
1277
1668
|
} : {},
|
|
1278
|
-
...
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
ageMs: breadth.ageMs,
|
|
1285
|
-
stale: breadth.stale,
|
|
1286
|
-
symbolsCount: toFiniteNumberOrNull(breadth.symbolsCount),
|
|
1287
|
-
advancers: toFiniteNumberOrNull(breadth.advancers),
|
|
1288
|
-
decliners: toFiniteNumberOrNull(breadth.decliners),
|
|
1289
|
-
unchanged: toFiniteNumberOrNull(breadth.unchanged),
|
|
1290
|
-
advanceDeclineRatio: toFiniteNumberOrNull(
|
|
1291
|
-
breadth.advanceDeclineRatio
|
|
1292
|
-
),
|
|
1293
|
-
pctAboveMa20: toFiniteNumberOrNull(breadth.pctAboveMa20),
|
|
1294
|
-
pctAboveMa50: toFiniteNumberOrNull(breadth.pctAboveMa50),
|
|
1295
|
-
equalWeightedReturn: toFiniteNumberOrNull(
|
|
1296
|
-
breadth.equalWeightedReturn
|
|
1297
|
-
),
|
|
1298
|
-
volumeWeightedReturn: toFiniteNumberOrNull(
|
|
1299
|
-
breadth.volumeWeightedReturn
|
|
1300
|
-
),
|
|
1301
|
-
dispersion: toFiniteNumberOrNull(breadth.dispersion)
|
|
1302
|
-
},
|
|
1303
|
-
btcAltRegime: {
|
|
1304
|
-
source: "binance_klines",
|
|
1305
|
-
universe: breadth.universe,
|
|
1306
|
-
interval,
|
|
1307
|
-
asOfTs: breadth.ts.getTime(),
|
|
1308
|
-
ageMs: breadth.ageMs,
|
|
1309
|
-
stale: breadth.stale,
|
|
1310
|
-
btcReturn1h: toFiniteNumberOrNull(breadth.btcReturn1h),
|
|
1311
|
-
btcReturn4h: toFiniteNumberOrNull(breadth.btcReturn4h),
|
|
1312
|
-
btcReturn24h: toFiniteNumberOrNull(breadth.btcReturn24h),
|
|
1313
|
-
altBasketReturn1h: toFiniteNumberOrNull(
|
|
1314
|
-
breadth.altBasketReturn1h
|
|
1315
|
-
),
|
|
1316
|
-
altBasketReturn4h: toFiniteNumberOrNull(
|
|
1317
|
-
breadth.altBasketReturn4h
|
|
1318
|
-
),
|
|
1319
|
-
altBasketReturn24h: toFiniteNumberOrNull(
|
|
1320
|
-
breadth.altBasketReturn24h
|
|
1669
|
+
...availableBreadths.length ? {
|
|
1670
|
+
marketBreadths,
|
|
1671
|
+
...primaryBreadth ? {
|
|
1672
|
+
marketBreadth: toMarketBreadthContext(
|
|
1673
|
+
primaryBreadth,
|
|
1674
|
+
interval
|
|
1321
1675
|
),
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1676
|
+
btcAltRegime: {
|
|
1677
|
+
source: "binance_klines",
|
|
1678
|
+
universe: primaryBreadth.universe,
|
|
1679
|
+
interval,
|
|
1680
|
+
asOfTs: primaryBreadth.ts.getTime(),
|
|
1681
|
+
ageMs: primaryBreadth.ageMs,
|
|
1682
|
+
stale: primaryBreadth.stale,
|
|
1683
|
+
btcReturn1h: toFiniteNumberOrNull(
|
|
1684
|
+
primaryBreadth.btcReturn1h
|
|
1685
|
+
),
|
|
1686
|
+
btcReturn4h: toFiniteNumberOrNull(
|
|
1687
|
+
primaryBreadth.btcReturn4h
|
|
1688
|
+
),
|
|
1689
|
+
btcReturn24h: toFiniteNumberOrNull(
|
|
1690
|
+
primaryBreadth.btcReturn24h
|
|
1691
|
+
),
|
|
1692
|
+
altBasketReturn1h: toFiniteNumberOrNull(
|
|
1693
|
+
primaryBreadth.altBasketReturn1h
|
|
1694
|
+
),
|
|
1695
|
+
altBasketReturn4h: toFiniteNumberOrNull(
|
|
1696
|
+
primaryBreadth.altBasketReturn4h
|
|
1697
|
+
),
|
|
1698
|
+
altBasketReturn24h: toFiniteNumberOrNull(
|
|
1699
|
+
primaryBreadth.altBasketReturn24h
|
|
1700
|
+
),
|
|
1701
|
+
btcVsAltReturn1h: toFiniteNumberOrNull(
|
|
1702
|
+
primaryBreadth.btcVsAltReturn1h
|
|
1703
|
+
),
|
|
1704
|
+
btcVsAltReturn4h: toFiniteNumberOrNull(
|
|
1705
|
+
primaryBreadth.btcVsAltReturn4h
|
|
1706
|
+
),
|
|
1707
|
+
btcVsAltReturn24h: toFiniteNumberOrNull(
|
|
1708
|
+
primaryBreadth.btcVsAltReturn24h
|
|
1709
|
+
),
|
|
1710
|
+
btcTurnoverShare1h: toFiniteNumberOrNull(
|
|
1711
|
+
primaryBreadth.btcTurnoverShare1h
|
|
1712
|
+
),
|
|
1713
|
+
btcTurnoverShare24h: toFiniteNumberOrNull(
|
|
1714
|
+
primaryBreadth.btcTurnoverShare24h
|
|
1715
|
+
),
|
|
1716
|
+
btcTurnoverShareChange24h: toFiniteNumberOrNull(
|
|
1717
|
+
primaryBreadth.btcTurnoverShareChange24h
|
|
1718
|
+
),
|
|
1719
|
+
altVolToBtcVol24h: toFiniteNumberOrNull(
|
|
1720
|
+
primaryBreadth.altVolToBtcVol24h
|
|
1721
|
+
),
|
|
1722
|
+
altDispersion24h: toFiniteNumberOrNull(
|
|
1723
|
+
primaryBreadth.altDispersion24h
|
|
1724
|
+
),
|
|
1725
|
+
regime: primaryBreadth.btcAltRegime ?? "unknown"
|
|
1726
|
+
}
|
|
1727
|
+
} : {}
|
|
1348
1728
|
} : {}
|
|
1349
1729
|
}
|
|
1350
1730
|
}
|
|
@@ -2318,7 +2698,7 @@ var getConnectorCreatorByName = async (connectorName, cwd = getTradejsProjectCwd
|
|
|
2318
2698
|
var DEFAULT_CONNECTOR_NAME = BUILTIN_CONNECTOR_NAMES.ByBit;
|
|
2319
2699
|
|
|
2320
2700
|
// src/testConnector.ts
|
|
2321
|
-
var
|
|
2701
|
+
var import_node_crypto2 = require("crypto");
|
|
2322
2702
|
var import_constants2 = require("@tradejs/core/constants");
|
|
2323
2703
|
var import_backtest = require("@tradejs/core/backtest");
|
|
2324
2704
|
var import_trade = require("@tradejs/core/trade");
|
|
@@ -2629,7 +3009,7 @@ var createTestConnector = (connector, context) => {
|
|
|
2629
3009
|
},
|
|
2630
3010
|
kline: async (options) => connector.kline(options),
|
|
2631
3011
|
getResult: async () => {
|
|
2632
|
-
const orderLogId = (0,
|
|
3012
|
+
const orderLogId = (0, import_node_crypto2.randomUUID)().slice(-12);
|
|
2633
3013
|
const fullStat = fastMode ? (0, import_backtest.calculateStatsFull)(positionLog) : null;
|
|
2634
3014
|
return {
|
|
2635
3015
|
stat: fullStat ? {
|
|
@@ -3249,6 +3629,16 @@ var resolveExecutionCosts = async (params) => {
|
|
|
3249
3629
|
var isBacktestEntryDelayControlCode = (value) => typeof value === "string" && value.startsWith("BACKTEST_ENTRY_DELAY_");
|
|
3250
3630
|
var CLOSED_RESULT_FLUSH_INTERVAL = 500;
|
|
3251
3631
|
var DEFAULT_STRATEGY_CANDLE_TIMEOUT_MS = 6e4;
|
|
3632
|
+
var createBacktestWarningCounts = () => ({
|
|
3633
|
+
[import_types.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY]: 0
|
|
3634
|
+
});
|
|
3635
|
+
var recordBacktestSignalWarning = (warningCounts, signal) => {
|
|
3636
|
+
if (!signal || typeof signal === "string" || signal.orderStatus !== "failed" || signal.orderFailureReason !== import_types.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY) {
|
|
3637
|
+
return;
|
|
3638
|
+
}
|
|
3639
|
+
const code = import_types.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY;
|
|
3640
|
+
warningCounts[code] = (warningCounts[code] ?? 0) + 1;
|
|
3641
|
+
};
|
|
3252
3642
|
var resolvePositiveInt = (value, fallback) => {
|
|
3253
3643
|
const parsed = parseInt(String(value ?? ""), 10);
|
|
3254
3644
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
@@ -4082,6 +4472,7 @@ var testing = async ({
|
|
|
4082
4472
|
);
|
|
4083
4473
|
const pendingMlPayloadBySignalId = /* @__PURE__ */ new Map();
|
|
4084
4474
|
const pendingAiRowBySignalId = /* @__PURE__ */ new Map();
|
|
4475
|
+
const warningCounts = createBacktestWarningCounts();
|
|
4085
4476
|
const replaySignalEvaluations = collectReplaySignalEvaluations ? [] : null;
|
|
4086
4477
|
const flushClosedResultsBatch = async () => {
|
|
4087
4478
|
if (!ml && !ai) return;
|
|
@@ -4126,6 +4517,7 @@ var testing = async ({
|
|
|
4126
4517
|
}
|
|
4127
4518
|
};
|
|
4128
4519
|
const processSignal = async (signal, candle) => {
|
|
4520
|
+
recordBacktestSignalWarning(warningCounts, signal);
|
|
4129
4521
|
if (isBacktestEntryDelayControlCode(signal)) {
|
|
4130
4522
|
return;
|
|
4131
4523
|
}
|
|
@@ -4234,10 +4626,14 @@ var testing = async ({
|
|
|
4234
4626
|
}
|
|
4235
4627
|
await withTimeout("flush closed results", flushClosedResultsBatch());
|
|
4236
4628
|
const result = await withTimeout("collect result", testConnector.getResult());
|
|
4237
|
-
|
|
4629
|
+
const resultWithWarnings = {
|
|
4238
4630
|
...result,
|
|
4631
|
+
warningCounts
|
|
4632
|
+
};
|
|
4633
|
+
return replaySignalEvaluations ? {
|
|
4634
|
+
...resultWithWarnings,
|
|
4239
4635
|
inlineReplaySignalEvaluations: replaySignalEvaluations
|
|
4240
|
-
} :
|
|
4636
|
+
} : resultWithWarnings;
|
|
4241
4637
|
};
|
|
4242
4638
|
var testingGroupInSharedCandleLoop = async (tests) => {
|
|
4243
4639
|
if (!canRunTestsInSharedCandleLoop(tests)) {
|
|
@@ -4491,6 +4887,7 @@ var testingGroupInSharedCandleLoop = async (tests) => {
|
|
|
4491
4887
|
testConnector,
|
|
4492
4888
|
pendingMlPayloadBySignalId: /* @__PURE__ */ new Map(),
|
|
4493
4889
|
pendingAiRowBySignalId: /* @__PURE__ */ new Map(),
|
|
4890
|
+
warningCounts: createBacktestWarningCounts(),
|
|
4494
4891
|
replaySignalEvaluations: collectReplaySignalEvaluations ? [] : null
|
|
4495
4892
|
});
|
|
4496
4893
|
}
|
|
@@ -4543,6 +4940,7 @@ var testingGroupInSharedCandleLoop = async (tests) => {
|
|
|
4543
4940
|
}
|
|
4544
4941
|
};
|
|
4545
4942
|
const processRunnerSignal = async (runner, signal, candle) => {
|
|
4943
|
+
recordBacktestSignalWarning(runner.warningCounts, signal);
|
|
4546
4944
|
if (isBacktestEntryDelayControlCode(signal)) {
|
|
4547
4945
|
return;
|
|
4548
4946
|
}
|
|
@@ -4684,8 +5082,12 @@ var testingGroupInSharedCandleLoop = async (tests) => {
|
|
|
4684
5082
|
test: runner.test,
|
|
4685
5083
|
result: runner.replaySignalEvaluations ? {
|
|
4686
5084
|
...result,
|
|
5085
|
+
warningCounts: runner.warningCounts,
|
|
4687
5086
|
inlineReplaySignalEvaluations: runner.replaySignalEvaluations
|
|
4688
|
-
} :
|
|
5087
|
+
} : {
|
|
5088
|
+
...result,
|
|
5089
|
+
warningCounts: runner.warningCounts
|
|
5090
|
+
}
|
|
4689
5091
|
});
|
|
4690
5092
|
}
|
|
4691
5093
|
return results;
|