@backtest-kit/cli 9.5.0 → 9.7.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.
Files changed (34) hide show
  1. package/README.md +1854 -1738
  2. package/build/index.cjs +302 -207
  3. package/build/index.mjs +302 -207
  4. package/config/notification.config.mjs +13 -13
  5. package/config/symbol.config.mjs +460 -460
  6. package/docker/.env.example +2 -2
  7. package/docker/README.md +1 -0
  8. package/docker/content/feb_2026/feb_2026.strategy.ts +11 -11
  9. package/docker/content/feb_2026/modules/backtest.module.ts +83 -83
  10. package/docker/docker-compose.yaml +46 -46
  11. package/docker/package.json +38 -38
  12. package/docker/tsconfig.json +36 -36
  13. package/package.json +126 -126
  14. package/template/average-buy.mustache +22 -22
  15. package/template/breakeven.mustache +21 -21
  16. package/template/cancel-scheduled.mustache +14 -14
  17. package/template/cancelled.mustache +21 -21
  18. package/template/close-pending.mustache +16 -16
  19. package/template/closed.mustache +23 -23
  20. package/template/opened.mustache +22 -22
  21. package/template/partial-loss.mustache +22 -22
  22. package/template/partial-profit.mustache +22 -22
  23. package/template/project/README.md +1 -0
  24. package/template/project/config/symbol.config.ts +460 -460
  25. package/template/project/package.mustache +28 -28
  26. package/template/risk.mustache +19 -19
  27. package/template/scheduled.mustache +22 -22
  28. package/template/signal-close.mustache +22 -22
  29. package/template/signal-info.mustache +20 -20
  30. package/template/signal-open.mustache +22 -22
  31. package/template/source/CLAUDE.md +160 -160
  32. package/template/trailing-stop.mustache +21 -21
  33. package/template/trailing-take.mustache +21 -21
  34. package/types.d.ts +1 -6
