@tradejs/cli 2.0.5 → 2.0.7
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 +3145 -1827
- package/dist/lib/aiExport.js +1822 -0
- package/dist/lib/binanceMarketContextBackfill.js +97 -88
- package/dist/lib/binanceMarketData.js +6 -3
- package/dist/lib/marketContextPrepare.js +149 -125
- package/dist/lib/runEnvironment.js +100 -55
- package/dist/lib/runFormatting.js +52 -37
- package/dist/lib/runtimeLineage.js +255 -0
- package/dist/lib/runtimeParityDetails.js +24 -1
- package/dist/lib/runtimeSignalsLoader.js +19 -0
- package/dist/lib/runtimeSignalsStorage.js +3 -0
- package/dist/lib/runtimeTradeSync.js +40 -11
- package/dist/lib/timeWindow.js +44 -16
- package/dist/scripts/aiExport.js +252 -125
- package/dist/scripts/aiPocketSearch.js +52 -37
- package/dist/scripts/aiTrain.js +52 -37
- package/dist/scripts/backtest.js +222 -144
- package/dist/scripts/binanceBreadthUniversesUpdate.js +120 -0
- package/dist/scripts/binanceMarketIngest.js +32 -33
- package/dist/scripts/candlesMigrateProvider.js +648 -419
- package/dist/scripts/doctor.js +648 -419
- package/dist/scripts/mlExport.js +57 -39
- package/dist/scripts/replay.js +1040 -255
- package/dist/scripts/replayRunner.js +1040 -255
- package/dist/scripts/replayRuntimeEvidence.js +3 -1
- package/dist/scripts/resolveExportStrategy.js +7 -2
- package/dist/scripts/results.js +52 -37
- package/dist/scripts/runtimeParity.js +193 -141
- package/dist/scripts/signals.js +433 -144
- package/dist/scripts/signalsDaemon.js +433 -144
- package/dist/scripts/signalsSummary.js +40 -11
- package/package.json +15 -14
package/dist/scripts/backtest.js
CHANGED
|
@@ -34,9 +34,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
34
34
|
));
|
|
35
35
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
36
36
|
|
|
37
|
-
// ../../node_modules/
|
|
37
|
+
// ../../node_modules/ms/index.js
|
|
38
38
|
var require_ms = __commonJS({
|
|
39
|
-
"../../node_modules/
|
|
39
|
+
"../../node_modules/ms/index.js"(exports2, module2) {
|
|
40
40
|
"use strict";
|
|
41
41
|
var s = 1e3;
|
|
42
42
|
var m = s * 60;
|
|
@@ -151,9 +151,9 @@ var require_ms = __commonJS({
|
|
|
151
151
|
}
|
|
152
152
|
});
|
|
153
153
|
|
|
154
|
-
// ../../node_modules/debug/src/common.js
|
|
154
|
+
// ../../node_modules/list-it/node_modules/debug/src/common.js
|
|
155
155
|
var require_common = __commonJS({
|
|
156
|
-
"../../node_modules/debug/src/common.js"(exports2, module2) {
|
|
156
|
+
"../../node_modules/list-it/node_modules/debug/src/common.js"(exports2, module2) {
|
|
157
157
|
"use strict";
|
|
158
158
|
function setup(env) {
|
|
159
159
|
createDebug.debug = createDebug;
|
|
@@ -255,50 +255,64 @@ var require_common = __commonJS({
|
|
|
255
255
|
createDebug.namespaces = namespaces;
|
|
256
256
|
createDebug.names = [];
|
|
257
257
|
createDebug.skips = [];
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
258
|
+
const split = (typeof namespaces === "string" ? namespaces : "").trim().replace(/\s+/g, ",").split(",").filter(Boolean);
|
|
259
|
+
for (const ns of split) {
|
|
260
|
+
if (ns[0] === "-") {
|
|
261
|
+
createDebug.skips.push(ns.slice(1));
|
|
262
|
+
} else {
|
|
263
|
+
createDebug.names.push(ns);
|
|
264
264
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function matchesTemplate(search, template) {
|
|
268
|
+
let searchIndex = 0;
|
|
269
|
+
let templateIndex = 0;
|
|
270
|
+
let starIndex = -1;
|
|
271
|
+
let matchIndex = 0;
|
|
272
|
+
while (searchIndex < search.length) {
|
|
273
|
+
if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === "*")) {
|
|
274
|
+
if (template[templateIndex] === "*") {
|
|
275
|
+
starIndex = templateIndex;
|
|
276
|
+
matchIndex = searchIndex;
|
|
277
|
+
templateIndex++;
|
|
278
|
+
} else {
|
|
279
|
+
searchIndex++;
|
|
280
|
+
templateIndex++;
|
|
281
|
+
}
|
|
282
|
+
} else if (starIndex !== -1) {
|
|
283
|
+
templateIndex = starIndex + 1;
|
|
284
|
+
matchIndex++;
|
|
285
|
+
searchIndex = matchIndex;
|
|
268
286
|
} else {
|
|
269
|
-
|
|
287
|
+
return false;
|
|
270
288
|
}
|
|
271
289
|
}
|
|
290
|
+
while (templateIndex < template.length && template[templateIndex] === "*") {
|
|
291
|
+
templateIndex++;
|
|
292
|
+
}
|
|
293
|
+
return templateIndex === template.length;
|
|
272
294
|
}
|
|
273
295
|
function disable() {
|
|
274
296
|
const namespaces = [
|
|
275
|
-
...createDebug.names
|
|
276
|
-
...createDebug.skips.map(
|
|
297
|
+
...createDebug.names,
|
|
298
|
+
...createDebug.skips.map((namespace) => "-" + namespace)
|
|
277
299
|
].join(",");
|
|
278
300
|
createDebug.enable("");
|
|
279
301
|
return namespaces;
|
|
280
302
|
}
|
|
281
303
|
function enabled(name) {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
let i;
|
|
286
|
-
let len;
|
|
287
|
-
for (i = 0, len = createDebug.skips.length; i < len; i++) {
|
|
288
|
-
if (createDebug.skips[i].test(name)) {
|
|
304
|
+
for (const skip of createDebug.skips) {
|
|
305
|
+
if (matchesTemplate(name, skip)) {
|
|
289
306
|
return false;
|
|
290
307
|
}
|
|
291
308
|
}
|
|
292
|
-
for (
|
|
293
|
-
if (
|
|
309
|
+
for (const ns of createDebug.names) {
|
|
310
|
+
if (matchesTemplate(name, ns)) {
|
|
294
311
|
return true;
|
|
295
312
|
}
|
|
296
313
|
}
|
|
297
314
|
return false;
|
|
298
315
|
}
|
|
299
|
-
function toNamespace(regexp) {
|
|
300
|
-
return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, "*");
|
|
301
|
-
}
|
|
302
316
|
function coerce(val) {
|
|
303
317
|
if (val instanceof Error) {
|
|
304
318
|
return val.stack || val.message;
|
|
@@ -315,9 +329,9 @@ var require_common = __commonJS({
|
|
|
315
329
|
}
|
|
316
330
|
});
|
|
317
331
|
|
|
318
|
-
// ../../node_modules/debug/src/browser.js
|
|
332
|
+
// ../../node_modules/list-it/node_modules/debug/src/browser.js
|
|
319
333
|
var require_browser = __commonJS({
|
|
320
|
-
"../../node_modules/debug/src/browser.js"(exports2, module2) {
|
|
334
|
+
"../../node_modules/list-it/node_modules/debug/src/browser.js"(exports2, module2) {
|
|
321
335
|
"use strict";
|
|
322
336
|
exports2.formatArgs = formatArgs;
|
|
323
337
|
exports2.save = save;
|
|
@@ -418,10 +432,11 @@ var require_browser = __commonJS({
|
|
|
418
432
|
if (typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
|
|
419
433
|
return false;
|
|
420
434
|
}
|
|
435
|
+
let m;
|
|
421
436
|
return typeof document !== "undefined" && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773
|
|
422
437
|
typeof window !== "undefined" && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31?
|
|
423
438
|
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
|
|
424
|
-
typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(
|
|
439
|
+
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
|
|
425
440
|
typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
|
|
426
441
|
}
|
|
427
442
|
function formatArgs(args2) {
|
|
@@ -459,7 +474,7 @@ var require_browser = __commonJS({
|
|
|
459
474
|
function load() {
|
|
460
475
|
let r;
|
|
461
476
|
try {
|
|
462
|
-
r = exports2.storage.getItem("debug");
|
|
477
|
+
r = exports2.storage.getItem("debug") || exports2.storage.getItem("DEBUG");
|
|
463
478
|
} catch (error) {
|
|
464
479
|
}
|
|
465
480
|
if (!r && typeof process !== "undefined" && "env" in process) {
|
|
@@ -600,9 +615,9 @@ var require_supports_color = __commonJS({
|
|
|
600
615
|
}
|
|
601
616
|
});
|
|
602
617
|
|
|
603
|
-
// ../../node_modules/debug/src/node.js
|
|
618
|
+
// ../../node_modules/list-it/node_modules/debug/src/node.js
|
|
604
619
|
var require_node = __commonJS({
|
|
605
|
-
"../../node_modules/debug/src/node.js"(exports2, module2) {
|
|
620
|
+
"../../node_modules/list-it/node_modules/debug/src/node.js"(exports2, module2) {
|
|
606
621
|
"use strict";
|
|
607
622
|
var tty = require("tty");
|
|
608
623
|
var util = require("util");
|
|
@@ -743,7 +758,7 @@ var require_node = __commonJS({
|
|
|
743
758
|
return (/* @__PURE__ */ new Date()).toISOString() + " ";
|
|
744
759
|
}
|
|
745
760
|
function log(...args2) {
|
|
746
|
-
return process.stderr.write(util.
|
|
761
|
+
return process.stderr.write(util.formatWithOptions(exports2.inspectOpts, ...args2) + "\n");
|
|
747
762
|
}
|
|
748
763
|
function save(namespaces) {
|
|
749
764
|
if (namespaces) {
|
|
@@ -775,9 +790,9 @@ var require_node = __commonJS({
|
|
|
775
790
|
}
|
|
776
791
|
});
|
|
777
792
|
|
|
778
|
-
// ../../node_modules/debug/src/index.js
|
|
793
|
+
// ../../node_modules/list-it/node_modules/debug/src/index.js
|
|
779
794
|
var require_src = __commonJS({
|
|
780
|
-
"../../node_modules/debug/src/index.js"(exports2, module2) {
|
|
795
|
+
"../../node_modules/list-it/node_modules/debug/src/index.js"(exports2, module2) {
|
|
781
796
|
"use strict";
|
|
782
797
|
if (typeof process === "undefined" || process.type === "renderer" || process.browser === true || process.__nwjs) {
|
|
783
798
|
module2.exports = require_browser();
|
|
@@ -1666,6 +1681,7 @@ var import_time3 = require("@tradejs/core/time");
|
|
|
1666
1681
|
var import_backtestArtifacts = require("@tradejs/infra/backtestArtifacts");
|
|
1667
1682
|
var import_redis4 = require("@tradejs/infra/redis");
|
|
1668
1683
|
var import_tradingAccounts = require("@tradejs/infra/tradingAccounts");
|
|
1684
|
+
var import_types3 = require("@tradejs/types");
|
|
1669
1685
|
|
|
1670
1686
|
// src/lib/runFormatting.ts
|
|
1671
1687
|
var import_date_fns = require("date-fns");
|
|
@@ -1695,6 +1711,7 @@ var import_progress = __toESM(require("progress"));
|
|
|
1695
1711
|
var import_chalk = __toESM(require("chalk"));
|
|
1696
1712
|
var import_connectors = require("@tradejs/connectors");
|
|
1697
1713
|
var import_connectors2 = require("@tradejs/node/connectors");
|
|
1714
|
+
var import_strategies = require("@tradejs/node/strategies");
|
|
1698
1715
|
var import_timescale = require("@tradejs/infra/timescale");
|
|
1699
1716
|
|
|
1700
1717
|
// src/lib/binanceMarketData.ts
|
|
@@ -1704,14 +1721,6 @@ var MARKET_FEATURE_INTERVAL_MS = {
|
|
|
1704
1721
|
"15m": 9e5,
|
|
1705
1722
|
"1h": 36e5
|
|
1706
1723
|
};
|
|
1707
|
-
var STABLE_QUOTE_SYMBOLS = /* @__PURE__ */ new Set([
|
|
1708
|
-
"USDCUSDT",
|
|
1709
|
-
"FDUSDUSDT",
|
|
1710
|
-
"TUSDUSDT",
|
|
1711
|
-
"BUSDUSDT",
|
|
1712
|
-
"USDPUSDT",
|
|
1713
|
-
"DAIUSDT"
|
|
1714
|
-
]);
|
|
1715
1724
|
var safeDivide = (num, den) => Number.isFinite(num) && Number.isFinite(den) && den !== 0 ? num / den : null;
|
|
1716
1725
|
var mean = (values) => values.length ? values.reduce((sum, value) => sum + value, 0) / values.length : null;
|
|
1717
1726
|
var standardDeviation = (values) => {
|
|
@@ -1755,9 +1764,6 @@ var classifyBtcAltRegime = ({
|
|
|
1755
1764
|
if (btcVsAltReturn24h < -5e-3) return "alt_lead";
|
|
1756
1765
|
return "neutral";
|
|
1757
1766
|
};
|
|
1758
|
-
var selectBreadthUniverseFromTickers = (tickers, limit) => tickers.filter(
|
|
1759
|
-
(ticker) => ticker.symbol.endsWith("USDT") && ticker.symbol !== "BTCUSDT" && !STABLE_QUOTE_SYMBOLS.has(ticker.symbol) && Number.isFinite(ticker.turnover24h) && ticker.turnover24h > 0
|
|
1760
|
-
).sort((a, b) => b.turnover24h - a.turnover24h).slice(0, Math.max(0, limit)).map((ticker) => ticker.symbol);
|
|
1761
1767
|
var aggregateAggTradesToRows = ({
|
|
1762
1768
|
symbol,
|
|
1763
1769
|
interval: interval2,
|
|
@@ -2239,10 +2245,6 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
2239
2245
|
process.env.BINANCE_MARKET_CONTEXT_TRADE_FLOW_SYMBOL_LIMIT,
|
|
2240
2246
|
2
|
|
2241
2247
|
);
|
|
2242
|
-
const breadthLimit = asInt(
|
|
2243
|
-
process.env.BINANCE_MARKET_CONTEXT_BREADTH_LIMIT,
|
|
2244
|
-
30
|
|
2245
|
-
);
|
|
2246
2248
|
const breadthChunkDays = asFloat(
|
|
2247
2249
|
process.env.BINANCE_MARKET_CONTEXT_BREADTH_CHUNK_DAYS,
|
|
2248
2250
|
30
|
|
@@ -2365,88 +2367,113 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
2365
2367
|
}
|
|
2366
2368
|
}
|
|
2367
2369
|
if (includeBreadth) {
|
|
2368
|
-
const
|
|
2369
|
-
const breadthSymbols =
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
);
|
|
2373
|
-
const universe = `binance_top${breadthSymbols.length}_usdt`;
|
|
2374
|
-
const coverage = await (0, import_timescale.getMarketBreadthCoverage)({
|
|
2375
|
-
universe,
|
|
2376
|
-
interval: interval2,
|
|
2377
|
-
startMs: breadthStartMs,
|
|
2378
|
-
endMs
|
|
2379
|
-
});
|
|
2380
|
-
const hasBtcAltMetrics = (coverage?.btcAltMetricsRows ?? 0) >= Math.floor(Math.max(1, coverage?.rows ?? 0) * 0.9);
|
|
2381
|
-
if (!hasCoverage({
|
|
2382
|
-
coverage,
|
|
2370
|
+
const breadthUniverses = (0, import_strategies.getBinanceBreadthUniverses)();
|
|
2371
|
+
const breadthSymbols = breadthUniverses.at(-1)?.symbols ?? [];
|
|
2372
|
+
const connectorInterval = marketIntervalToConnectorInterval(interval2);
|
|
2373
|
+
const chunks = buildBreadthBackfillChunks({
|
|
2383
2374
|
startMs: breadthStartMs,
|
|
2384
2375
|
endMs,
|
|
2385
|
-
intervalMs
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
chunks,
|
|
2396
|
-
coverage,
|
|
2397
|
-
intervalMs
|
|
2398
|
-
});
|
|
2399
|
-
const bar = new import_progress.default(
|
|
2400
|
-
"breadth :current/:total [:bar][:percent] :etas(s) candles=:candles skip=:skip chunk=:chunk :symbol",
|
|
2401
|
-
{
|
|
2402
|
-
total: Math.max(1, missingChunks.length * breadthSymbols.length),
|
|
2403
|
-
width: 24
|
|
2404
|
-
}
|
|
2405
|
-
);
|
|
2406
|
-
let candlesRead = 0;
|
|
2407
|
-
let skippedBreadthChunks = chunks.length - missingChunks.length;
|
|
2408
|
-
if (!missingChunks.length) {
|
|
2409
|
-
bar.tick(1, {
|
|
2410
|
-
candles: 0,
|
|
2411
|
-
skip: skippedBreadthChunks,
|
|
2412
|
-
chunk: "cached",
|
|
2413
|
-
symbol: universe
|
|
2376
|
+
intervalMs,
|
|
2377
|
+
chunkDays: breadthChunkDays
|
|
2378
|
+
});
|
|
2379
|
+
const plans = await Promise.all(
|
|
2380
|
+
breadthUniverses.map(async (definition) => {
|
|
2381
|
+
const coverage = await (0, import_timescale.getMarketBreadthCoverage)({
|
|
2382
|
+
universe: definition.universe,
|
|
2383
|
+
interval: interval2,
|
|
2384
|
+
startMs: breadthStartMs,
|
|
2385
|
+
endMs
|
|
2414
2386
|
});
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
candlesBySymbol[symbol] = candles;
|
|
2431
|
-
candlesRead += candles.length;
|
|
2432
|
-
bar.tick(1, {
|
|
2433
|
-
candles: candlesRead,
|
|
2434
|
-
skip: skippedBreadthChunks,
|
|
2435
|
-
chunk: `${originalChunkIndex + 1}/${chunks.length}`,
|
|
2436
|
-
symbol
|
|
2437
|
-
});
|
|
2387
|
+
const hasBtcAltMetrics = (coverage?.btcAltMetricsRows ?? 0) >= Math.floor(Math.max(1, coverage?.rows ?? 0) * 0.9);
|
|
2388
|
+
const missingChunks2 = !hasCoverage({
|
|
2389
|
+
coverage,
|
|
2390
|
+
startMs: breadthStartMs,
|
|
2391
|
+
endMs,
|
|
2392
|
+
intervalMs
|
|
2393
|
+
}) || !hasBtcAltMetrics ? !hasBtcAltMetrics ? chunks : filterMissingBreadthBackfillChunks({
|
|
2394
|
+
chunks,
|
|
2395
|
+
coverage,
|
|
2396
|
+
intervalMs
|
|
2397
|
+
}) : [];
|
|
2398
|
+
if (!missingChunks2.length) {
|
|
2399
|
+
console.log(
|
|
2400
|
+
import_chalk.default.gray(`breadth cached: universe=${definition.universe}`)
|
|
2401
|
+
);
|
|
2438
2402
|
}
|
|
2439
|
-
|
|
2440
|
-
|
|
2403
|
+
return {
|
|
2404
|
+
definition,
|
|
2405
|
+
missingChunkKeys: new Set(
|
|
2406
|
+
missingChunks2.map((chunk) => `${chunk.startMs}:${chunk.endMs}`)
|
|
2407
|
+
)
|
|
2408
|
+
};
|
|
2409
|
+
})
|
|
2410
|
+
);
|
|
2411
|
+
const missingChunks = chunks.filter((chunk) => {
|
|
2412
|
+
const key = `${chunk.startMs}:${chunk.endMs}`;
|
|
2413
|
+
return plans.some((plan) => plan.missingChunkKeys.has(key));
|
|
2414
|
+
});
|
|
2415
|
+
const bar = new import_progress.default(
|
|
2416
|
+
"breadth :current/:total [:bar][:percent] :etas(s) candles=:candles skip=:skip chunk=:chunk :symbol",
|
|
2417
|
+
{
|
|
2418
|
+
total: Math.max(1, missingChunks.length * breadthSymbols.length),
|
|
2419
|
+
width: 24
|
|
2420
|
+
}
|
|
2421
|
+
);
|
|
2422
|
+
let candlesRead = 0;
|
|
2423
|
+
const skippedBreadthChunks = chunks.length - missingChunks.length;
|
|
2424
|
+
if (!missingChunks.length) {
|
|
2425
|
+
bar.tick(1, {
|
|
2426
|
+
candles: 0,
|
|
2427
|
+
skip: skippedBreadthChunks,
|
|
2428
|
+
chunk: "cached",
|
|
2429
|
+
symbol: "top5/top10/top30/top50/top100"
|
|
2430
|
+
});
|
|
2431
|
+
}
|
|
2432
|
+
for (let chunkIndex = 0; chunkIndex < missingChunks.length; chunkIndex += 1) {
|
|
2433
|
+
const chunk = missingChunks[chunkIndex];
|
|
2434
|
+
const originalChunkIndex = chunks.findIndex(
|
|
2435
|
+
(item) => item.startMs === chunk.startMs && item.endMs === chunk.endMs
|
|
2436
|
+
);
|
|
2437
|
+
const candlesBySymbol = {};
|
|
2438
|
+
for (const symbol of breadthSymbols) {
|
|
2439
|
+
const candles = await connector.kline({
|
|
2440
|
+
symbol,
|
|
2441
2441
|
interval: connectorInterval,
|
|
2442
2442
|
start: chunk.fetchStartMs,
|
|
2443
2443
|
end: chunk.endMs,
|
|
2444
2444
|
silent: true
|
|
2445
2445
|
});
|
|
2446
|
+
candlesBySymbol[symbol] = candles;
|
|
2447
|
+
candlesRead += candles.length;
|
|
2448
|
+
bar.tick(1, {
|
|
2449
|
+
candles: candlesRead,
|
|
2450
|
+
skip: skippedBreadthChunks,
|
|
2451
|
+
chunk: `${originalChunkIndex + 1}/${chunks.length}`,
|
|
2452
|
+
symbol
|
|
2453
|
+
});
|
|
2454
|
+
}
|
|
2455
|
+
const btcCandles = await connector.kline({
|
|
2456
|
+
symbol: "BTCUSDT",
|
|
2457
|
+
interval: connectorInterval,
|
|
2458
|
+
start: chunk.fetchStartMs,
|
|
2459
|
+
end: chunk.endMs,
|
|
2460
|
+
silent: true
|
|
2461
|
+
});
|
|
2462
|
+
const chunkKey = `${chunk.startMs}:${chunk.endMs}`;
|
|
2463
|
+
for (const plan of plans) {
|
|
2464
|
+
if (!plan.missingChunkKeys.has(chunkKey)) {
|
|
2465
|
+
continue;
|
|
2466
|
+
}
|
|
2467
|
+
const symbolSet = new Set(plan.definition.symbols);
|
|
2468
|
+
const universeCandles = Object.fromEntries(
|
|
2469
|
+
Object.entries(candlesBySymbol).filter(
|
|
2470
|
+
([symbol]) => symbolSet.has(symbol)
|
|
2471
|
+
)
|
|
2472
|
+
);
|
|
2446
2473
|
const rows = buildMarketBreadthRows({
|
|
2447
|
-
universe,
|
|
2474
|
+
universe: plan.definition.universe,
|
|
2448
2475
|
interval: interval2,
|
|
2449
|
-
candlesBySymbol,
|
|
2476
|
+
candlesBySymbol: universeCandles,
|
|
2450
2477
|
btcCandles
|
|
2451
2478
|
}).filter((row) => {
|
|
2452
2479
|
const ts = row.ts.getTime();
|
|
@@ -2455,8 +2482,6 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
2455
2482
|
await (0, import_timescale.upsertMarketBreadthRows)(rows);
|
|
2456
2483
|
breadthRows += rows.length;
|
|
2457
2484
|
}
|
|
2458
|
-
} else {
|
|
2459
|
-
console.log(import_chalk.default.gray(`breadth cached: universe=${universe}`));
|
|
2460
2485
|
}
|
|
2461
2486
|
}
|
|
2462
2487
|
console.log(
|
|
@@ -4651,11 +4676,30 @@ var resolveTimeWindow = ({
|
|
|
4651
4676
|
endTime,
|
|
4652
4677
|
nowMs = Date.now(),
|
|
4653
4678
|
defaultStartMs,
|
|
4654
|
-
defaultEndMs
|
|
4679
|
+
defaultEndMs,
|
|
4680
|
+
closedIntervalMs
|
|
4655
4681
|
}) => {
|
|
4656
4682
|
const explicitStart = toEpochMs(startTime);
|
|
4657
4683
|
const explicitEnd = toEpochMs(endTime);
|
|
4658
4684
|
const resolvedDays = toPositiveNumber(days);
|
|
4685
|
+
const alignToLastClosedInterval = (window2, preserveDuration) => {
|
|
4686
|
+
if (typeof closedIntervalMs !== "number" || !Number.isFinite(closedIntervalMs) || closedIntervalMs <= 0) {
|
|
4687
|
+
return window2;
|
|
4688
|
+
}
|
|
4689
|
+
const alignedEnd = Math.floor(window2.end / closedIntervalMs) * closedIntervalMs - 1;
|
|
4690
|
+
const delta = window2.end - alignedEnd;
|
|
4691
|
+
const alignedStart = preserveDuration ? window2.start - delta : window2.start;
|
|
4692
|
+
if (alignedStart >= alignedEnd) {
|
|
4693
|
+
throw new Error(
|
|
4694
|
+
`Invalid closed-candle window: start (${alignedStart}) must be less than end (${alignedEnd})`
|
|
4695
|
+
);
|
|
4696
|
+
}
|
|
4697
|
+
return {
|
|
4698
|
+
...window2,
|
|
4699
|
+
start: Math.trunc(alignedStart),
|
|
4700
|
+
end: Math.trunc(alignedEnd)
|
|
4701
|
+
};
|
|
4702
|
+
};
|
|
4659
4703
|
if (explicitStart != null || explicitEnd != null) {
|
|
4660
4704
|
const resolvedStart = explicitStart ?? (resolvedDays != null && explicitEnd != null ? explicitEnd - resolvedDays * ONE_DAY_MS : defaultStartMs);
|
|
4661
4705
|
const resolvedEnd2 = explicitEnd ?? (resolvedDays != null && explicitStart != null ? explicitStart + resolvedDays * ONE_DAY_MS : defaultEndMs ?? nowMs);
|
|
@@ -4664,11 +4708,14 @@ var resolveTimeWindow = ({
|
|
|
4664
4708
|
`Invalid time window: startTime (${resolvedStart}) must be less than endTime (${resolvedEnd2})`
|
|
4665
4709
|
);
|
|
4666
4710
|
}
|
|
4667
|
-
return
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4711
|
+
return alignToLastClosedInterval(
|
|
4712
|
+
{
|
|
4713
|
+
start: resolvedStart,
|
|
4714
|
+
end: resolvedEnd2,
|
|
4715
|
+
source: "explicit"
|
|
4716
|
+
},
|
|
4717
|
+
explicitStart == null && resolvedDays != null
|
|
4718
|
+
);
|
|
4672
4719
|
}
|
|
4673
4720
|
if (resolvedDays != null) {
|
|
4674
4721
|
const resolvedEnd2 = defaultEndMs ?? nowMs;
|
|
@@ -4678,11 +4725,14 @@ var resolveTimeWindow = ({
|
|
|
4678
4725
|
`Invalid time window: computed start (${resolvedStart}) must be less than end (${resolvedEnd2})`
|
|
4679
4726
|
);
|
|
4680
4727
|
}
|
|
4681
|
-
return
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4728
|
+
return alignToLastClosedInterval(
|
|
4729
|
+
{
|
|
4730
|
+
start: Math.trunc(resolvedStart),
|
|
4731
|
+
end: Math.trunc(resolvedEnd2),
|
|
4732
|
+
source: "days"
|
|
4733
|
+
},
|
|
4734
|
+
true
|
|
4735
|
+
);
|
|
4686
4736
|
}
|
|
4687
4737
|
const resolvedEnd = defaultEndMs ?? nowMs;
|
|
4688
4738
|
if (defaultStartMs >= resolvedEnd) {
|
|
@@ -4690,11 +4740,14 @@ var resolveTimeWindow = ({
|
|
|
4690
4740
|
`Invalid default time window: start (${defaultStartMs}) must be less than end (${resolvedEnd})`
|
|
4691
4741
|
);
|
|
4692
4742
|
}
|
|
4693
|
-
return
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
|
|
4743
|
+
return alignToLastClosedInterval(
|
|
4744
|
+
{
|
|
4745
|
+
start: Math.trunc(defaultStartMs),
|
|
4746
|
+
end: Math.trunc(resolvedEnd),
|
|
4747
|
+
source: "default"
|
|
4748
|
+
},
|
|
4749
|
+
true
|
|
4750
|
+
);
|
|
4698
4751
|
};
|
|
4699
4752
|
|
|
4700
4753
|
// src/lib/runtimeStrategyBacktest.ts
|
|
@@ -5080,7 +5133,8 @@ var prepareRunEnvironment = async ({
|
|
|
5080
5133
|
accountId,
|
|
5081
5134
|
deploymentId,
|
|
5082
5135
|
assetClasses,
|
|
5083
|
-
deployment
|
|
5136
|
+
deployment,
|
|
5137
|
+
closedIntervalMs
|
|
5084
5138
|
}) => {
|
|
5085
5139
|
const connectorName = await resolveRunConnectorName({
|
|
5086
5140
|
value: connector,
|
|
@@ -5143,7 +5197,8 @@ var prepareRunEnvironment = async ({
|
|
|
5143
5197
|
startTime,
|
|
5144
5198
|
endTime,
|
|
5145
5199
|
defaultStartMs: (0, import_time2.getTimestamp)(import_constants2.BACKTEST_DEFAULT_DAYS),
|
|
5146
|
-
defaultEndMs: (0, import_time2.getTimestamp)()
|
|
5200
|
+
defaultEndMs: (0, import_time2.getTimestamp)(),
|
|
5201
|
+
closedIntervalMs
|
|
5147
5202
|
});
|
|
5148
5203
|
const preloadStart = (0, import_time2.getBacktestPreloadStart)(
|
|
5149
5204
|
window2.start,
|
|
@@ -5538,8 +5593,12 @@ var markBacktestRunStatus = async ({
|
|
|
5538
5593
|
};
|
|
5539
5594
|
|
|
5540
5595
|
// src/lib/backtest/runState.ts
|
|
5596
|
+
var import_types2 = require("@tradejs/types");
|
|
5541
5597
|
var successTests = 0;
|
|
5542
5598
|
var errorTests = 0;
|
|
5599
|
+
var warningCounts = {
|
|
5600
|
+
[import_types2.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY]: 0
|
|
5601
|
+
};
|
|
5543
5602
|
var errorMessages = [];
|
|
5544
5603
|
var topResults = [];
|
|
5545
5604
|
var bestTickerResults = /* @__PURE__ */ new Map();
|
|
@@ -5586,6 +5645,14 @@ var addResultToAggregate = (aggregate, result) => {
|
|
|
5586
5645
|
aggregate.winRateCount += 1;
|
|
5587
5646
|
}
|
|
5588
5647
|
};
|
|
5648
|
+
var addResultWarnings = (result) => {
|
|
5649
|
+
for (const code of Object.values(import_types2.BACKTEST_WARNING_CODES)) {
|
|
5650
|
+
const count = Number(result.warningCounts?.[code] ?? 0);
|
|
5651
|
+
if (Number.isFinite(count) && count > 0) {
|
|
5652
|
+
warningCounts[code] = (warningCounts[code] ?? 0) + count;
|
|
5653
|
+
}
|
|
5654
|
+
}
|
|
5655
|
+
};
|
|
5589
5656
|
var getAggregateAverageProfit = (aggregate) => aggregate.count > 0 ? aggregate.netProfitSum / aggregate.count : 0;
|
|
5590
5657
|
var getAggregateWinRate = (aggregate) => {
|
|
5591
5658
|
const closedTrades = aggregate.wins + aggregate.losses;
|
|
@@ -5611,6 +5678,7 @@ var markTestsStarted = () => {
|
|
|
5611
5678
|
var getRunCounters = () => ({
|
|
5612
5679
|
successTests,
|
|
5613
5680
|
errorTests,
|
|
5681
|
+
warningCounts: { ...warningCounts },
|
|
5614
5682
|
errors: [...errorMessages]
|
|
5615
5683
|
});
|
|
5616
5684
|
var getTopResults = () => topResults;
|
|
@@ -5624,6 +5692,7 @@ var setBestTickerResultForSymbol = (symbol, result) => {
|
|
|
5624
5692
|
};
|
|
5625
5693
|
var recordResultAggregates = (result) => {
|
|
5626
5694
|
addResultToAggregate(progressStats, result);
|
|
5695
|
+
addResultWarnings(result);
|
|
5627
5696
|
const configId = result.test.configId || result.test.name;
|
|
5628
5697
|
const existing = configResultBuckets.get(configId);
|
|
5629
5698
|
const bucket = existing ?? {
|
|
@@ -5673,6 +5742,9 @@ var setRuntimeCompareContext = ({
|
|
|
5673
5742
|
var resetRunState = () => {
|
|
5674
5743
|
successTests = 0;
|
|
5675
5744
|
errorTests = 0;
|
|
5745
|
+
for (const code of Object.values(import_types2.BACKTEST_WARNING_CODES)) {
|
|
5746
|
+
warningCounts[code] = 0;
|
|
5747
|
+
}
|
|
5676
5748
|
errorMessages.length = 0;
|
|
5677
5749
|
topResults = [];
|
|
5678
5750
|
bestTickerResults.clear();
|
|
@@ -6269,9 +6341,15 @@ var printRunOutro = () => {
|
|
|
6269
6341
|
import_chalk9.default.gray(`backtest total: done in ${formatDuration(getRunStartedAt())}`)
|
|
6270
6342
|
);
|
|
6271
6343
|
console.log("");
|
|
6272
|
-
const { successTests: successTests2, errorTests: errorTests2 } = getRunCounters();
|
|
6344
|
+
const { successTests: successTests2, errorTests: errorTests2, warningCounts: warningCounts2 } = getRunCounters();
|
|
6273
6345
|
console.log(`${import_chalk9.default.green("SUCCESS TESTS")}: ${successTests2}`);
|
|
6274
6346
|
console.log(`${import_chalk9.default.red("ERRORS")}: ${errorTests2}`);
|
|
6347
|
+
const takeProfitCrossedWarnings = warningCounts2[import_types3.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY] ?? 0;
|
|
6348
|
+
console.log(
|
|
6349
|
+
import_chalk9.default.yellow(
|
|
6350
|
+
`WARNINGS (${import_types3.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY}): ${takeProfitCrossedWarnings}`
|
|
6351
|
+
)
|
|
6352
|
+
);
|
|
6275
6353
|
console.log("");
|
|
6276
6354
|
};
|
|
6277
6355
|
|