@tradejs/cli 3.1.29-beta.292 → 3.1.29-beta.296
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 +563 -485
- package/dist/lib/binanceMarketContextBackfill.js +2 -2
- package/dist/lib/coinMarketCapContextBackfill.js +1 -1
- package/dist/lib/marketContextPrepare.js +3 -3
- package/dist/lib/strategyResearch.js +33 -79
- package/dist/scripts/backtest.js +192 -69
- package/dist/scripts/binanceMarketIngest.js +3 -3
- package/dist/scripts/replay.js +3 -3
- package/dist/scripts/replayRunner.js +3 -3
- package/dist/scripts/runtimeParity.js +3 -3
- package/dist/scripts/signals.js +3 -3
- package/dist/scripts/signalsDaemon.js +3 -3
- package/dist/scripts/strategyRelease.js +6 -37
- package/dist/scripts/strategyResearch.js +36 -82
- package/package.json +7 -7
- package/dist/lib/researchMetricSnapshot.js +0 -74
package/dist/scripts/backtest.js
CHANGED
|
@@ -30,7 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/scripts/backtest.ts
|
|
31
31
|
var backtest_exports = {};
|
|
32
32
|
__export(backtest_exports, {
|
|
33
|
-
BACKTEST_PRELOAD_DAYS: () =>
|
|
33
|
+
BACKTEST_PRELOAD_DAYS: () => import_constants7.BACKTEST_PRELOAD_DAYS,
|
|
34
34
|
backtest: () => backtest,
|
|
35
35
|
chunkTestSuiteBySymbol: () => chunkTestSuiteBySymbol,
|
|
36
36
|
interval: () => interval,
|
|
@@ -62,8 +62,8 @@ var import_chalk10 = __toESM(require("chalk"));
|
|
|
62
62
|
var import_backtest3 = require("@tradejs/core/backtest");
|
|
63
63
|
var import_grid = require("@tradejs/core/grid");
|
|
64
64
|
var import_data = require("@tradejs/core/data");
|
|
65
|
-
var
|
|
66
|
-
var
|
|
65
|
+
var import_redis6 = require("@tradejs/infra/redis");
|
|
66
|
+
var import_constants7 = require("@tradejs/core/constants");
|
|
67
67
|
|
|
68
68
|
// src/lib/backtest/jevOptions.ts
|
|
69
69
|
var import_promises = __toESM(require("fs/promises"));
|
|
@@ -491,13 +491,12 @@ var getReferenceSymbols = () => {
|
|
|
491
491
|
};
|
|
492
492
|
var intervalToMarketFeatureInterval = (interval2) => {
|
|
493
493
|
const normalized = String(interval2).trim().toLowerCase();
|
|
494
|
-
if (normalized === "1" || normalized === "1m") return
|
|
494
|
+
if (normalized === "1" || normalized === "1m") return null;
|
|
495
495
|
if (normalized === "5" || normalized === "5m") return "5m";
|
|
496
496
|
if (normalized === "60" || normalized === "1h") return "1h";
|
|
497
497
|
return "15m";
|
|
498
498
|
};
|
|
499
499
|
var marketIntervalToConnectorInterval = (interval2) => {
|
|
500
|
-
if (interval2 === "1m") return "1";
|
|
501
500
|
if (interval2 === "5m") return "5";
|
|
502
501
|
if (interval2 === "1h") return "60";
|
|
503
502
|
return "15";
|
|
@@ -662,6 +661,7 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
662
661
|
return skippedBackfillResult();
|
|
663
662
|
}
|
|
664
663
|
const interval2 = intervalToMarketFeatureInterval(params.interval);
|
|
664
|
+
if (interval2 === null) return skippedBackfillResult();
|
|
665
665
|
const intervalMs = MARKET_FEATURE_INTERVAL_MS[interval2];
|
|
666
666
|
const { breadthStartMs, tradeFlowStartMs, endMs } = resolveBinanceMarketContextBackfillWindow(params);
|
|
667
667
|
if (endMs <= Math.min(breadthStartMs, tradeFlowStartMs)) {
|
|
@@ -2242,7 +2242,7 @@ var coinMarketCapFetch = async (params) => {
|
|
|
2242
2242
|
};
|
|
2243
2243
|
var fetchCoinMarketCapIndexRows = async (params) => {
|
|
2244
2244
|
const rows = [];
|
|
2245
|
-
const maxChunkDays =
|
|
2245
|
+
const maxChunkDays = 4;
|
|
2246
2246
|
const totalChunks = Math.max(
|
|
2247
2247
|
1,
|
|
2248
2248
|
Math.ceil(
|
|
@@ -4777,7 +4777,9 @@ var executeBacktestWorkerPool = async ({
|
|
|
4777
4777
|
chunkTestSuite,
|
|
4778
4778
|
getProgressSnapshot,
|
|
4779
4779
|
initialCompletedTests = 0,
|
|
4780
|
-
totalTests
|
|
4780
|
+
totalTests,
|
|
4781
|
+
onProgress,
|
|
4782
|
+
onIncomplete
|
|
4781
4783
|
}) => {
|
|
4782
4784
|
const chunks = chunkTestSuite(testSuite);
|
|
4783
4785
|
const fastAiWorkerPgPoolMax = resolveFastAiWorkerPgPoolMax(
|
|
@@ -4793,7 +4795,9 @@ var executeBacktestWorkerPool = async ({
|
|
|
4793
4795
|
);
|
|
4794
4796
|
let completedTests = initialCompletedTests;
|
|
4795
4797
|
let renderedTests = 0;
|
|
4798
|
+
let pendingResults = 0;
|
|
4796
4799
|
let isFinishing = false;
|
|
4800
|
+
let isInterrupted = false;
|
|
4797
4801
|
let signalHandlersRemoved = false;
|
|
4798
4802
|
const workers = /* @__PURE__ */ new Set();
|
|
4799
4803
|
const stopWorkers = () => {
|
|
@@ -4804,6 +4808,7 @@ var executeBacktestWorkerPool = async ({
|
|
|
4804
4808
|
}
|
|
4805
4809
|
};
|
|
4806
4810
|
const handleInterrupt = (signal, exitCode) => {
|
|
4811
|
+
isInterrupted = true;
|
|
4807
4812
|
stopWorkers();
|
|
4808
4813
|
void Promise.resolve(onInterrupt?.(signal)).catch((error) => {
|
|
4809
4814
|
console.error(
|
|
@@ -4824,15 +4829,16 @@ var executeBacktestWorkerPool = async ({
|
|
|
4824
4829
|
process.removeListener("SIGTERM", handleSigterm);
|
|
4825
4830
|
};
|
|
4826
4831
|
const maybeFinish = async () => {
|
|
4827
|
-
if (isFinishing) {
|
|
4828
|
-
return;
|
|
4829
|
-
}
|
|
4830
|
-
if (completedTests !== totalExpectedTests || workers.size > 0) {
|
|
4832
|
+
if (isFinishing || isInterrupted || workers.size > 0 || pendingResults > 0) {
|
|
4831
4833
|
return;
|
|
4832
4834
|
}
|
|
4833
4835
|
isFinishing = true;
|
|
4834
4836
|
try {
|
|
4835
|
-
|
|
4837
|
+
if (completedTests === totalExpectedTests) {
|
|
4838
|
+
await onFinish();
|
|
4839
|
+
} else {
|
|
4840
|
+
await onIncomplete?.();
|
|
4841
|
+
}
|
|
4836
4842
|
} finally {
|
|
4837
4843
|
cleanupSignalHandlers();
|
|
4838
4844
|
}
|
|
@@ -4891,14 +4897,22 @@ var executeBacktestWorkerPool = async ({
|
|
|
4891
4897
|
return;
|
|
4892
4898
|
}
|
|
4893
4899
|
completedTests++;
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4900
|
+
onProgress?.(completedTests);
|
|
4901
|
+
pendingResults++;
|
|
4902
|
+
try {
|
|
4903
|
+
await onMessage(msg);
|
|
4904
|
+
if (completedTests % progressStep2 === 0 || completedTests === totalExpectedTests) {
|
|
4905
|
+
tickProgress();
|
|
4906
|
+
}
|
|
4907
|
+
} finally {
|
|
4908
|
+
pendingResults--;
|
|
4897
4909
|
}
|
|
4910
|
+
await maybeFinish();
|
|
4898
4911
|
});
|
|
4899
4912
|
tester.on("error", (err) => {
|
|
4900
4913
|
workers.delete(tester);
|
|
4901
4914
|
onWorkerError(err?.message ?? String(err));
|
|
4915
|
+
void maybeFinish();
|
|
4902
4916
|
});
|
|
4903
4917
|
tester.on("exit", (code) => {
|
|
4904
4918
|
workers.delete(tester);
|
|
@@ -5285,7 +5299,9 @@ var executeTestSuite = async ({
|
|
|
5285
5299
|
onFinish,
|
|
5286
5300
|
initialCompletedTests = 0,
|
|
5287
5301
|
totalTests,
|
|
5288
|
-
backtestRunId
|
|
5302
|
+
backtestRunId,
|
|
5303
|
+
onProgress,
|
|
5304
|
+
onIncomplete
|
|
5289
5305
|
}) => {
|
|
5290
5306
|
markTestsStarted();
|
|
5291
5307
|
const { testerWorkerPath, testerNeedsTsRuntime } = resolveTesterWorker();
|
|
@@ -5309,6 +5325,8 @@ var executeTestSuite = async ({
|
|
|
5309
5325
|
chunkTestSuite: (suite) => chunkTestSuiteBySymbol(suite, effectiveParallel),
|
|
5310
5326
|
initialCompletedTests,
|
|
5311
5327
|
totalTests,
|
|
5328
|
+
onProgress,
|
|
5329
|
+
onIncomplete,
|
|
5312
5330
|
onInterrupt,
|
|
5313
5331
|
onMessage: async (msg) => {
|
|
5314
5332
|
if (msg.error) {
|
|
@@ -5362,6 +5380,106 @@ var printRunOutro = () => {
|
|
|
5362
5380
|
console.log("");
|
|
5363
5381
|
};
|
|
5364
5382
|
|
|
5383
|
+
// src/lib/backtest/observeRun.ts
|
|
5384
|
+
var import_constants6 = require("@tradejs/core/constants");
|
|
5385
|
+
var import_redis5 = require("@tradejs/infra/redis");
|
|
5386
|
+
var import_logger = require("@tradejs/infra/logger");
|
|
5387
|
+
var HEARTBEAT_MS = 2e3;
|
|
5388
|
+
var ACTIVE_TTL_SECONDS = 30;
|
|
5389
|
+
var LINKED_RUN_TTL_SECONDS = 30 * 24 * import_constants6.TTL_1H;
|
|
5390
|
+
var RESEARCH_STUDY = /^[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}\/\d{4}-\d{2}-\d{2}-[a-zA-Z0-9][a-zA-Z0-9_.-]{0,127}$/;
|
|
5391
|
+
var MAX_VALUES_PER_PARAMETER = 12;
|
|
5392
|
+
var MAX_VALUE_LENGTH = 500;
|
|
5393
|
+
var summarizeBacktestParameters = (tests) => {
|
|
5394
|
+
const parameters = /* @__PURE__ */ new Map();
|
|
5395
|
+
for (const test of tests) {
|
|
5396
|
+
for (const [name, value] of Object.entries(test.strategyConfig)) {
|
|
5397
|
+
const serialized = JSON.stringify(value);
|
|
5398
|
+
if (serialized === void 0) continue;
|
|
5399
|
+
const parameter = parameters.get(name) ?? {
|
|
5400
|
+
values: /* @__PURE__ */ new Set(),
|
|
5401
|
+
truncated: false
|
|
5402
|
+
};
|
|
5403
|
+
parameters.set(name, parameter);
|
|
5404
|
+
if (parameter.values.has(serialized)) continue;
|
|
5405
|
+
if (parameter.values.size === MAX_VALUES_PER_PARAMETER || serialized.length > MAX_VALUE_LENGTH) {
|
|
5406
|
+
parameter.truncated = true;
|
|
5407
|
+
} else {
|
|
5408
|
+
parameter.values.add(serialized);
|
|
5409
|
+
}
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
return [...parameters].map(([name, { values, truncated }]) => ({
|
|
5413
|
+
name,
|
|
5414
|
+
values: [...values],
|
|
5415
|
+
truncated
|
|
5416
|
+
}));
|
|
5417
|
+
};
|
|
5418
|
+
var observeBacktestRun = async (manifest, initialCompleted, parallel) => {
|
|
5419
|
+
if (process.env.TRADEJS_BACKTEST_WEB_JOB === "1") return;
|
|
5420
|
+
const researchStudy = process.env.TRADEJS_RESEARCH_STUDY?.trim();
|
|
5421
|
+
const linkedStudy = researchStudy && RESEARCH_STUDY.test(researchStudy) ? researchStudy : void 0;
|
|
5422
|
+
if (researchStudy && !linkedStudy) {
|
|
5423
|
+
import_logger.logger.warn(
|
|
5424
|
+
"invalid TRADEJS_RESEARCH_STUDY; expected Strategy/YYYY-MM-DD-slug"
|
|
5425
|
+
);
|
|
5426
|
+
}
|
|
5427
|
+
const { testSuite } = manifest;
|
|
5428
|
+
const firstTest = testSuite[0];
|
|
5429
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5430
|
+
const run = {
|
|
5431
|
+
id: manifest.runId,
|
|
5432
|
+
...linkedStudy && { researchStudy: linkedStudy },
|
|
5433
|
+
userName: manifest.userName,
|
|
5434
|
+
strategyName: firstTest.strategyName,
|
|
5435
|
+
configId: manifest.config,
|
|
5436
|
+
connector: manifest.connectorName,
|
|
5437
|
+
interval: manifest.interval,
|
|
5438
|
+
startTime: manifest.window.start,
|
|
5439
|
+
endTime: manifest.window.end,
|
|
5440
|
+
symbols: [...new Set(testSuite.map((test) => test.symbol))],
|
|
5441
|
+
settings: {
|
|
5442
|
+
...manifest.flags,
|
|
5443
|
+
parallel,
|
|
5444
|
+
...firstTest.executionCosts && {
|
|
5445
|
+
executionCosts: JSON.stringify(firstTest.executionCosts)
|
|
5446
|
+
}
|
|
5447
|
+
},
|
|
5448
|
+
parameters: summarizeBacktestParameters(testSuite),
|
|
5449
|
+
status: "running",
|
|
5450
|
+
completed: initialCompleted,
|
|
5451
|
+
total: testSuite.length,
|
|
5452
|
+
startedAt,
|
|
5453
|
+
updatedAt: startedAt
|
|
5454
|
+
};
|
|
5455
|
+
const save = async (expire) => {
|
|
5456
|
+
run.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5457
|
+
try {
|
|
5458
|
+
await import_redis5.cliBacktestRunStorage.put(run, expire);
|
|
5459
|
+
} catch (error) {
|
|
5460
|
+
import_logger.logger.warn("failed to publish CLI backtest progress: %s", String(error));
|
|
5461
|
+
}
|
|
5462
|
+
};
|
|
5463
|
+
await save(ACTIVE_TTL_SECONDS);
|
|
5464
|
+
let pending;
|
|
5465
|
+
const timer = setInterval(() => {
|
|
5466
|
+
pending ??= save(ACTIVE_TTL_SECONDS).finally(() => {
|
|
5467
|
+
pending = void 0;
|
|
5468
|
+
});
|
|
5469
|
+
}, HEARTBEAT_MS).unref();
|
|
5470
|
+
return {
|
|
5471
|
+
progress: (completed) => {
|
|
5472
|
+
run.completed = completed;
|
|
5473
|
+
},
|
|
5474
|
+
finish: async (status) => {
|
|
5475
|
+
clearInterval(timer);
|
|
5476
|
+
await pending;
|
|
5477
|
+
run.status = status;
|
|
5478
|
+
await save(linkedStudy ? LINKED_RUN_TTL_SECONDS : import_constants6.TTL_1H);
|
|
5479
|
+
}
|
|
5480
|
+
};
|
|
5481
|
+
};
|
|
5482
|
+
|
|
5365
5483
|
// src/scripts/backtest.ts
|
|
5366
5484
|
if (process.argv.some(
|
|
5367
5485
|
(arg) => arg === "--live" || String(arg).startsWith("--live=")
|
|
@@ -5497,8 +5615,8 @@ var loadBacktestConfig = async () => {
|
|
|
5497
5615
|
if (!strategyName) {
|
|
5498
5616
|
throw new Error("Strategy name not found");
|
|
5499
5617
|
}
|
|
5500
|
-
const backtestConfig = await (0,
|
|
5501
|
-
|
|
5618
|
+
const backtestConfig = await (0, import_redis6.getData)(
|
|
5619
|
+
import_redis6.redisKeys.backtestConfig(userName, flags.config),
|
|
5502
5620
|
null
|
|
5503
5621
|
);
|
|
5504
5622
|
if (!backtestConfig) {
|
|
@@ -5645,8 +5763,8 @@ var finishBacktest = async (testSuite) => {
|
|
|
5645
5763
|
console.log(import_chalk10.default.gray(`full report: ${markdownReportPath}`));
|
|
5646
5764
|
console.log("");
|
|
5647
5765
|
if (!isFastMode || flags.persistResults) {
|
|
5648
|
-
await (0,
|
|
5649
|
-
|
|
5766
|
+
await (0, import_redis6.setData)(
|
|
5767
|
+
import_redis6.redisKeys.backtestResults(userName, flags.config, timestamp),
|
|
5650
5768
|
{
|
|
5651
5769
|
config: flags.config,
|
|
5652
5770
|
mode: "config",
|
|
@@ -5666,11 +5784,10 @@ var finishBacktest = async (testSuite) => {
|
|
|
5666
5784
|
errorTests: errorTests2
|
|
5667
5785
|
},
|
|
5668
5786
|
{
|
|
5669
|
-
expire:
|
|
5787
|
+
expire: import_constants7.TTL_1M
|
|
5670
5788
|
}
|
|
5671
5789
|
);
|
|
5672
5790
|
}
|
|
5673
|
-
process.exit();
|
|
5674
5791
|
};
|
|
5675
5792
|
var restoreCompletedBacktestResults = (completed) => {
|
|
5676
5793
|
for (const { result } of completed) {
|
|
@@ -5751,6 +5868,56 @@ var prepareContinuedBacktestMarketContext = async ({
|
|
|
5751
5868
|
log: (message) => console.log(import_chalk10.default.gray(message))
|
|
5752
5869
|
});
|
|
5753
5870
|
};
|
|
5871
|
+
var runBacktestSuite = async (manifest, testSuite, initialCompletedTests = 0) => {
|
|
5872
|
+
const observation = await observeBacktestRun(
|
|
5873
|
+
manifest,
|
|
5874
|
+
initialCompletedTests,
|
|
5875
|
+
effectiveParallel
|
|
5876
|
+
);
|
|
5877
|
+
try {
|
|
5878
|
+
await executeTestSuite({
|
|
5879
|
+
testSuite,
|
|
5880
|
+
window: manifest.window,
|
|
5881
|
+
preloadStart: manifest.preloadStart,
|
|
5882
|
+
backtestRunId: manifest.runId,
|
|
5883
|
+
initialCompletedTests,
|
|
5884
|
+
totalTests: manifest.testSuite.length,
|
|
5885
|
+
onProgress: observation?.progress,
|
|
5886
|
+
onIncomplete: async () => {
|
|
5887
|
+
await observation?.finish("failed");
|
|
5888
|
+
await markBacktestRunStatus({ run: manifest, status: "interrupted" });
|
|
5889
|
+
process.exit(1);
|
|
5890
|
+
},
|
|
5891
|
+
onResult: async (result) => {
|
|
5892
|
+
await saveBacktestCheckpointResult({
|
|
5893
|
+
result,
|
|
5894
|
+
runId: manifest.runId,
|
|
5895
|
+
userName
|
|
5896
|
+
});
|
|
5897
|
+
trackTopResult(result);
|
|
5898
|
+
updateBestTickerResult(result);
|
|
5899
|
+
},
|
|
5900
|
+
onInterrupt: async () => {
|
|
5901
|
+
await observation?.finish("cancelled");
|
|
5902
|
+
await markBacktestRunStatus({ run: manifest, status: "interrupted" });
|
|
5903
|
+
},
|
|
5904
|
+
onFinish: async () => {
|
|
5905
|
+
try {
|
|
5906
|
+
await markBacktestRunStatus({ run: manifest, status: "completed" });
|
|
5907
|
+
await finishBacktest(manifest.testSuite);
|
|
5908
|
+
await observation?.finish("completed");
|
|
5909
|
+
process.exit();
|
|
5910
|
+
} catch (error) {
|
|
5911
|
+
await observation?.finish("failed");
|
|
5912
|
+
throw error;
|
|
5913
|
+
}
|
|
5914
|
+
}
|
|
5915
|
+
});
|
|
5916
|
+
} catch (error) {
|
|
5917
|
+
await observation?.finish("failed");
|
|
5918
|
+
throw error;
|
|
5919
|
+
}
|
|
5920
|
+
};
|
|
5754
5921
|
var backtest = async () => {
|
|
5755
5922
|
resetRunState();
|
|
5756
5923
|
const config = await loadBacktestConfig();
|
|
@@ -5765,33 +5932,10 @@ var backtest = async () => {
|
|
|
5765
5932
|
if (!remainingSuite.length) {
|
|
5766
5933
|
await markBacktestRunStatus({ run: manifest2, status: "completed" });
|
|
5767
5934
|
await finishBacktest(manifest2.testSuite);
|
|
5768
|
-
|
|
5935
|
+
process.exit();
|
|
5769
5936
|
}
|
|
5770
5937
|
await prepareContinuedBacktestMarketContext({ manifest: manifest2, remainingSuite });
|
|
5771
|
-
await
|
|
5772
|
-
testSuite: remainingSuite,
|
|
5773
|
-
window: manifest2.window,
|
|
5774
|
-
preloadStart: manifest2.preloadStart,
|
|
5775
|
-
backtestRunId: manifest2.runId,
|
|
5776
|
-
initialCompletedTests: completed.length,
|
|
5777
|
-
totalTests: manifest2.testSuite.length,
|
|
5778
|
-
onResult: async (result) => {
|
|
5779
|
-
await saveBacktestCheckpointResult({
|
|
5780
|
-
result,
|
|
5781
|
-
runId: manifest2.runId,
|
|
5782
|
-
userName
|
|
5783
|
-
});
|
|
5784
|
-
trackTopResult(result);
|
|
5785
|
-
updateBestTickerResult(result);
|
|
5786
|
-
},
|
|
5787
|
-
onInterrupt: async () => {
|
|
5788
|
-
await markBacktestRunStatus({ run: manifest2, status: "interrupted" });
|
|
5789
|
-
},
|
|
5790
|
-
onFinish: async () => {
|
|
5791
|
-
await markBacktestRunStatus({ run: manifest2, status: "completed" });
|
|
5792
|
-
await finishBacktest(manifest2.testSuite);
|
|
5793
|
-
}
|
|
5794
|
-
});
|
|
5938
|
+
await runBacktestSuite(manifest2, remainingSuite, completed.length);
|
|
5795
5939
|
return;
|
|
5796
5940
|
}
|
|
5797
5941
|
const preparedRun = await prepareRunEnvironment2(config.strategyName);
|
|
@@ -5852,28 +5996,7 @@ var backtest = async () => {
|
|
|
5852
5996
|
testSuite
|
|
5853
5997
|
});
|
|
5854
5998
|
console.log(import_chalk10.default.gray(`backtest run id: ${manifest.runId}`));
|
|
5855
|
-
await
|
|
5856
|
-
testSuite,
|
|
5857
|
-
window: preparedRun.window,
|
|
5858
|
-
preloadStart: preparedRun.preloadStart,
|
|
5859
|
-
backtestRunId: manifest.runId,
|
|
5860
|
-
onResult: async (result) => {
|
|
5861
|
-
await saveBacktestCheckpointResult({
|
|
5862
|
-
result,
|
|
5863
|
-
runId: manifest.runId,
|
|
5864
|
-
userName
|
|
5865
|
-
});
|
|
5866
|
-
trackTopResult(result);
|
|
5867
|
-
updateBestTickerResult(result);
|
|
5868
|
-
},
|
|
5869
|
-
onInterrupt: async () => {
|
|
5870
|
-
await markBacktestRunStatus({ run: manifest, status: "interrupted" });
|
|
5871
|
-
},
|
|
5872
|
-
onFinish: async () => {
|
|
5873
|
-
await markBacktestRunStatus({ run: manifest, status: "completed" });
|
|
5874
|
-
await finishBacktest(testSuite);
|
|
5875
|
-
}
|
|
5876
|
-
});
|
|
5999
|
+
await runBacktestSuite(manifest, testSuite);
|
|
5877
6000
|
};
|
|
5878
6001
|
var main = backtest;
|
|
5879
6002
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -352,7 +352,7 @@ var buildMarketBreadthRows = ({
|
|
|
352
352
|
|
|
353
353
|
// src/scripts/binanceMarketIngest.ts
|
|
354
354
|
import_args.default.example(
|
|
355
|
-
"yarn cli:node8g binance:market-ingest --all --symbols BTCUSDT,ETHUSDT --days 0.05 --interval
|
|
355
|
+
"yarn cli:node8g binance:market-ingest --all --symbols BTCUSDT,ETHUSDT --days 0.05 --interval 15m --write",
|
|
356
356
|
"Ingest Binance public market breadth and historical aggTrades buckets"
|
|
357
357
|
);
|
|
358
358
|
import_args.default.option(
|
|
@@ -360,7 +360,7 @@ import_args.default.option(
|
|
|
360
360
|
"Comma-separated target symbols",
|
|
361
361
|
"BTCUSDT,ETHUSDT"
|
|
362
362
|
);
|
|
363
|
-
import_args.default.option(["i", "interval"], "Aggregation interval:
|
|
363
|
+
import_args.default.option(["i", "interval"], "Aggregation interval: 5m,15m,1h", "15m");
|
|
364
364
|
import_args.default.option(["d", "days"], "Lookback window in days", "1");
|
|
365
365
|
import_args.default.option(["h", "hours"], "Lookback window in hours; overrides --days");
|
|
366
366
|
import_args.default.option(["a", "aggTrades"], "Fetch and bucket Binance aggTrades");
|
|
@@ -394,7 +394,6 @@ var asInt = (value, fallback) => {
|
|
|
394
394
|
};
|
|
395
395
|
var sleep = (ms) => ms > 0 ? new Promise((resolve) => setTimeout(resolve, ms)) : Promise.resolve();
|
|
396
396
|
var intervalToConnectorInterval = (interval) => {
|
|
397
|
-
if (interval === "1m") return "1";
|
|
398
397
|
if (interval === "5m") return "5";
|
|
399
398
|
if (interval === "15m") return "15";
|
|
400
399
|
return "60";
|
|
@@ -450,6 +449,7 @@ var printEstimate = (estimate) => {
|
|
|
450
449
|
var main = async () => {
|
|
451
450
|
const symbols = normalizeBinanceSymbols(flags.symbols);
|
|
452
451
|
const interval = normalizeMarketFeatureInterval(flags.interval);
|
|
452
|
+
if (interval === "1m") throw new Error("Use interval 5m, 15m, or 1h");
|
|
453
453
|
const hours = flags.hours == null ? null : asFloat(flags.hours, 0);
|
|
454
454
|
const days = hours != null && hours > 0 ? hours / 24 : asFloat(flags.days, 1);
|
|
455
455
|
const breadthUniverses = (0, import_strategies.getBinanceBreadthUniverses)();
|
package/dist/scripts/replay.js
CHANGED
|
@@ -1515,13 +1515,12 @@ var getReferenceSymbols = () => {
|
|
|
1515
1515
|
};
|
|
1516
1516
|
var intervalToMarketFeatureInterval = (interval) => {
|
|
1517
1517
|
const normalized = String(interval).trim().toLowerCase();
|
|
1518
|
-
if (normalized === "1" || normalized === "1m") return
|
|
1518
|
+
if (normalized === "1" || normalized === "1m") return null;
|
|
1519
1519
|
if (normalized === "5" || normalized === "5m") return "5m";
|
|
1520
1520
|
if (normalized === "60" || normalized === "1h") return "1h";
|
|
1521
1521
|
return "15m";
|
|
1522
1522
|
};
|
|
1523
1523
|
var marketIntervalToConnectorInterval = (interval) => {
|
|
1524
|
-
if (interval === "1m") return "1";
|
|
1525
1524
|
if (interval === "5m") return "5";
|
|
1526
1525
|
if (interval === "1h") return "60";
|
|
1527
1526
|
return "15";
|
|
@@ -1686,6 +1685,7 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
1686
1685
|
return skippedBackfillResult();
|
|
1687
1686
|
}
|
|
1688
1687
|
const interval = intervalToMarketFeatureInterval(params.interval);
|
|
1688
|
+
if (interval === null) return skippedBackfillResult();
|
|
1689
1689
|
const intervalMs = MARKET_FEATURE_INTERVAL_MS[interval];
|
|
1690
1690
|
const { breadthStartMs, tradeFlowStartMs, endMs } = resolveBinanceMarketContextBackfillWindow(params);
|
|
1691
1691
|
if (endMs <= Math.min(breadthStartMs, tradeFlowStartMs)) {
|
|
@@ -3266,7 +3266,7 @@ var coinMarketCapFetch = async (params) => {
|
|
|
3266
3266
|
};
|
|
3267
3267
|
var fetchCoinMarketCapIndexRows = async (params) => {
|
|
3268
3268
|
const rows = [];
|
|
3269
|
-
const maxChunkDays =
|
|
3269
|
+
const maxChunkDays = 4;
|
|
3270
3270
|
const totalChunks = Math.max(
|
|
3271
3271
|
1,
|
|
3272
3272
|
Math.ceil(
|
|
@@ -1520,13 +1520,12 @@ var getReferenceSymbols = () => {
|
|
|
1520
1520
|
};
|
|
1521
1521
|
var intervalToMarketFeatureInterval = (interval) => {
|
|
1522
1522
|
const normalized = String(interval).trim().toLowerCase();
|
|
1523
|
-
if (normalized === "1" || normalized === "1m") return
|
|
1523
|
+
if (normalized === "1" || normalized === "1m") return null;
|
|
1524
1524
|
if (normalized === "5" || normalized === "5m") return "5m";
|
|
1525
1525
|
if (normalized === "60" || normalized === "1h") return "1h";
|
|
1526
1526
|
return "15m";
|
|
1527
1527
|
};
|
|
1528
1528
|
var marketIntervalToConnectorInterval = (interval) => {
|
|
1529
|
-
if (interval === "1m") return "1";
|
|
1530
1529
|
if (interval === "5m") return "5";
|
|
1531
1530
|
if (interval === "1h") return "60";
|
|
1532
1531
|
return "15";
|
|
@@ -1691,6 +1690,7 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
1691
1690
|
return skippedBackfillResult();
|
|
1692
1691
|
}
|
|
1693
1692
|
const interval = intervalToMarketFeatureInterval(params.interval);
|
|
1693
|
+
if (interval === null) return skippedBackfillResult();
|
|
1694
1694
|
const intervalMs = MARKET_FEATURE_INTERVAL_MS[interval];
|
|
1695
1695
|
const { breadthStartMs, tradeFlowStartMs, endMs } = resolveBinanceMarketContextBackfillWindow(params);
|
|
1696
1696
|
if (endMs <= Math.min(breadthStartMs, tradeFlowStartMs)) {
|
|
@@ -3271,7 +3271,7 @@ var coinMarketCapFetch = async (params) => {
|
|
|
3271
3271
|
};
|
|
3272
3272
|
var fetchCoinMarketCapIndexRows = async (params) => {
|
|
3273
3273
|
const rows = [];
|
|
3274
|
-
const maxChunkDays =
|
|
3274
|
+
const maxChunkDays = 4;
|
|
3275
3275
|
const totalChunks = Math.max(
|
|
3276
3276
|
1,
|
|
3277
3277
|
Math.ceil(
|
|
@@ -1028,13 +1028,12 @@ var getReferenceSymbols = () => {
|
|
|
1028
1028
|
};
|
|
1029
1029
|
var intervalToMarketFeatureInterval = (interval2) => {
|
|
1030
1030
|
const normalized = String(interval2).trim().toLowerCase();
|
|
1031
|
-
if (normalized === "1" || normalized === "1m") return
|
|
1031
|
+
if (normalized === "1" || normalized === "1m") return null;
|
|
1032
1032
|
if (normalized === "5" || normalized === "5m") return "5m";
|
|
1033
1033
|
if (normalized === "60" || normalized === "1h") return "1h";
|
|
1034
1034
|
return "15m";
|
|
1035
1035
|
};
|
|
1036
1036
|
var marketIntervalToConnectorInterval = (interval2) => {
|
|
1037
|
-
if (interval2 === "1m") return "1";
|
|
1038
1037
|
if (interval2 === "5m") return "5";
|
|
1039
1038
|
if (interval2 === "1h") return "60";
|
|
1040
1039
|
return "15";
|
|
@@ -1199,6 +1198,7 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
1199
1198
|
return skippedBackfillResult();
|
|
1200
1199
|
}
|
|
1201
1200
|
const interval2 = intervalToMarketFeatureInterval(params.interval);
|
|
1201
|
+
if (interval2 === null) return skippedBackfillResult();
|
|
1202
1202
|
const intervalMs = MARKET_FEATURE_INTERVAL_MS[interval2];
|
|
1203
1203
|
const { breadthStartMs, tradeFlowStartMs, endMs } = resolveBinanceMarketContextBackfillWindow(params);
|
|
1204
1204
|
if (endMs <= Math.min(breadthStartMs, tradeFlowStartMs)) {
|
|
@@ -2779,7 +2779,7 @@ var coinMarketCapFetch = async (params) => {
|
|
|
2779
2779
|
};
|
|
2780
2780
|
var fetchCoinMarketCapIndexRows = async (params) => {
|
|
2781
2781
|
const rows = [];
|
|
2782
|
-
const maxChunkDays =
|
|
2782
|
+
const maxChunkDays = 4;
|
|
2783
2783
|
const totalChunks = Math.max(
|
|
2784
2784
|
1,
|
|
2785
2785
|
Math.ceil(
|
package/dist/scripts/signals.js
CHANGED
|
@@ -488,13 +488,12 @@ var getReferenceSymbols = () => {
|
|
|
488
488
|
};
|
|
489
489
|
var intervalToMarketFeatureInterval = (interval) => {
|
|
490
490
|
const normalized = String(interval).trim().toLowerCase();
|
|
491
|
-
if (normalized === "1" || normalized === "1m") return
|
|
491
|
+
if (normalized === "1" || normalized === "1m") return null;
|
|
492
492
|
if (normalized === "5" || normalized === "5m") return "5m";
|
|
493
493
|
if (normalized === "60" || normalized === "1h") return "1h";
|
|
494
494
|
return "15m";
|
|
495
495
|
};
|
|
496
496
|
var marketIntervalToConnectorInterval = (interval) => {
|
|
497
|
-
if (interval === "1m") return "1";
|
|
498
497
|
if (interval === "5m") return "5";
|
|
499
498
|
if (interval === "1h") return "60";
|
|
500
499
|
return "15";
|
|
@@ -659,6 +658,7 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
659
658
|
return skippedBackfillResult();
|
|
660
659
|
}
|
|
661
660
|
const interval = intervalToMarketFeatureInterval(params.interval);
|
|
661
|
+
if (interval === null) return skippedBackfillResult();
|
|
662
662
|
const intervalMs = MARKET_FEATURE_INTERVAL_MS[interval];
|
|
663
663
|
const { breadthStartMs, tradeFlowStartMs, endMs } = resolveBinanceMarketContextBackfillWindow(params);
|
|
664
664
|
if (endMs <= Math.min(breadthStartMs, tradeFlowStartMs)) {
|
|
@@ -2239,7 +2239,7 @@ var coinMarketCapFetch = async (params) => {
|
|
|
2239
2239
|
};
|
|
2240
2240
|
var fetchCoinMarketCapIndexRows = async (params) => {
|
|
2241
2241
|
const rows = [];
|
|
2242
|
-
const maxChunkDays =
|
|
2242
|
+
const maxChunkDays = 4;
|
|
2243
2243
|
const totalChunks = Math.max(
|
|
2244
2244
|
1,
|
|
2245
2245
|
Math.ceil(
|
|
@@ -486,13 +486,12 @@ var getReferenceSymbols = () => {
|
|
|
486
486
|
};
|
|
487
487
|
var intervalToMarketFeatureInterval = (interval) => {
|
|
488
488
|
const normalized = String(interval).trim().toLowerCase();
|
|
489
|
-
if (normalized === "1" || normalized === "1m") return
|
|
489
|
+
if (normalized === "1" || normalized === "1m") return null;
|
|
490
490
|
if (normalized === "5" || normalized === "5m") return "5m";
|
|
491
491
|
if (normalized === "60" || normalized === "1h") return "1h";
|
|
492
492
|
return "15m";
|
|
493
493
|
};
|
|
494
494
|
var marketIntervalToConnectorInterval = (interval) => {
|
|
495
|
-
if (interval === "1m") return "1";
|
|
496
495
|
if (interval === "5m") return "5";
|
|
497
496
|
if (interval === "1h") return "60";
|
|
498
497
|
return "15";
|
|
@@ -657,6 +656,7 @@ var backfillBinanceMarketContext = async (params, enabled) => {
|
|
|
657
656
|
return skippedBackfillResult();
|
|
658
657
|
}
|
|
659
658
|
const interval = intervalToMarketFeatureInterval(params.interval);
|
|
659
|
+
if (interval === null) return skippedBackfillResult();
|
|
660
660
|
const intervalMs = MARKET_FEATURE_INTERVAL_MS[interval];
|
|
661
661
|
const { breadthStartMs, tradeFlowStartMs, endMs } = resolveBinanceMarketContextBackfillWindow(params);
|
|
662
662
|
if (endMs <= Math.min(breadthStartMs, tradeFlowStartMs)) {
|
|
@@ -2237,7 +2237,7 @@ var coinMarketCapFetch = async (params) => {
|
|
|
2237
2237
|
};
|
|
2238
2238
|
var fetchCoinMarketCapIndexRows = async (params) => {
|
|
2239
2239
|
const rows = [];
|
|
2240
|
-
const maxChunkDays =
|
|
2240
|
+
const maxChunkDays = 4;
|
|
2241
2241
|
const totalChunks = Math.max(
|
|
2242
2242
|
1,
|
|
2243
2243
|
Math.ceil(
|