package/build/index.mjs CHANGED
@@ -885,9 +885,15 @@ class BacktestMainService {
885
885
  this.loggerService.log("backtestMainService run", {
886
886
  payload,
887
887
  });
888
+ await this.configConnectionService.loadConfig("setup.config");
888
889
  {
889
- await this.configConnectionService.loadConfig("setup.config");
890
- await this.configConnectionService.loadConfig("loader.config");
890
+ const loader = await this.configConnectionService.loadConfig("loader.config");
891
+ if (typeof loader === "function") {
892
+ await loader();
893
+ }
894
+ if (typeof loader?.loader === "function") {
895
+ await loader.loader();
896
+ }
891
897
  }
892
898
  {
893
899
  await this.configService.waitForInit();
@@ -1007,9 +1013,15 @@ class WalkerMainService {
1007
1013
  this.configConnectionService = inject(TYPES.configConnectionService);
1008
1014
  this.run = singleshot(async (payload) => {
1009
1015
  this.loggerService.log("walkerMainService run", { payload });
1016
+ await this.configConnectionService.loadConfig("setup.config");
1010
1017
  {
1011
- await this.configConnectionService.loadConfig("setup.config");
1012
- await this.configConnectionService.loadConfig("loader.config");
1018
+ const loader = await this.configConnectionService.loadConfig("loader.config");
1019
+ if (typeof loader === "function") {
1020
+ await loader();
1021
+ }
1022
+ if (typeof loader?.loader === "function") {
1023
+ await loader.loader();
1024
+ }
1013
1025
  }
1014
1026
  {
1015
1027
  await this.configService.waitForInit();
@@ -1235,9 +1247,15 @@ class LiveMainService {
1235
1247
  this.loggerService.log("liveMainService run", {
1236
1248
  payload,
1237
1249
  });
1250
+ await this.configConnectionService.loadConfig("setup.config");
1238
1251
  {
1239
- await this.configConnectionService.loadConfig("setup.config");
1240
- await this.configConnectionService.loadConfig("loader.config");
1252
+ const loader = await this.configConnectionService.loadConfig("loader.config");
1253
+ if (typeof loader === "function") {
1254
+ await loader();
1255
+ }
1256
+ if (typeof loader?.loader === "function") {
1257
+ await loader.loader();
1258
+ }
1241
1259
  }
1242
1260
  {
1243
1261
  await this.configService.waitForInit();
@@ -1324,9 +1342,15 @@ class PaperMainService {
1324
1342
  this.configConnectionService = inject(TYPES.configConnectionService);
1325
1343
  this.run = singleshot(async (payload) => {
1326
1344
  this.loggerService.log("paperMainService init");
1345
+ await this.configConnectionService.loadConfig("setup.config");
1327
1346
  {
1328
- await this.configConnectionService.loadConfig("setup.config");
1329
- await this.configConnectionService.loadConfig("loader.config");
1347
+ const loader = await this.configConnectionService.loadConfig("loader.config");
1348
+ if (typeof loader === "function") {
1349
+ await loader();
1350
+ }
1351
+ if (typeof loader?.loader === "function") {
1352
+ await loader.loader();
1353
+ }
1330
1354
  }
1331
1355
  {
1332
1356
  await this.configService.waitForInit();
@@ -2850,13 +2874,50 @@ overrideModule('@backtest-kit/ollama', BacktestKitOllama);
2850
2874
  overrideModule('@backtest-kit/pinets', BacktestKitPinets);
2851
2875
  overrideModule('@backtest-kit/signals', BacktestKitSignals);
2852
2876
 
2877
+ const GET_ALIAS_VARIANTS_FN = (self) => {
2878
+ const result = [];
2879
+ result.push({
2880
+ filePath: path.join(self.resolveService.OVERRIDE_CONFIG_DIR, "alias.config.cjs"),
2881
+ baseDir: self.resolveService.OVERRIDE_CONFIG_DIR,
2882
+ });
2883
+ result.push({
2884
+ filePath: path.join(self.resolveService.OVERRIDE_CONFIG_DIR, "alias.config.mjs"),
2885
+ baseDir: self.resolveService.OVERRIDE_CONFIG_DIR,
2886
+ });
2887
+ result.push({
2888
+ filePath: path.join(self.resolveService.OVERRIDE_CONFIG_DIR, "alias.config.ts"),
2889
+ baseDir: self.resolveService.OVERRIDE_CONFIG_DIR,
2890
+ });
2891
+ result.push({
2892
+ filePath: path.join(self.resolveService.OVERRIDE_CONFIG_DIR, "alias.config.tsx"),
2893
+ baseDir: self.resolveService.OVERRIDE_CONFIG_DIR,
2894
+ });
2895
+ result.push({
2896
+ filePath: path.join(self.resolveService.OVERRIDE_CONFIG_DIR, "alias.config.js"),
2897
+ baseDir: self.resolveService.OVERRIDE_CONFIG_DIR,
2898
+ });
2899
+ result.push({
2900
+ filePath: path.join(self.resolveService.OVERRIDE_CONFIG_DIR, "alias.config.json"),
2901
+ baseDir: self.resolveService.OVERRIDE_CONFIG_DIR,
2902
+ });
2903
+ return result;
2904
+ };
2853
2905
  const GET_ALIAS_EXPORTS_FN = (self) => {
2854
- const instance = self.getInstance(self.resolveService.OVERRIDE_CONFIG_DIR);
2855
- if (!instance.check("alias.config")) {
2856
- return null;
2906
+ for (const { filePath, baseDir } of GET_ALIAS_VARIANTS_FN(self)) {
2907
+ if (!fs.existsSync(filePath)) {
2908
+ continue;
2909
+ }
2910
+ const instance = self.getInstance(baseDir);
2911
+ const alias = instance.import(filePath);
2912
+ if (!alias) {
2913
+ return null;
2914
+ }
2915
+ if ("default" in alias) {
2916
+ return alias.default;
2917
+ }
2918
+ return alias;
2857
2919
  }
2858
- const exports = instance.import("alias.config");
2859
- return "default" in exports ? exports.default : exports;
2920
+ return null;
2860
2921
  };
2861
2922
  const INIT_ALIAS_FN = (self) => {
2862
2923
  const alias = GET_ALIAS_EXPORTS_FN(self);
@@ -2953,6 +3014,10 @@ class ConfigConnectionService {
2953
3014
  const config = await LOAD_CONFIG_CONFIG_FN(fileName, this);
2954
3015
  if (!config) {
2955
3016
  this.loadConfig.clear(fileName);
3017
+ return null;
3018
+ }
3019
+ if ("default" in config) {
3020
+ return config.default;
2956
3021
  }
2957
3022
  return config;
2958
3023
  });
@@ -3098,10 +3163,10 @@ init();
3098
3163
 
3099
3164
  const MODES = ["backtest", "walker", "paper", "live", "pine", "editor", "dump", "pnldebug", "brokerdebug", "flush", "init", "docker", "help", "version"];
3100
3165
  const ENTRY_PATH$1 = "./node_modules/@backtest-kit/cli/build/index.mjs";
3101
- const HELP_TEXT$1 = `
3102
- Example:
3103
-
3104
- node ${ENTRY_PATH$1} --help
3166
+ const HELP_TEXT$1 = `
3167
+ Example:
3168
+
3169
+ node ${ENTRY_PATH$1} --help
3105
3170
  `.trimStart();
3106
3171
  const main$g = async () => {
3107
3172
  if (!getEntry(import.meta.url)) {
@@ -3111,7 +3176,7 @@ const main$g = async () => {
3111
3176
  if (MODES.some((mode) => values[mode])) {
3112
3177
  return;
3113
3178
  }
3114
- process.stdout.write(`@backtest-kit/cli ${"9.5.0"}\n`);
3179
+ process.stdout.write(`@backtest-kit/cli ${"9.7.0"}\n`);
3115
3180
  process.stdout.write("\n");
3116
3181
  process.stdout.write(`Run with --help to see available commands.\n`);
3117
3182
  process.stdout.write("\n");
@@ -3402,9 +3467,15 @@ const main$a = async () => {
3402
3467
  }
3403
3468
  await flush(entryPoint);
3404
3469
  }
3470
+ await cli.configConnectionService.loadConfig("setup.config");
3405
3471
  {
3406
- await cli.configConnectionService.loadConfig("setup.config");
3407
- await cli.configConnectionService.loadConfig("loader.config");
3472
+ const loader = await cli.configConnectionService.loadConfig("loader.config");
3473
+ if (typeof loader === "function") {
3474
+ await loader();
3475
+ }
3476
+ if (typeof loader?.loader === "function") {
3477
+ await loader.loader();
3478
+ }
3408
3479
  }
3409
3480
  {
3410
3481
  await cli.configService.waitForInit();
@@ -3506,9 +3577,15 @@ const main$7 = async () => {
3506
3577
  const cwd = process.cwd();
3507
3578
  dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
3508
3579
  }
3580
+ await cli.configConnectionService.loadConfig("setup.config");
3509
3581
  {
3510
- await cli.configConnectionService.loadConfig("setup.config");
3511
- await cli.configConnectionService.loadConfig("loader.config");
3582
+ const loader = await cli.configConnectionService.loadConfig("loader.config");
3583
+ if (typeof loader === "function") {
3584
+ await loader();
3585
+ }
3586
+ if (typeof loader?.loader === "function") {
3587
+ await loader.loader();
3588
+ }
3512
3589
  }
3513
3590
  await cli.moduleConnectionService.loadModule("pine.module");
3514
3591
  {
@@ -3587,9 +3664,15 @@ const main$6 = async () => {
3587
3664
  console.warn("--editor and --pine are mutually exclusive. Use one at a time.");
3588
3665
  process.exit(1);
3589
3666
  }
3667
+ await cli.configConnectionService.loadConfig("setup.config");
3590
3668
  {
3591
- await cli.configConnectionService.loadConfig("setup.config");
3592
- await cli.configConnectionService.loadConfig("loader.config");
3669
+ const loader = await cli.configConnectionService.loadConfig("loader.config");
3670
+ if (typeof loader === "function") {
3671
+ await loader();
3672
+ }
3673
+ if (typeof loader?.loader === "function") {
3674
+ await loader.loader();
3675
+ }
3593
3676
  }
3594
3677
  {
3595
3678
  await cli.configService.waitForInit();
@@ -3632,9 +3715,15 @@ const main$5 = async () => {
3632
3715
  const cwd = process.cwd();
3633
3716
  dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
3634
3717
  }
3718
+ await cli.configConnectionService.loadConfig("setup.config");
3635
3719
  {
3636
- await cli.configConnectionService.loadConfig("setup.config");
3637
- await cli.configConnectionService.loadConfig("loader.config");
3720
+ const loader = await cli.configConnectionService.loadConfig("loader.config");
3721
+ if (typeof loader === "function") {
3722
+ await loader();
3723
+ }
3724
+ if (typeof loader?.loader === "function") {
3725
+ await loader.loader();
3726
+ }
3638
3727
  }
3639
3728
  await cli.moduleConnectionService.loadModule("dump.module");
3640
3729
  {
@@ -3703,9 +3792,15 @@ const main$4 = async () => {
3703
3792
  const cwd = process.cwd();
3704
3793
  dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
3705
3794
  }
3795
+ await cli.configConnectionService.loadConfig("setup.config");
3706
3796
  {
3707
- await cli.configConnectionService.loadConfig("setup.config");
3708
- await cli.configConnectionService.loadConfig("loader.config");
3797
+ const loader = await cli.configConnectionService.loadConfig("loader.config");
3798
+ if (typeof loader === "function") {
3799
+ await loader();
3800
+ }
3801
+ if (typeof loader?.loader === "function") {
3802
+ await loader.loader();
3803
+ }
3709
3804
  }
3710
3805
  await cli.moduleConnectionService.loadModule("pnldebug.module");
3711
3806
  {
@@ -3975,183 +4070,183 @@ const main$2 = async () => {
3975
4070
  main$2();
3976
4071
 
3977
4072
  const ENTRY_PATH = "./node_modules/@backtest-kit/cli/build/index.mjs";
3978
- const HELP_TEXT = `
3979
- Usage:
3980
- node index.mjs --<mode> [flags] [entry-point]
3981
-
3982
- Modes:
3983
-
3984
- --backtest <entry> Run strategy against historical candle data
3985
- --walker <entry...> Run Walker A/B strategy comparison across multiple strategies
3986
- --paper <entry> Paper trading (live prices, no real orders)
3987
- --live <entry> Live trading with real orders
3988
- --pine <entry> Execute a local .pine indicator file
3989
- --editor Open the Pine Script visual editor in the browser
3990
- --dump Fetch and save raw OHLCV candles
3991
- --pnldebug Simulate PnL per minute for a given entry price and direction
3992
- --brokerdebug Fire a single broker commit against the live broker adapter
3993
- --flush <entry...> Delete report/log/markdown/agent folders from strategy dump dir
3994
- --init Scaffold a new project in the current directory
3995
- --docker Scaffold a Docker workspace for running strategies in a container
3996
- --help Print this help message
3997
-
3998
- Backtest flags:
3999
-
4000
- --symbol <string> Trading pair (default: BTCUSDT)
4001
- --strategy <string> Strategy name from addStrategySchema (default: first registered)
4002
- --exchange <string> Exchange name from addExchangeSchema (default: first registered)
4003
- --frame <string> Frame name from addFrameSchema (default: first registered)
4004
- --cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
4005
- --noCache Skip candle cache warming before the run
4006
- --noFlush Skip removing report/log/markdown/agent folders before backtest run
4007
- --verbose Log every candle fetch to stdout
4008
- --ui Start web dashboard at http://localhost:60050
4009
- --telegram Send trade notifications to Telegram
4010
-
4011
- Walker flags (--walker):
4012
-
4013
- --symbol <string> Trading pair (default: BTCUSDT)
4014
- --cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
4015
- --noCache Skip candle cache warming before the run
4016
- --noFlush Skip removing report/log/markdown/agent folders before walker run
4017
- --verbose Log every candle fetch to stdout
4018
- --output <string> Output file base name (default: walker_{SYMBOL}_{TIMESTAMP})
4019
- --json Save results as JSON to ./dump/<output>.json
4020
- --markdown Save report as Markdown to ./dump/<output>.md
4021
-
4022
- Each positional argument is a strategy entry point. All strategy files are loaded without
4023
- changing process.cwd() — .env is read from the working directory only.
4024
- addWalkerSchema is called automatically using the registered exchange and frame.
4025
- After comparison completes the report is printed to stdout (or saved if --json/--markdown).
4026
-
4027
- Module file ./modules/walker.module is loaded automatically if it exists.
4028
-
4029
- Paper / Live flags:
4030
-
4031
- --symbol <string> Trading pair (default: BTCUSDT)
4032
- --strategy <string> Strategy name (default: first registered)
4033
- --exchange <string> Exchange name (default: first registered)
4034
- --verbose Log every candle fetch to stdout
4035
- --ui Start web dashboard
4036
- --telegram Send Telegram notifications
4037
-
4038
- PineScript flags (--pine):
4039
-
4040
- --symbol <string> Trading pair (default: BTCUSDT)
4041
- --timeframe <string> Candle interval (default: 15m)
4042
- --limit <string> Number of candles to fetch (default: 250)
4043
- --when <string> End date — ISO 8601 or Unix ms (default: now)
4044
- --exchange <string> Exchange name (default: first registered)
4045
- --output <string> Output file base name without extension
4046
- --json Save output as JSON array to <pine-dir>/dump/<output>.json
4047
- --jsonl Save output as JSONL to <pine-dir>/dump/<output>.jsonl
4048
- --markdown Save output as Markdown table to <pine-dir>/dump/<output>.md
4049
-
4050
- Only plot() calls with display=display.data_window produce output columns.
4051
- Module file ./modules/pine.module is loaded automatically if it exists.
4052
-
4053
- Candle dump flags (--dump):
4054
-
4055
- --symbol <string> Trading pair (default: BTCUSDT)
4056
- --timeframe <string> Candle interval (default: 15m)
4057
- --limit <string> Number of candles (default: 250)
4058
- --when <string> End date — ISO 8601 or Unix ms (default: now)
4059
- --exchange <string> Exchange name (default: first registered)
4060
- --output <string> Output file base name (default: {SYMBOL}_{LIMIT}_{TIMEFRAME}_{TIMESTAMP})
4061
- --json Save as JSON array to ./dump/<output>.json
4062
- --jsonl Save as JSONL to ./dump/<output>.jsonl
4063
-
4064
- Module file ./modules/dump.module is loaded automatically if it exists.
4065
-
4066
- PnL debug flags (--pnldebug):
4067
-
4068
- --symbol <string> Trading pair (default: BTCUSDT)
4069
- --priceopen <number> Entry price (required)
4070
- --direction <string> Position direction: long or short (default: long)
4071
- --when <string> Start timestamp — ISO 8601 or Unix ms (default: now)
4072
- --minutes <string> Number of 1m candles to simulate (default: 60)
4073
- --exchange <string> Exchange name (default: first registered)
4074
- --output <string> Output file base name (default: {SYMBOL}_{DIRECTION}_{PRICEOPEN}_{TIMESTAMP})
4075
- --json Save as JSON array to ./dump/<output>.json
4076
- --jsonl Save as JSONL to ./dump/<output>.jsonl
4077
- --markdown Save as Markdown table to ./dump/<output>.md
4078
-
4079
- Module file ./modules/pnldebug.module is loaded automatically if it exists.
4080
-
4081
- Broker debug flags (--brokerdebug):
4082
-
4083
- --symbol <string> Trading pair (default: BTCUSDT)
4084
- --exchange <string> Exchange name (default: first registered)
4085
- --commit <string> Commit type to fire: signal-open, signal-close, partial-profit,
4086
- partial-loss, average-buy, trailing-stop, trailing-take, breakeven
4087
- (default: signal-open)
4088
-
4089
- Loads ./live.module, fetches the last candle for --symbol/--timeframe, and calls
4090
- the selected broker commit with synthetic payload values derived from current price.
4091
-
4092
- Flush flags (--flush):
4093
-
4094
- One or more positional entry points. For each entry point the following
4095
- subdirectories are removed from <entry-dir>/dump/:
4096
-
4097
- report log markdown agent
4098
-
4099
- Init flags (--init):
4100
-
4101
- --output <string> Target directory name (default: backtest-kit-project)
4102
-
4103
- Scaffolds a project and runs scripts/fetch_docs.mjs to download library docs.
4104
-
4105
- Docker flags (--docker):
4106
-
4107
- --output <string> Target directory name (default: backtest-kit-docker)
4108
-
4109
- Scaffolds a Docker workspace: docker-compose.yaml, .env.example, package.json,
4110
- tsconfig.json, and a sample strategy under content/. Run npm install then
4111
- docker compose up to start the container.
4112
-
4113
- Module hooks (loaded automatically by each mode):
4114
-
4115
- modules/backtest.module --backtest Broker adapter for backtest
4116
- modules/walker.module --walker Broker adapter for walker comparison
4117
- modules/paper.module --paper Broker adapter for paper trading
4118
- modules/live.module --live Broker adapter for live trading
4119
- modules/pine.module --pine Exchange schema for PineScript runs
4120
- modules/editor.module --editor Exchange schema for the visual Pine editor
4121
- modules/dump.module --dump Exchange schema for candle dumps
4122
- modules/pnldebug.module --pnldebug Exchange schema for PnL debug runs
4123
- modules/brokerdebug.module --brokerdebug Broker adapter used for broker commit testing
4124
-
4125
- --flush has no associated module. It only removes dump subdirectories.
4126
-
4127
- Extensions .ts, .mjs, .cjs are tried automatically. Missing module = soft warning.
4128
-
4129
- Environment variables:
4130
-
4131
- CC_TELEGRAM_TOKEN Telegram bot token (required for --telegram)
4132
- CC_TELEGRAM_CHANNEL Telegram channel or chat ID (required for --telegram)
4133
- CC_WWWROOT_HOST UI server bind address (default: 0.0.0.0)
4134
- CC_WWWROOT_PORT UI server port (default: 60050)
4135
-
4136
- Examples:
4137
-
4138
- node ${ENTRY_PATH} --backtest ./content/feb_2026.strategy.ts
4139
- node ${ENTRY_PATH} --backtest --symbol BTCUSDT --noCache --noFlush --ui ./content/feb_2026.strategy.ts
4140
- node ${ENTRY_PATH} --walker ./content/feb_2026_v1.strategy.ts ./content/feb_2026_v2.strategy.ts ./content/feb_2026_v3.strategy.ts
4141
- node ${ENTRY_PATH} --walker --symbol BTCUSDT --noCache --noFlush --markdown ./content/feb_2026_v1.ts ./content/feb_2026_v2.ts
4142
- node ${ENTRY_PATH} --paper --symbol ETHUSDT ./content/feb_2026.strategy.ts
4143
- node ${ENTRY_PATH} --live --ui --telegram ./content/feb_2026.strategy.ts
4144
- node ${ENTRY_PATH} --pine ./math/feb_2026.pine --timeframe 15m --limit 500 --jsonl
4145
- node ${ENTRY_PATH} --editor
4146
- node ${ENTRY_PATH} --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl
4147
- node ${ENTRY_PATH} --pnldebug --symbol BTCUSDT --priceopen 64069.50 --direction short --when "2025-02-25" --minutes 120
4148
- node ${ENTRY_PATH} --pnldebug --priceopen 67956.73 --direction long --when 1772064000000 --minutes 60 --markdown
4149
- node ${ENTRY_PATH} --brokerdebug --commit signal-open --symbol BTCUSDT
4150
- node ${ENTRY_PATH} --brokerdebug --commit partial-profit --symbol ETHUSDT
4151
- node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts
4152
- node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts ./content/feb_2026.strategy/feb_2026.test.ts
4153
- node ${ENTRY_PATH} --init --output my-trading-bot
4154
- node ${ENTRY_PATH} --docker --output my-docker-workspace
4073
+ const HELP_TEXT = `
4074
+ Usage:
4075
+ node index.mjs --<mode> [flags] [entry-point]
4076
+
4077
+ Modes:
4078
+
4079
+ --backtest <entry> Run strategy against historical candle data
4080
+ --walker <entry...> Run Walker A/B strategy comparison across multiple strategies
4081
+ --paper <entry> Paper trading (live prices, no real orders)
4082
+ --live <entry> Live trading with real orders
4083
+ --pine <entry> Execute a local .pine indicator file
4084
+ --editor Open the Pine Script visual editor in the browser
4085
+ --dump Fetch and save raw OHLCV candles
4086
+ --pnldebug Simulate PnL per minute for a given entry price and direction
4087
+ --brokerdebug Fire a single broker commit against the live broker adapter
4088
+ --flush <entry...> Delete report/log/markdown/agent folders from strategy dump dir
4089
+ --init Scaffold a new project in the current directory
4090
+ --docker Scaffold a Docker workspace for running strategies in a container
4091
+ --help Print this help message
4092
+
4093
+ Backtest flags:
4094
+
4095
+ --symbol <string> Trading pair (default: BTCUSDT)
4096
+ --strategy <string> Strategy name from addStrategySchema (default: first registered)
4097
+ --exchange <string> Exchange name from addExchangeSchema (default: first registered)
4098
+ --frame <string> Frame name from addFrameSchema (default: first registered)
4099
+ --cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
4100
+ --noCache Skip candle cache warming before the run
4101
+ --noFlush Skip removing report/log/markdown/agent folders before backtest run
4102
+ --verbose Log every candle fetch to stdout
4103
+ --ui Start web dashboard at http://localhost:60050
4104
+ --telegram Send trade notifications to Telegram
4105
+
4106
+ Walker flags (--walker):
4107
+
4108
+ --symbol <string> Trading pair (default: BTCUSDT)
4109
+ --cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
4110
+ --noCache Skip candle cache warming before the run
4111
+ --noFlush Skip removing report/log/markdown/agent folders before walker run
4112
+ --verbose Log every candle fetch to stdout
4113
+ --output <string> Output file base name (default: walker_{SYMBOL}_{TIMESTAMP})
4114
+ --json Save results as JSON to ./dump/<output>.json
4115
+ --markdown Save report as Markdown to ./dump/<output>.md
4116
+
4117
+ Each positional argument is a strategy entry point. All strategy files are loaded without
4118
+ changing process.cwd() — .env is read from the working directory only.
4119
+ addWalkerSchema is called automatically using the registered exchange and frame.
4120
+ After comparison completes the report is printed to stdout (or saved if --json/--markdown).
4121
+
4122
+ Module file ./modules/walker.module is loaded automatically if it exists.
4123
+
4124
+ Paper / Live flags:
4125
+
4126
+ --symbol <string> Trading pair (default: BTCUSDT)
4127
+ --strategy <string> Strategy name (default: first registered)
4128
+ --exchange <string> Exchange name (default: first registered)
4129
+ --verbose Log every candle fetch to stdout
4130
+ --ui Start web dashboard
4131
+ --telegram Send Telegram notifications
4132
+
4133
+ PineScript flags (--pine):
4134
+
4135
+ --symbol <string> Trading pair (default: BTCUSDT)
4136
+ --timeframe <string> Candle interval (default: 15m)
4137
+ --limit <string> Number of candles to fetch (default: 250)
4138
+ --when <string> End date — ISO 8601 or Unix ms (default: now)
4139
+ --exchange <string> Exchange name (default: first registered)
4140
+ --output <string> Output file base name without extension
4141
+ --json Save output as JSON array to <pine-dir>/dump/<output>.json
4142
+ --jsonl Save output as JSONL to <pine-dir>/dump/<output>.jsonl
4143
+ --markdown Save output as Markdown table to <pine-dir>/dump/<output>.md
4144
+
4145
+ Only plot() calls with display=display.data_window produce output columns.
4146
+ Module file ./modules/pine.module is loaded automatically if it exists.
4147
+
4148
+ Candle dump flags (--dump):
4149
+
4150
+ --symbol <string> Trading pair (default: BTCUSDT)
4151
+ --timeframe <string> Candle interval (default: 15m)
4152
+ --limit <string> Number of candles (default: 250)
4153
+ --when <string> End date — ISO 8601 or Unix ms (default: now)
4154
+ --exchange <string> Exchange name (default: first registered)
4155
+ --output <string> Output file base name (default: {SYMBOL}_{LIMIT}_{TIMEFRAME}_{TIMESTAMP})
4156
+ --json Save as JSON array to ./dump/<output>.json
4157
+ --jsonl Save as JSONL to ./dump/<output>.jsonl
4158
+
4159
+ Module file ./modules/dump.module is loaded automatically if it exists.
4160
+
4161
+ PnL debug flags (--pnldebug):
4162
+
4163
+ --symbol <string> Trading pair (default: BTCUSDT)
4164
+ --priceopen <number> Entry price (required)
4165
+ --direction <string> Position direction: long or short (default: long)
4166
+ --when <string> Start timestamp — ISO 8601 or Unix ms (default: now)
4167
+ --minutes <string> Number of 1m candles to simulate (default: 60)
4168
+ --exchange <string> Exchange name (default: first registered)
4169
+ --output <string> Output file base name (default: {SYMBOL}_{DIRECTION}_{PRICEOPEN}_{TIMESTAMP})
4170
+ --json Save as JSON array to ./dump/<output>.json
4171
+ --jsonl Save as JSONL to ./dump/<output>.jsonl
4172
+ --markdown Save as Markdown table to ./dump/<output>.md
4173
+
4174
+ Module file ./modules/pnldebug.module is loaded automatically if it exists.
4175
+
4176
+ Broker debug flags (--brokerdebug):
4177
+
4178
+ --symbol <string> Trading pair (default: BTCUSDT)
4179
+ --exchange <string> Exchange name (default: first registered)
4180
+ --commit <string> Commit type to fire: signal-open, signal-close, partial-profit,
4181
+ partial-loss, average-buy, trailing-stop, trailing-take, breakeven
4182
+ (default: signal-open)
4183
+
4184
+ Loads ./live.module, fetches the last candle for --symbol/--timeframe, and calls
4185
+ the selected broker commit with synthetic payload values derived from current price.
4186
+
4187
+ Flush flags (--flush):
4188
+
4189
+ One or more positional entry points. For each entry point the following
4190
+ subdirectories are removed from <entry-dir>/dump/:
4191
+
4192
+ report log markdown agent
4193
+
4194
+ Init flags (--init):
4195
+
4196
+ --output <string> Target directory name (default: backtest-kit-project)
4197
+
4198
+ Scaffolds a project and runs scripts/fetch_docs.mjs to download library docs.
4199
+
4200
+ Docker flags (--docker):
4201
+
4202
+ --output <string> Target directory name (default: backtest-kit-docker)
4203
+
4204
+ Scaffolds a Docker workspace: docker-compose.yaml, .env.example, package.json,
4205
+ tsconfig.json, and a sample strategy under content/. Run npm install then
4206
+ docker compose up to start the container.
4207
+
4208
+ Module hooks (loaded automatically by each mode):
4209
+
4210
+ modules/backtest.module --backtest Broker adapter for backtest
4211
+ modules/walker.module --walker Broker adapter for walker comparison
4212
+ modules/paper.module --paper Broker adapter for paper trading
4213
+ modules/live.module --live Broker adapter for live trading
4214
+ modules/pine.module --pine Exchange schema for PineScript runs
4215
+ modules/editor.module --editor Exchange schema for the visual Pine editor
4216
+ modules/dump.module --dump Exchange schema for candle dumps
4217
+ modules/pnldebug.module --pnldebug Exchange schema for PnL debug runs
4218
+ modules/brokerdebug.module --brokerdebug Broker adapter used for broker commit testing
4219
+
4220
+ --flush has no associated module. It only removes dump subdirectories.
4221
+
4222
+ Extensions .ts, .mjs, .cjs are tried automatically. Missing module = soft warning.
4223
+
4224
+ Environment variables:
4225
+
4226
+ CC_TELEGRAM_TOKEN Telegram bot token (required for --telegram)
4227
+ CC_TELEGRAM_CHANNEL Telegram channel or chat ID (required for --telegram)
4228
+ CC_WWWROOT_HOST UI server bind address (default: 0.0.0.0)
4229
+ CC_WWWROOT_PORT UI server port (default: 60050)
4230
+
4231
+ Examples:
4232
+
4233
+ node ${ENTRY_PATH} --backtest ./content/feb_2026.strategy.ts
4234
+ node ${ENTRY_PATH} --backtest --symbol BTCUSDT --noCache --noFlush --ui ./content/feb_2026.strategy.ts
4235
+ node ${ENTRY_PATH} --walker ./content/feb_2026_v1.strategy.ts ./content/feb_2026_v2.strategy.ts ./content/feb_2026_v3.strategy.ts
4236
+ node ${ENTRY_PATH} --walker --symbol BTCUSDT --noCache --noFlush --markdown ./content/feb_2026_v1.ts ./content/feb_2026_v2.ts
4237
+ node ${ENTRY_PATH} --paper --symbol ETHUSDT ./content/feb_2026.strategy.ts
4238
+ node ${ENTRY_PATH} --live --ui --telegram ./content/feb_2026.strategy.ts
4239
+ node ${ENTRY_PATH} --pine ./math/feb_2026.pine --timeframe 15m --limit 500 --jsonl
4240
+ node ${ENTRY_PATH} --editor
4241
+ node ${ENTRY_PATH} --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl
4242
+ node ${ENTRY_PATH} --pnldebug --symbol BTCUSDT --priceopen 64069.50 --direction short --when "2025-02-25" --minutes 120
4243
+ node ${ENTRY_PATH} --pnldebug --priceopen 67956.73 --direction long --when 1772064000000 --minutes 60 --markdown
4244
+ node ${ENTRY_PATH} --brokerdebug --commit signal-open --symbol BTCUSDT
4245
+ node ${ENTRY_PATH} --brokerdebug --commit partial-profit --symbol ETHUSDT
4246
+ node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts
4247
+ node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts ./content/feb_2026.strategy/feb_2026.test.ts
4248
+ node ${ENTRY_PATH} --init --output my-trading-bot
4249
+ node ${ENTRY_PATH} --docker --output my-docker-workspace
4155
4250
  `.trimStart();
4156
4251
  const main$1 = async () => {
4157
4252
  if (!getEntry(import.meta.url)) {
@@ -4161,7 +4256,7 @@ const main$1 = async () => {
4161
4256
  if (!values.help) {
4162
4257
  return;
4163
4258
  }
4164
- process.stdout.write(`@backtest-kit/cli ${"9.5.0"}\n\n`);
4259
+ process.stdout.write(`@backtest-kit/cli ${"9.7.0"}\n\n`);
4165
4260
  process.stdout.write(HELP_TEXT);
4166
4261
  process.exit(0);
4167
4262
  };
@@ -4175,7 +4270,7 @@ const main = async () => {
4175
4270
  if (!values.version) {
4176
4271
  return;
4177
4272
  }
4178
- process.stdout.write(`@backtest-kit/cli ${"9.5.0"}\n`);
4273
+ process.stdout.write(`@backtest-kit/cli ${"9.7.0"}\n`);
4179
4274
  process.exit(0);
4180
4275
  };
4181
4276
  main();
@@ -1,13 +1,13 @@
1
- export default {
2
- signal: true,
3
- risk: true,
4
- info: true,
5
- breakeven: true,
6
- common_error: true,
7
- critical_error: true,
8
- validation_error: true,
9
- partial_loss: false,
10
- partial_profit: false,
11
- signal_sync: false,
12
- strategy_commit: true,
13
- };
1
+ export default {
2
+ signal: true,
3
+ risk: true,
4
+ info: true,
5
+ breakeven: true,
6
+ common_error: true,
7
+ critical_error: true,
8
+ validation_error: true,
9
+ partial_loss: false,
10
+ partial_profit: false,
11
+ signal_sync: false,
12
+ strategy_commit: true,
13
+ };