@backtest-kit/cli 12.3.0 → 12.5.0
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/README.md +1888 -1854
- package/build/index.cjs +398 -225
- package/build/index.mjs +398 -225
- package/config/notification.config.mjs +13 -13
- package/config/symbol.config.mjs +460 -460
- package/docker/.env.example +2 -2
- package/docker/content/feb_2026/feb_2026.strategy.ts +11 -11
- package/docker/content/feb_2026/modules/backtest.module.ts +83 -83
- package/docker/docker-compose.yaml +46 -46
- package/docker/package.json +39 -38
- package/docker/tsconfig.json +36 -36
- package/package.json +126 -126
- package/template/average-buy.mustache +22 -22
- package/template/breakeven.mustache +21 -21
- package/template/cancel-scheduled.mustache +14 -14
- package/template/cancelled.mustache +21 -21
- package/template/close-pending.mustache +16 -16
- package/template/closed.mustache +23 -23
- package/template/opened.mustache +22 -22
- package/template/partial-loss.mustache +22 -22
- package/template/partial-profit.mustache +22 -22
- package/template/project/config/symbol.config.ts +460 -460
- package/template/project/package.mustache +28 -28
- package/template/risk.mustache +19 -19
- package/template/scheduled.mustache +22 -22
- package/template/signal-close.mustache +22 -22
- package/template/signal-info.mustache +20 -20
- package/template/signal-open.mustache +22 -22
- package/template/source/CLAUDE.md +160 -160
- package/template/trailing-stop.mustache +21 -21
- package/template/trailing-take.mustache +21 -21
package/build/index.cjs
CHANGED
|
@@ -98,11 +98,11 @@ const getEntrySubject = functoolsKit.singleshot(() => {
|
|
|
98
98
|
return new functoolsKit.BehaviorSubject();
|
|
99
99
|
});
|
|
100
100
|
|
|
101
|
-
const main$
|
|
101
|
+
const main$i = () => {
|
|
102
102
|
const entrySubject = getEntrySubject();
|
|
103
103
|
entrySubject.subscribe((path) => console.log("Running", path));
|
|
104
104
|
};
|
|
105
|
-
main$
|
|
105
|
+
main$i();
|
|
106
106
|
|
|
107
107
|
const ERROR_HANDLER_INSTALLED = Symbol.for("error-handler-installed");
|
|
108
108
|
function dumpStackTrace() {
|
|
@@ -663,6 +663,10 @@ const getArgs = functoolsKit.singleshot(() => {
|
|
|
663
663
|
type: "boolean",
|
|
664
664
|
default: false,
|
|
665
665
|
},
|
|
666
|
+
main: {
|
|
667
|
+
type: "boolean",
|
|
668
|
+
default: false,
|
|
669
|
+
},
|
|
666
670
|
cacheInterval: {
|
|
667
671
|
type: "string",
|
|
668
672
|
default: "1m, 15m, 30m, 4h",
|
|
@@ -3358,14 +3362,14 @@ const cli = {
|
|
|
3358
3362
|
};
|
|
3359
3363
|
init();
|
|
3360
3364
|
|
|
3361
|
-
const MODES = ["backtest", "walker", "paper", "live", "pine", "editor", "dump", "pnldebug", "brokerdebug", "flush", "init", "docker", "help", "version"];
|
|
3365
|
+
const MODES = ["backtest", "walker", "paper", "live", "main", "pine", "editor", "dump", "pnldebug", "brokerdebug", "flush", "init", "docker", "help", "version"];
|
|
3362
3366
|
const ENTRY_PATH$1 = "./node_modules/@backtest-kit/cli/build/index.mjs";
|
|
3363
|
-
const HELP_TEXT$1 = `
|
|
3364
|
-
Example:
|
|
3365
|
-
|
|
3366
|
-
node ${ENTRY_PATH$1} --help
|
|
3367
|
+
const HELP_TEXT$1 = `
|
|
3368
|
+
Example:
|
|
3369
|
+
|
|
3370
|
+
node ${ENTRY_PATH$1} --help
|
|
3367
3371
|
`.trimStart();
|
|
3368
|
-
const main$
|
|
3372
|
+
const main$h = async () => {
|
|
3369
3373
|
if (!getEntry((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))) {
|
|
3370
3374
|
return;
|
|
3371
3375
|
}
|
|
@@ -3373,14 +3377,14 @@ const main$g = async () => {
|
|
|
3373
3377
|
if (MODES.some((mode) => values[mode])) {
|
|
3374
3378
|
return;
|
|
3375
3379
|
}
|
|
3376
|
-
process.stdout.write(`@backtest-kit/cli ${"12.
|
|
3380
|
+
process.stdout.write(`@backtest-kit/cli ${"12.5.0"}\n`);
|
|
3377
3381
|
process.stdout.write("\n");
|
|
3378
3382
|
process.stdout.write(`Run with --help to see available commands.\n`);
|
|
3379
3383
|
process.stdout.write("\n");
|
|
3380
3384
|
process.stdout.write(HELP_TEXT$1);
|
|
3381
3385
|
process.exit(0);
|
|
3382
3386
|
};
|
|
3383
|
-
main$
|
|
3387
|
+
main$h();
|
|
3384
3388
|
|
|
3385
3389
|
const notifyShutdown = functoolsKit.singleshot(async () => {
|
|
3386
3390
|
console.log("Graceful shutdown initiated.");
|
|
@@ -3396,7 +3400,7 @@ const flush = async (entryPoint) => {
|
|
|
3396
3400
|
console.log(`Removed: ${target}`);
|
|
3397
3401
|
}
|
|
3398
3402
|
};
|
|
3399
|
-
const main$
|
|
3403
|
+
const main$g = async () => {
|
|
3400
3404
|
if (!getEntry((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))) {
|
|
3401
3405
|
return;
|
|
3402
3406
|
}
|
|
@@ -3413,10 +3417,10 @@ const main$f = async () => {
|
|
|
3413
3417
|
}
|
|
3414
3418
|
process.exit(0);
|
|
3415
3419
|
};
|
|
3416
|
-
main$
|
|
3420
|
+
main$g();
|
|
3417
3421
|
|
|
3418
|
-
const BEFORE_EXIT_FN$
|
|
3419
|
-
process.off("SIGINT", BEFORE_EXIT_FN$
|
|
3422
|
+
const BEFORE_EXIT_FN$6 = functoolsKit.singleshot(async () => {
|
|
3423
|
+
process.off("SIGINT", BEFORE_EXIT_FN$6);
|
|
3420
3424
|
const [running = null] = await BacktestKit.Backtest.list();
|
|
3421
3425
|
if (!running) {
|
|
3422
3426
|
return;
|
|
@@ -3433,10 +3437,10 @@ const BEFORE_EXIT_FN$5 = functoolsKit.singleshot(async () => {
|
|
|
3433
3437
|
frameName,
|
|
3434
3438
|
});
|
|
3435
3439
|
});
|
|
3436
|
-
const listenGracefulShutdown$
|
|
3437
|
-
process.on("SIGINT", BEFORE_EXIT_FN$
|
|
3440
|
+
const listenGracefulShutdown$6 = functoolsKit.singleshot(() => {
|
|
3441
|
+
process.on("SIGINT", BEFORE_EXIT_FN$6);
|
|
3438
3442
|
});
|
|
3439
|
-
const main$
|
|
3443
|
+
const main$f = async () => {
|
|
3440
3444
|
if (!getEntry((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))) {
|
|
3441
3445
|
return;
|
|
3442
3446
|
}
|
|
@@ -3452,12 +3456,12 @@ const main$e = async () => {
|
|
|
3452
3456
|
entryPoint && await flush(entryPoint);
|
|
3453
3457
|
}
|
|
3454
3458
|
await cli.backtestMainService.connect();
|
|
3455
|
-
listenGracefulShutdown$
|
|
3459
|
+
listenGracefulShutdown$6();
|
|
3456
3460
|
};
|
|
3457
|
-
main$
|
|
3461
|
+
main$f();
|
|
3458
3462
|
|
|
3459
|
-
const BEFORE_EXIT_FN$
|
|
3460
|
-
process.off("SIGINT", BEFORE_EXIT_FN$
|
|
3463
|
+
const BEFORE_EXIT_FN$5 = functoolsKit.singleshot(async () => {
|
|
3464
|
+
process.off("SIGINT", BEFORE_EXIT_FN$5);
|
|
3461
3465
|
const [running = null] = await BacktestKit.Walker.list();
|
|
3462
3466
|
if (!running) {
|
|
3463
3467
|
return;
|
|
@@ -3470,10 +3474,10 @@ const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
|
|
|
3470
3474
|
}
|
|
3471
3475
|
BacktestKit.Walker.stop(symbol, { walkerName });
|
|
3472
3476
|
});
|
|
3473
|
-
const listenGracefulShutdown$
|
|
3474
|
-
process.on("SIGINT", BEFORE_EXIT_FN$
|
|
3477
|
+
const listenGracefulShutdown$5 = functoolsKit.singleshot(() => {
|
|
3478
|
+
process.on("SIGINT", BEFORE_EXIT_FN$5);
|
|
3475
3479
|
});
|
|
3476
|
-
const main$
|
|
3480
|
+
const main$e = async () => {
|
|
3477
3481
|
if (!getEntry((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))) {
|
|
3478
3482
|
return;
|
|
3479
3483
|
}
|
|
@@ -3489,13 +3493,13 @@ const main$d = async () => {
|
|
|
3489
3493
|
await flush(entryPoint);
|
|
3490
3494
|
}
|
|
3491
3495
|
}
|
|
3492
|
-
listenGracefulShutdown$
|
|
3496
|
+
listenGracefulShutdown$5();
|
|
3493
3497
|
await cli.walkerMainService.connect();
|
|
3494
3498
|
};
|
|
3495
|
-
main$
|
|
3499
|
+
main$e();
|
|
3496
3500
|
|
|
3497
|
-
const BEFORE_EXIT_FN$
|
|
3498
|
-
process.off("SIGINT", BEFORE_EXIT_FN$
|
|
3501
|
+
const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
|
|
3502
|
+
process.off("SIGINT", BEFORE_EXIT_FN$4);
|
|
3499
3503
|
const [running = null] = await BacktestKit.Live.list();
|
|
3500
3504
|
if (!running) {
|
|
3501
3505
|
return;
|
|
@@ -3511,10 +3515,10 @@ const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
|
|
|
3511
3515
|
strategyName,
|
|
3512
3516
|
});
|
|
3513
3517
|
});
|
|
3514
|
-
const listenGracefulShutdown$
|
|
3515
|
-
process.on("SIGINT", BEFORE_EXIT_FN$
|
|
3518
|
+
const listenGracefulShutdown$4 = functoolsKit.singleshot(() => {
|
|
3519
|
+
process.on("SIGINT", BEFORE_EXIT_FN$4);
|
|
3516
3520
|
});
|
|
3517
|
-
const main$
|
|
3521
|
+
const main$d = async () => {
|
|
3518
3522
|
if (!getEntry((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))) {
|
|
3519
3523
|
return;
|
|
3520
3524
|
}
|
|
@@ -3526,12 +3530,12 @@ const main$c = async () => {
|
|
|
3526
3530
|
return;
|
|
3527
3531
|
}
|
|
3528
3532
|
cli.paperMainService.connect();
|
|
3529
|
-
listenGracefulShutdown$
|
|
3533
|
+
listenGracefulShutdown$4();
|
|
3530
3534
|
};
|
|
3531
|
-
main$
|
|
3535
|
+
main$d();
|
|
3532
3536
|
|
|
3533
|
-
const BEFORE_EXIT_FN$
|
|
3534
|
-
process.off("SIGINT", BEFORE_EXIT_FN$
|
|
3537
|
+
const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
|
|
3538
|
+
process.off("SIGINT", BEFORE_EXIT_FN$3);
|
|
3535
3539
|
const [running = null] = await BacktestKit.Live.list();
|
|
3536
3540
|
if (!running) {
|
|
3537
3541
|
return;
|
|
@@ -3547,6 +3551,84 @@ const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
|
|
|
3547
3551
|
strategyName,
|
|
3548
3552
|
});
|
|
3549
3553
|
});
|
|
3554
|
+
const listenGracefulShutdown$3 = functoolsKit.singleshot(() => {
|
|
3555
|
+
process.on("SIGINT", BEFORE_EXIT_FN$3);
|
|
3556
|
+
});
|
|
3557
|
+
const main$c = async () => {
|
|
3558
|
+
if (!getEntry((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))) {
|
|
3559
|
+
return;
|
|
3560
|
+
}
|
|
3561
|
+
const { values } = getArgs();
|
|
3562
|
+
if (!values.live) {
|
|
3563
|
+
return;
|
|
3564
|
+
}
|
|
3565
|
+
if (values.entry) {
|
|
3566
|
+
return;
|
|
3567
|
+
}
|
|
3568
|
+
await cli.liveMainService.connect();
|
|
3569
|
+
listenGracefulShutdown$3();
|
|
3570
|
+
};
|
|
3571
|
+
main$c();
|
|
3572
|
+
|
|
3573
|
+
const stopBacktestList$1 = async () => {
|
|
3574
|
+
for (const item of await BacktestKit.Backtest.list()) {
|
|
3575
|
+
if (item.status === "fulfilled") {
|
|
3576
|
+
continue;
|
|
3577
|
+
}
|
|
3578
|
+
BacktestKit.Backtest.stop(item.symbol, {
|
|
3579
|
+
exchangeName: item.exchangeName,
|
|
3580
|
+
strategyName: item.strategyName,
|
|
3581
|
+
frameName: item.frameName,
|
|
3582
|
+
});
|
|
3583
|
+
}
|
|
3584
|
+
};
|
|
3585
|
+
const stopLiveList$1 = async () => {
|
|
3586
|
+
for (const item of await BacktestKit.Live.list()) {
|
|
3587
|
+
if (item.status === "fulfilled") {
|
|
3588
|
+
continue;
|
|
3589
|
+
}
|
|
3590
|
+
BacktestKit.Live.stop(item.symbol, {
|
|
3591
|
+
exchangeName: item.exchangeName,
|
|
3592
|
+
strategyName: item.strategyName,
|
|
3593
|
+
});
|
|
3594
|
+
}
|
|
3595
|
+
};
|
|
3596
|
+
const stopWalkerList$1 = async () => {
|
|
3597
|
+
for (const item of await BacktestKit.Walker.list()) {
|
|
3598
|
+
if (item.status === "fulfilled") {
|
|
3599
|
+
continue;
|
|
3600
|
+
}
|
|
3601
|
+
BacktestKit.Walker.stop(item.symbol, { walkerName: item.walkerName });
|
|
3602
|
+
}
|
|
3603
|
+
};
|
|
3604
|
+
const stopMain$1 = async () => {
|
|
3605
|
+
await stopBacktestList$1();
|
|
3606
|
+
await stopLiveList$1();
|
|
3607
|
+
await stopWalkerList$1();
|
|
3608
|
+
};
|
|
3609
|
+
const listenFinish$1 = functoolsKit.singleshot(() => {
|
|
3610
|
+
let disposeRef;
|
|
3611
|
+
const unBacktest = BacktestKit.listenDoneBacktest(() => {
|
|
3612
|
+
console.log("Backtest trading finished");
|
|
3613
|
+
disposeRef && disposeRef();
|
|
3614
|
+
});
|
|
3615
|
+
const unLive = BacktestKit.listenDoneLive(() => {
|
|
3616
|
+
console.log("Live trading finished");
|
|
3617
|
+
disposeRef && disposeRef();
|
|
3618
|
+
});
|
|
3619
|
+
const unWalker = BacktestKit.listenDoneWalker(() => {
|
|
3620
|
+
console.log("Walker comparison finished");
|
|
3621
|
+
disposeRef && disposeRef();
|
|
3622
|
+
});
|
|
3623
|
+
disposeRef = functoolsKit.compose(() => unBacktest(), () => unLive(), () => unWalker());
|
|
3624
|
+
BacktestKit.shutdown();
|
|
3625
|
+
});
|
|
3626
|
+
const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
|
|
3627
|
+
process.off("SIGINT", BEFORE_EXIT_FN$2);
|
|
3628
|
+
notifyShutdown();
|
|
3629
|
+
notifyKill();
|
|
3630
|
+
await stopMain$1();
|
|
3631
|
+
});
|
|
3550
3632
|
const listenGracefulShutdown$2 = functoolsKit.singleshot(() => {
|
|
3551
3633
|
process.on("SIGINT", BEFORE_EXIT_FN$2);
|
|
3552
3634
|
});
|
|
@@ -3555,14 +3637,56 @@ const main$b = async () => {
|
|
|
3555
3637
|
return;
|
|
3556
3638
|
}
|
|
3557
3639
|
const { values } = getArgs();
|
|
3558
|
-
if (!values.
|
|
3640
|
+
if (!values.main) {
|
|
3559
3641
|
return;
|
|
3560
3642
|
}
|
|
3561
3643
|
if (values.entry) {
|
|
3562
3644
|
return;
|
|
3563
3645
|
}
|
|
3564
|
-
|
|
3646
|
+
const [entryPoint = null] = getPositionals();
|
|
3647
|
+
if (!entryPoint) {
|
|
3648
|
+
throw new Error("Entry point is required");
|
|
3649
|
+
}
|
|
3650
|
+
{
|
|
3651
|
+
const cwd = process.cwd();
|
|
3652
|
+
dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
|
|
3653
|
+
}
|
|
3654
|
+
await cli.configConnectionService.loadConfig("setup.config");
|
|
3655
|
+
{
|
|
3656
|
+
const loader = await cli.configConnectionService.loadConfig("loader.config");
|
|
3657
|
+
try {
|
|
3658
|
+
if (typeof loader === "function") {
|
|
3659
|
+
await loader();
|
|
3660
|
+
}
|
|
3661
|
+
if (typeof loader?.loader === "function") {
|
|
3662
|
+
await loader.loader();
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
catch (error) {
|
|
3666
|
+
console.error("Module loader failed", error);
|
|
3667
|
+
kill(-1);
|
|
3668
|
+
return;
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
{
|
|
3672
|
+
await cli.configService.waitForInit();
|
|
3673
|
+
Setup.enable();
|
|
3674
|
+
}
|
|
3675
|
+
const cwd = process.cwd();
|
|
3676
|
+
{
|
|
3677
|
+
const absolutePath = path.resolve(entryPoint);
|
|
3678
|
+
const moduleRoot = path.dirname(absolutePath);
|
|
3679
|
+
process.chdir(moduleRoot);
|
|
3680
|
+
cwd !== moduleRoot && Setup.update();
|
|
3681
|
+
dotenv.config({ path: path.join(moduleRoot, '.env'), override: true, quiet: true });
|
|
3682
|
+
}
|
|
3683
|
+
if (!values.noFlush) {
|
|
3684
|
+
await flush(path.resolve(cwd, entryPoint));
|
|
3685
|
+
}
|
|
3686
|
+
await cli.moduleConnectionService.loadModule("main.module");
|
|
3687
|
+
listenFinish$1();
|
|
3565
3688
|
listenGracefulShutdown$2();
|
|
3689
|
+
await cli.resolveService.attachEntry(path.resolve(cwd, entryPoint));
|
|
3566
3690
|
};
|
|
3567
3691
|
main$b();
|
|
3568
3692
|
|
|
@@ -3571,13 +3695,29 @@ const MODE_MODULE = {
|
|
|
3571
3695
|
live: "live.module",
|
|
3572
3696
|
paper: "paper.module",
|
|
3573
3697
|
walker: "walker.module",
|
|
3698
|
+
main: "main.module",
|
|
3574
3699
|
};
|
|
3700
|
+
const MODE_LIST = ["backtest", "live", "paper", "walker", "main"];
|
|
3575
3701
|
const resolveMode = (values) => {
|
|
3576
|
-
|
|
3577
|
-
if (enabled.length !== 1) {
|
|
3702
|
+
if (MODE_LIST.filter((mode) => values[mode]).length > 1) {
|
|
3578
3703
|
return null;
|
|
3579
3704
|
}
|
|
3580
|
-
|
|
3705
|
+
if (values.main) {
|
|
3706
|
+
return "main";
|
|
3707
|
+
}
|
|
3708
|
+
if (values.backtest) {
|
|
3709
|
+
return "backtest";
|
|
3710
|
+
}
|
|
3711
|
+
if (values.live) {
|
|
3712
|
+
return "live";
|
|
3713
|
+
}
|
|
3714
|
+
if (values.paper) {
|
|
3715
|
+
return "paper";
|
|
3716
|
+
}
|
|
3717
|
+
if (values.walker) {
|
|
3718
|
+
return "walker";
|
|
3719
|
+
}
|
|
3720
|
+
return null;
|
|
3581
3721
|
};
|
|
3582
3722
|
const stopBacktestList = async () => {
|
|
3583
3723
|
for (const item of await BacktestKit.Backtest.list()) {
|
|
@@ -3610,11 +3750,17 @@ const stopWalkerList = async () => {
|
|
|
3610
3750
|
BacktestKit.Walker.stop(item.symbol, { walkerName: item.walkerName });
|
|
3611
3751
|
}
|
|
3612
3752
|
};
|
|
3753
|
+
const stopMain = async () => {
|
|
3754
|
+
await stopBacktestList();
|
|
3755
|
+
await stopLiveList();
|
|
3756
|
+
await stopWalkerList();
|
|
3757
|
+
};
|
|
3613
3758
|
const MODE_STOP = {
|
|
3614
3759
|
backtest: stopBacktestList,
|
|
3615
3760
|
live: stopLiveList,
|
|
3616
3761
|
paper: stopLiveList,
|
|
3617
3762
|
walker: stopWalkerList,
|
|
3763
|
+
main: stopMain,
|
|
3618
3764
|
};
|
|
3619
3765
|
const listenFinish = functoolsKit.singleshot(() => {
|
|
3620
3766
|
let disposeRef;
|
|
@@ -3655,12 +3801,12 @@ const main$a = async () => {
|
|
|
3655
3801
|
}
|
|
3656
3802
|
const mode = resolveMode(values);
|
|
3657
3803
|
if (!mode) {
|
|
3658
|
-
console.error("--entry requires exactly one of --backtest, --live, --paper, --walker");
|
|
3804
|
+
console.error("--entry requires exactly one of --backtest, --live, --paper, --walker, --main");
|
|
3659
3805
|
kill(1);
|
|
3660
3806
|
return;
|
|
3661
3807
|
}
|
|
3662
3808
|
const entryPoints = getPositionals();
|
|
3663
|
-
if (!entryPoints.length) {
|
|
3809
|
+
if (mode !== "main" && !entryPoints.length) {
|
|
3664
3810
|
throw new Error("At least one entry point is required");
|
|
3665
3811
|
}
|
|
3666
3812
|
{
|
|
@@ -3707,12 +3853,18 @@ const main$a = async () => {
|
|
|
3707
3853
|
}
|
|
3708
3854
|
await cli.moduleConnectionService.loadModule(MODE_MODULE[mode]);
|
|
3709
3855
|
listenFinish();
|
|
3710
|
-
|
|
3856
|
+
{
|
|
3857
|
+
const listenShutdown = createGracefulShutdown(mode);
|
|
3858
|
+
listenShutdown();
|
|
3859
|
+
}
|
|
3711
3860
|
let absolutePath;
|
|
3712
3861
|
for (const entryPoint of entryPoints) {
|
|
3713
3862
|
absolutePath = path.resolve(cwd, entryPoint);
|
|
3714
3863
|
await cli.resolveService.attachEntry(absolutePath);
|
|
3715
3864
|
}
|
|
3865
|
+
if (mode === "main") {
|
|
3866
|
+
return;
|
|
3867
|
+
}
|
|
3716
3868
|
await getEntrySubject().next(absolutePath);
|
|
3717
3869
|
};
|
|
3718
3870
|
main$a();
|
|
@@ -4324,183 +4476,204 @@ const main$2 = async () => {
|
|
|
4324
4476
|
main$2();
|
|
4325
4477
|
|
|
4326
4478
|
const ENTRY_PATH = "./node_modules/@backtest-kit/cli/build/index.mjs";
|
|
4327
|
-
const HELP_TEXT = `
|
|
4328
|
-
Usage:
|
|
4329
|
-
node index.mjs --<mode> [flags] [entry-point]
|
|
4330
|
-
|
|
4331
|
-
Modes:
|
|
4332
|
-
|
|
4333
|
-
--backtest <entry> Run strategy against historical candle data
|
|
4334
|
-
--walker <entry...> Run Walker A/B strategy comparison across multiple strategies
|
|
4335
|
-
--paper <entry> Paper trading (live prices, no real orders)
|
|
4336
|
-
--live <entry> Live trading with real orders
|
|
4337
|
-
--
|
|
4338
|
-
--
|
|
4339
|
-
--
|
|
4340
|
-
--
|
|
4341
|
-
--
|
|
4342
|
-
--
|
|
4343
|
-
--
|
|
4344
|
-
--
|
|
4345
|
-
--
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
--
|
|
4351
|
-
--
|
|
4352
|
-
--
|
|
4353
|
-
--
|
|
4354
|
-
--
|
|
4355
|
-
--
|
|
4356
|
-
--
|
|
4357
|
-
--
|
|
4358
|
-
--
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
--
|
|
4364
|
-
--
|
|
4365
|
-
--
|
|
4366
|
-
--
|
|
4367
|
-
--
|
|
4368
|
-
--
|
|
4369
|
-
--
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
--
|
|
4382
|
-
--
|
|
4383
|
-
--
|
|
4384
|
-
--
|
|
4385
|
-
--
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
--
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
--
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
--
|
|
4409
|
-
--
|
|
4410
|
-
--
|
|
4411
|
-
--
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
--
|
|
4424
|
-
--
|
|
4425
|
-
--
|
|
4426
|
-
--
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4479
|
+
const HELP_TEXT = `
|
|
4480
|
+
Usage:
|
|
4481
|
+
node index.mjs --<mode> [flags] [entry-point]
|
|
4482
|
+
|
|
4483
|
+
Modes:
|
|
4484
|
+
|
|
4485
|
+
--backtest <entry> Run strategy against historical candle data
|
|
4486
|
+
--walker <entry...> Run Walker A/B strategy comparison across multiple strategies
|
|
4487
|
+
--paper <entry> Paper trading (live prices, no real orders)
|
|
4488
|
+
--live <entry> Live trading with real orders
|
|
4489
|
+
--main <entry> Run an entry point with prepared environment, no trading harness
|
|
4490
|
+
--pine <entry> Execute a local .pine indicator file
|
|
4491
|
+
--editor Open the Pine Script visual editor in the browser
|
|
4492
|
+
--dump Fetch and save raw OHLCV candles
|
|
4493
|
+
--pnldebug Simulate PnL per minute for a given entry price and direction
|
|
4494
|
+
--brokerdebug Fire a single broker commit against the live broker adapter
|
|
4495
|
+
--flush <entry...> Delete report/log/markdown/agent folders from strategy dump dir
|
|
4496
|
+
--init Scaffold a new project in the current directory
|
|
4497
|
+
--docker Scaffold a Docker workspace for running strategies in a container
|
|
4498
|
+
--help Print this help message
|
|
4499
|
+
|
|
4500
|
+
Backtest flags:
|
|
4501
|
+
|
|
4502
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4503
|
+
--strategy <string> Strategy name from addStrategySchema (default: first registered)
|
|
4504
|
+
--exchange <string> Exchange name from addExchangeSchema (default: first registered)
|
|
4505
|
+
--frame <string> Frame name from addFrameSchema (default: first registered)
|
|
4506
|
+
--cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
|
|
4507
|
+
--noCache Skip candle cache warming before the run
|
|
4508
|
+
--noFlush Skip removing report/log/markdown/agent folders before backtest run
|
|
4509
|
+
--verbose Log every candle fetch to stdout
|
|
4510
|
+
--ui Start web dashboard at http://localhost:60050
|
|
4511
|
+
--telegram Send trade notifications to Telegram
|
|
4512
|
+
|
|
4513
|
+
Walker flags (--walker):
|
|
4514
|
+
|
|
4515
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4516
|
+
--cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
|
|
4517
|
+
--noCache Skip candle cache warming before the run
|
|
4518
|
+
--noFlush Skip removing report/log/markdown/agent folders before walker run
|
|
4519
|
+
--verbose Log every candle fetch to stdout
|
|
4520
|
+
--output <string> Output file base name (default: walker_{SYMBOL}_{TIMESTAMP})
|
|
4521
|
+
--json Save results as JSON to ./dump/<output>.json
|
|
4522
|
+
--markdown Save report as Markdown to ./dump/<output>.md
|
|
4523
|
+
|
|
4524
|
+
Each positional argument is a strategy entry point. All strategy files are loaded without
|
|
4525
|
+
changing process.cwd() — .env is read from the working directory only.
|
|
4526
|
+
addWalkerSchema is called automatically using the registered exchange and frame.
|
|
4527
|
+
After comparison completes the report is printed to stdout (or saved if --json/--markdown).
|
|
4528
|
+
|
|
4529
|
+
Module file ./modules/walker.module is loaded automatically if it exists.
|
|
4530
|
+
|
|
4531
|
+
Paper / Live flags:
|
|
4532
|
+
|
|
4533
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4534
|
+
--strategy <string> Strategy name (default: first registered)
|
|
4535
|
+
--exchange <string> Exchange name (default: first registered)
|
|
4536
|
+
--verbose Log every candle fetch to stdout
|
|
4537
|
+
--ui Start web dashboard
|
|
4538
|
+
--telegram Send Telegram notifications
|
|
4539
|
+
|
|
4540
|
+
Main flags (--main):
|
|
4541
|
+
|
|
4542
|
+
--noFlush Skip removing report/log/markdown/agent folders before the run
|
|
4543
|
+
|
|
4544
|
+
Prepares the runtime environment (loads .env, setup.config, loader.config and
|
|
4545
|
+
modules/main.module) and runs the single positional entry point — but does NOT
|
|
4546
|
+
start any trading harness. Unlike --backtest/--live/--walker, the CLI never calls
|
|
4547
|
+
Backtest/Live/Walker.background; the entry point decides what to run.
|
|
4548
|
+
|
|
4549
|
+
Exactly one positional entry point is required. process.cwd() is changed to the
|
|
4550
|
+
entry point directory and its local .env is loaded.
|
|
4551
|
+
|
|
4552
|
+
Backtest, Live and Walker runs started from userspace are still tracked: the run
|
|
4553
|
+
finishes automatically when one of them completes, Ctrl+C stops any active run,
|
|
4554
|
+
and a second Ctrl+C force-quits.
|
|
4555
|
+
|
|
4556
|
+
Module file ./modules/main.module is loaded automatically if it exists.
|
|
4557
|
+
|
|
4558
|
+
PineScript flags (--pine):
|
|
4559
|
+
|
|
4560
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4561
|
+
--timeframe <string> Candle interval (default: 15m)
|
|
4562
|
+
--limit <string> Number of candles to fetch (default: 250)
|
|
4563
|
+
--when <string> End date — ISO 8601 or Unix ms (default: now)
|
|
4564
|
+
--exchange <string> Exchange name (default: first registered)
|
|
4565
|
+
--output <string> Output file base name without extension
|
|
4566
|
+
--json Save output as JSON array to <pine-dir>/dump/<output>.json
|
|
4567
|
+
--jsonl Save output as JSONL to <pine-dir>/dump/<output>.jsonl
|
|
4568
|
+
--markdown Save output as Markdown table to <pine-dir>/dump/<output>.md
|
|
4569
|
+
|
|
4570
|
+
Only plot() calls with display=display.data_window produce output columns.
|
|
4571
|
+
Module file ./modules/pine.module is loaded automatically if it exists.
|
|
4572
|
+
|
|
4573
|
+
Candle dump flags (--dump):
|
|
4574
|
+
|
|
4575
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4576
|
+
--timeframe <string> Candle interval (default: 15m)
|
|
4577
|
+
--limit <string> Number of candles (default: 250)
|
|
4578
|
+
--when <string> End date — ISO 8601 or Unix ms (default: now)
|
|
4579
|
+
--exchange <string> Exchange name (default: first registered)
|
|
4580
|
+
--output <string> Output file base name (default: {SYMBOL}_{LIMIT}_{TIMEFRAME}_{TIMESTAMP})
|
|
4581
|
+
--json Save as JSON array to ./dump/<output>.json
|
|
4582
|
+
--jsonl Save as JSONL to ./dump/<output>.jsonl
|
|
4583
|
+
|
|
4584
|
+
Module file ./modules/dump.module is loaded automatically if it exists.
|
|
4585
|
+
|
|
4586
|
+
PnL debug flags (--pnldebug):
|
|
4587
|
+
|
|
4588
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4589
|
+
--priceopen <number> Entry price (required)
|
|
4590
|
+
--direction <string> Position direction: long or short (default: long)
|
|
4591
|
+
--when <string> Start timestamp — ISO 8601 or Unix ms (default: now)
|
|
4592
|
+
--minutes <string> Number of 1m candles to simulate (default: 60)
|
|
4593
|
+
--exchange <string> Exchange name (default: first registered)
|
|
4594
|
+
--output <string> Output file base name (default: {SYMBOL}_{DIRECTION}_{PRICEOPEN}_{TIMESTAMP})
|
|
4595
|
+
--json Save as JSON array to ./dump/<output>.json
|
|
4596
|
+
--jsonl Save as JSONL to ./dump/<output>.jsonl
|
|
4597
|
+
--markdown Save as Markdown table to ./dump/<output>.md
|
|
4598
|
+
|
|
4599
|
+
Module file ./modules/pnldebug.module is loaded automatically if it exists.
|
|
4600
|
+
|
|
4601
|
+
Broker debug flags (--brokerdebug):
|
|
4602
|
+
|
|
4603
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4604
|
+
--exchange <string> Exchange name (default: first registered)
|
|
4605
|
+
--commit <string> Commit type to fire: signal-open, signal-close, partial-profit,
|
|
4606
|
+
partial-loss, average-buy, trailing-stop, trailing-take, breakeven
|
|
4607
|
+
(default: signal-open)
|
|
4608
|
+
|
|
4609
|
+
Loads ./live.module, fetches the last candle for --symbol/--timeframe, and calls
|
|
4610
|
+
the selected broker commit with synthetic payload values derived from current price.
|
|
4611
|
+
|
|
4612
|
+
Flush flags (--flush):
|
|
4613
|
+
|
|
4614
|
+
One or more positional entry points. For each entry point the following
|
|
4615
|
+
subdirectories are removed from <entry-dir>/dump/:
|
|
4616
|
+
|
|
4617
|
+
report log markdown agent
|
|
4618
|
+
|
|
4619
|
+
Init flags (--init):
|
|
4620
|
+
|
|
4621
|
+
--output <string> Target directory name (default: backtest-kit-project)
|
|
4622
|
+
|
|
4623
|
+
Scaffolds a project and runs scripts/fetch_docs.mjs to download library docs.
|
|
4624
|
+
|
|
4625
|
+
Docker flags (--docker):
|
|
4626
|
+
|
|
4627
|
+
--output <string> Target directory name (default: backtest-kit-docker)
|
|
4628
|
+
|
|
4629
|
+
Scaffolds a Docker workspace: docker-compose.yaml, .env.example, package.json,
|
|
4630
|
+
tsconfig.json, and a sample strategy under content/. Run npm install then
|
|
4631
|
+
docker compose up to start the container.
|
|
4632
|
+
|
|
4633
|
+
Module hooks (loaded automatically by each mode):
|
|
4634
|
+
|
|
4635
|
+
modules/backtest.module --backtest Broker adapter for backtest
|
|
4636
|
+
modules/walker.module --walker Broker adapter for walker comparison
|
|
4637
|
+
modules/paper.module --paper Broker adapter for paper trading
|
|
4638
|
+
modules/live.module --live Broker adapter for live trading
|
|
4639
|
+
modules/main.module --main Environment setup for a custom entry point
|
|
4640
|
+
modules/pine.module --pine Exchange schema for PineScript runs
|
|
4641
|
+
modules/editor.module --editor Exchange schema for the visual Pine editor
|
|
4642
|
+
modules/dump.module --dump Exchange schema for candle dumps
|
|
4643
|
+
modules/pnldebug.module --pnldebug Exchange schema for PnL debug runs
|
|
4644
|
+
modules/brokerdebug.module --brokerdebug Broker adapter used for broker commit testing
|
|
4645
|
+
|
|
4646
|
+
--flush has no associated module. It only removes dump subdirectories.
|
|
4647
|
+
|
|
4648
|
+
Extensions .ts, .mjs, .cjs are tried automatically. Missing module = soft warning.
|
|
4649
|
+
|
|
4650
|
+
Environment variables:
|
|
4651
|
+
|
|
4652
|
+
CC_TELEGRAM_TOKEN Telegram bot token (required for --telegram)
|
|
4653
|
+
CC_TELEGRAM_CHANNEL Telegram channel or chat ID (required for --telegram)
|
|
4654
|
+
CC_WWWROOT_HOST UI server bind address (default: 0.0.0.0)
|
|
4655
|
+
CC_WWWROOT_PORT UI server port (default: 60050)
|
|
4656
|
+
|
|
4657
|
+
Examples:
|
|
4658
|
+
|
|
4659
|
+
node ${ENTRY_PATH} --backtest ./content/feb_2026.strategy.ts
|
|
4660
|
+
node ${ENTRY_PATH} --backtest --symbol BTCUSDT --noCache --noFlush --ui ./content/feb_2026.strategy.ts
|
|
4661
|
+
node ${ENTRY_PATH} --walker ./content/feb_2026_v1.strategy.ts ./content/feb_2026_v2.strategy.ts ./content/feb_2026_v3.strategy.ts
|
|
4662
|
+
node ${ENTRY_PATH} --walker --symbol BTCUSDT --noCache --noFlush --markdown ./content/feb_2026_v1.ts ./content/feb_2026_v2.ts
|
|
4663
|
+
node ${ENTRY_PATH} --paper --symbol ETHUSDT ./content/feb_2026.strategy.ts
|
|
4664
|
+
node ${ENTRY_PATH} --live --ui --telegram ./content/feb_2026.strategy.ts
|
|
4665
|
+
node ${ENTRY_PATH} --main ./tools/fetch_fear_and_greed.ts
|
|
4666
|
+
node ${ENTRY_PATH} --pine ./math/feb_2026.pine --timeframe 15m --limit 500 --jsonl
|
|
4667
|
+
node ${ENTRY_PATH} --editor
|
|
4668
|
+
node ${ENTRY_PATH} --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl
|
|
4669
|
+
node ${ENTRY_PATH} --pnldebug --symbol BTCUSDT --priceopen 64069.50 --direction short --when "2025-02-25" --minutes 120
|
|
4670
|
+
node ${ENTRY_PATH} --pnldebug --priceopen 67956.73 --direction long --when 1772064000000 --minutes 60 --markdown
|
|
4671
|
+
node ${ENTRY_PATH} --brokerdebug --commit signal-open --symbol BTCUSDT
|
|
4672
|
+
node ${ENTRY_PATH} --brokerdebug --commit partial-profit --symbol ETHUSDT
|
|
4673
|
+
node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts
|
|
4674
|
+
node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts ./content/feb_2026.strategy/feb_2026.test.ts
|
|
4675
|
+
node ${ENTRY_PATH} --init --output my-trading-bot
|
|
4676
|
+
node ${ENTRY_PATH} --docker --output my-docker-workspace
|
|
4504
4677
|
`.trimStart();
|
|
4505
4678
|
const main$1 = async () => {
|
|
4506
4679
|
if (!getEntry((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))) {
|
|
@@ -4510,7 +4683,7 @@ const main$1 = async () => {
|
|
|
4510
4683
|
if (!values.help) {
|
|
4511
4684
|
return;
|
|
4512
4685
|
}
|
|
4513
|
-
process.stdout.write(`@backtest-kit/cli ${"12.
|
|
4686
|
+
process.stdout.write(`@backtest-kit/cli ${"12.5.0"}\n\n`);
|
|
4514
4687
|
process.stdout.write(HELP_TEXT);
|
|
4515
4688
|
process.exit(0);
|
|
4516
4689
|
};
|
|
@@ -4524,7 +4697,7 @@ const main = async () => {
|
|
|
4524
4697
|
if (!values.version) {
|
|
4525
4698
|
return;
|
|
4526
4699
|
}
|
|
4527
|
-
process.stdout.write(`@backtest-kit/cli ${"12.
|
|
4700
|
+
process.stdout.write(`@backtest-kit/cli ${"12.5.0"}\n`);
|
|
4528
4701
|
process.exit(0);
|
|
4529
4702
|
};
|
|
4530
4703
|
main();
|
|
@@ -4546,17 +4719,17 @@ async function run(mode, args) {
|
|
|
4546
4719
|
}
|
|
4547
4720
|
if (mode === "backtest") {
|
|
4548
4721
|
await cli.backtestMainService.run(args);
|
|
4549
|
-
listenGracefulShutdown$
|
|
4722
|
+
listenGracefulShutdown$6();
|
|
4550
4723
|
return;
|
|
4551
4724
|
}
|
|
4552
4725
|
if (mode === "paper") {
|
|
4553
4726
|
await cli.paperMainService.run(args);
|
|
4554
|
-
listenGracefulShutdown$
|
|
4727
|
+
listenGracefulShutdown$4();
|
|
4555
4728
|
return;
|
|
4556
4729
|
}
|
|
4557
4730
|
if (mode === "live") {
|
|
4558
4731
|
await cli.liveMainService.run(args);
|
|
4559
|
-
listenGracefulShutdown$
|
|
4732
|
+
listenGracefulShutdown$3();
|
|
4560
4733
|
return;
|
|
4561
4734
|
}
|
|
4562
4735
|
throw new Error(`Invalid mode: ${mode}`);
|