@backtest-kit/cli 8.4.3 → 9.0.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/build/index.mjs CHANGED
@@ -496,6 +496,10 @@ const getArgs = singleshot(() => {
496
496
  type: "boolean",
497
497
  default: false,
498
498
  },
499
+ entry: {
500
+ type: "boolean",
501
+ default: false,
502
+ },
499
503
  cacheInterval: {
500
504
  type: "string",
501
505
  default: "1m, 15m, 30m, 4h",
@@ -652,6 +656,9 @@ const notifyFinish = singleshot(() => {
652
656
  });
653
657
 
654
658
  const getEntry = (metaUrl) => {
659
+ if (!process.argv[1]) {
660
+ return "";
661
+ }
655
662
  const metaPath = fileURLToPath(metaUrl);
656
663
  const realArgv = realpathSync(process.argv[1]);
657
664
  return path.resolve(realArgv) === path.resolve(metaPath);
@@ -2985,12 +2992,12 @@ init();
2985
2992
 
2986
2993
  const MODES = ["backtest", "walker", "paper", "live", "pine", "editor", "dump", "pnldebug", "brokerdebug", "flush", "init", "docker", "help", "version"];
2987
2994
  const ENTRY_PATH$1 = "./node_modules/@backtest-kit/cli/build/index.mjs";
2988
- const HELP_TEXT$1 = `
2989
- Example:
2990
-
2991
- node ${ENTRY_PATH$1} --help
2995
+ const HELP_TEXT$1 = `
2996
+ Example:
2997
+
2998
+ node ${ENTRY_PATH$1} --help
2992
2999
  `.trimStart();
2993
- const main$f = async () => {
3000
+ const main$g = async () => {
2994
3001
  if (!getEntry(import.meta.url)) {
2995
3002
  return;
2996
3003
  }
@@ -2998,14 +3005,14 @@ const main$f = async () => {
2998
3005
  if (MODES.some((mode) => values[mode])) {
2999
3006
  return;
3000
3007
  }
3001
- process.stdout.write(`@backtest-kit/cli ${"8.4.3"}\n`);
3008
+ process.stdout.write(`@backtest-kit/cli ${"9.0.0"}\n`);
3002
3009
  process.stdout.write("\n");
3003
3010
  process.stdout.write(`Run with --help to see available commands.\n`);
3004
3011
  process.stdout.write("\n");
3005
3012
  process.stdout.write(HELP_TEXT$1);
3006
3013
  process.exit(0);
3007
3014
  };
3008
- main$f();
3015
+ main$g();
3009
3016
 
3010
3017
  const notifyShutdown = singleshot(async () => {
3011
3018
  console.log("Graceful shutdown initiated. Press Ctrl+C again to force quit.");
@@ -3021,7 +3028,7 @@ const flush = async (entryPoint) => {
3021
3028
  console.log(`Removed: ${target}`);
3022
3029
  }
3023
3030
  };
3024
- const main$e = async () => {
3031
+ const main$f = async () => {
3025
3032
  if (!getEntry(import.meta.url)) {
3026
3033
  return;
3027
3034
  }
@@ -3038,7 +3045,7 @@ const main$e = async () => {
3038
3045
  }
3039
3046
  process.exit(0);
3040
3047
  };
3041
- main$e();
3048
+ main$f();
3042
3049
 
3043
3050
  const BEFORE_EXIT_FN$5 = singleshot(async () => {
3044
3051
  process.off("SIGINT", BEFORE_EXIT_FN$5);
@@ -3060,7 +3067,7 @@ const BEFORE_EXIT_FN$5 = singleshot(async () => {
3060
3067
  const listenGracefulShutdown$5 = singleshot(() => {
3061
3068
  process.on("SIGINT", BEFORE_EXIT_FN$5);
3062
3069
  });
3063
- const main$d = async () => {
3070
+ const main$e = async () => {
3064
3071
  if (!getEntry(import.meta.url)) {
3065
3072
  return;
3066
3073
  }
@@ -3068,6 +3075,9 @@ const main$d = async () => {
3068
3075
  if (!values.backtest) {
3069
3076
  return;
3070
3077
  }
3078
+ if (values.entry) {
3079
+ return;
3080
+ }
3071
3081
  if (!values.noFlush) {
3072
3082
  const [entryPoint = null] = getPositionals();
3073
3083
  entryPoint && await flush(entryPoint);
@@ -3075,7 +3085,7 @@ const main$d = async () => {
3075
3085
  await cli.backtestMainService.connect();
3076
3086
  listenGracefulShutdown$5();
3077
3087
  };
3078
- main$d();
3088
+ main$e();
3079
3089
 
3080
3090
  const BEFORE_EXIT_FN$4 = singleshot(async () => {
3081
3091
  process.off("SIGINT", BEFORE_EXIT_FN$4);
@@ -3093,7 +3103,7 @@ const BEFORE_EXIT_FN$4 = singleshot(async () => {
3093
3103
  const listenGracefulShutdown$4 = singleshot(() => {
3094
3104
  process.on("SIGINT", BEFORE_EXIT_FN$4);
3095
3105
  });
3096
- const main$c = async () => {
3106
+ const main$d = async () => {
3097
3107
  if (!getEntry(import.meta.url)) {
3098
3108
  return;
3099
3109
  }
@@ -3101,6 +3111,9 @@ const main$c = async () => {
3101
3111
  if (!values.walker) {
3102
3112
  return;
3103
3113
  }
3114
+ if (values.entry) {
3115
+ return;
3116
+ }
3104
3117
  if (!values.noFlush) {
3105
3118
  for (const entryPoint of getPositionals()) {
3106
3119
  await flush(entryPoint);
@@ -3109,7 +3122,7 @@ const main$c = async () => {
3109
3122
  listenGracefulShutdown$4();
3110
3123
  await cli.walkerMainService.connect();
3111
3124
  };
3112
- main$c();
3125
+ main$d();
3113
3126
 
3114
3127
  const BEFORE_EXIT_FN$3 = singleshot(async () => {
3115
3128
  process.off("SIGINT", BEFORE_EXIT_FN$3);
@@ -3130,7 +3143,7 @@ const BEFORE_EXIT_FN$3 = singleshot(async () => {
3130
3143
  const listenGracefulShutdown$3 = singleshot(() => {
3131
3144
  process.on("SIGINT", BEFORE_EXIT_FN$3);
3132
3145
  });
3133
- const main$b = async () => {
3146
+ const main$c = async () => {
3134
3147
  if (!getEntry(import.meta.url)) {
3135
3148
  return;
3136
3149
  }
@@ -3138,10 +3151,13 @@ const main$b = async () => {
3138
3151
  if (!values.paper) {
3139
3152
  return;
3140
3153
  }
3154
+ if (values.entry) {
3155
+ return;
3156
+ }
3141
3157
  cli.paperMainService.connect();
3142
3158
  listenGracefulShutdown$3();
3143
3159
  };
3144
- main$b();
3160
+ main$c();
3145
3161
 
3146
3162
  const BEFORE_EXIT_FN$2 = singleshot(async () => {
3147
3163
  process.off("SIGINT", BEFORE_EXIT_FN$2);
@@ -3162,7 +3178,7 @@ const BEFORE_EXIT_FN$2 = singleshot(async () => {
3162
3178
  const listenGracefulShutdown$2 = singleshot(() => {
3163
3179
  process.on("SIGINT", BEFORE_EXIT_FN$2);
3164
3180
  });
3165
- const main$a = async () => {
3181
+ const main$b = async () => {
3166
3182
  if (!getEntry(import.meta.url)) {
3167
3183
  return;
3168
3184
  }
@@ -3170,9 +3186,122 @@ const main$a = async () => {
3170
3186
  if (!values.live) {
3171
3187
  return;
3172
3188
  }
3189
+ if (values.entry) {
3190
+ return;
3191
+ }
3173
3192
  await cli.liveMainService.connect();
3174
3193
  listenGracefulShutdown$2();
3175
3194
  };
3195
+ main$b();
3196
+
3197
+ const MODE_MODULE = {
3198
+ backtest: "./backtest.module",
3199
+ live: "./live.module",
3200
+ paper: "./paper.module",
3201
+ walker: "./walker.module",
3202
+ };
3203
+ const resolveMode = (values) => {
3204
+ const enabled = ["backtest", "live", "paper", "walker"].filter((mode) => Boolean(values[mode]));
3205
+ if (enabled.length !== 1) {
3206
+ return null;
3207
+ }
3208
+ return enabled[0];
3209
+ };
3210
+ const stopBacktestList = async () => {
3211
+ for (const item of await Backtest.list()) {
3212
+ if (item.status === "fulfilled") {
3213
+ continue;
3214
+ }
3215
+ Backtest.stop(item.symbol, {
3216
+ exchangeName: item.exchangeName,
3217
+ strategyName: item.strategyName,
3218
+ frameName: item.frameName,
3219
+ });
3220
+ }
3221
+ };
3222
+ const stopLiveList = async () => {
3223
+ for (const item of await Live.list()) {
3224
+ if (item.status === "fulfilled") {
3225
+ continue;
3226
+ }
3227
+ Live.stop(item.symbol, {
3228
+ exchangeName: item.exchangeName,
3229
+ strategyName: item.strategyName,
3230
+ });
3231
+ }
3232
+ };
3233
+ const stopWalkerList = async () => {
3234
+ for (const item of await Walker.list()) {
3235
+ if (item.status === "fulfilled") {
3236
+ continue;
3237
+ }
3238
+ Walker.stop(item.symbol, { walkerName: item.walkerName });
3239
+ }
3240
+ };
3241
+ const MODE_STOP = {
3242
+ backtest: stopBacktestList,
3243
+ live: stopLiveList,
3244
+ paper: stopLiveList,
3245
+ walker: stopWalkerList,
3246
+ };
3247
+ const listenFinish = singleshot(() => {
3248
+ let disposeRef;
3249
+ const unBacktest = listenDoneBacktest(() => {
3250
+ console.log("Backtest trading finished");
3251
+ disposeRef && disposeRef();
3252
+ });
3253
+ const unLive = listenDoneLive(() => {
3254
+ console.log("Live trading finished");
3255
+ disposeRef && disposeRef();
3256
+ });
3257
+ const unWalker = listenDoneWalker(() => {
3258
+ console.log("Walker comparison finished");
3259
+ disposeRef && disposeRef();
3260
+ });
3261
+ disposeRef = compose(() => unBacktest(), () => unLive(), () => unWalker());
3262
+ shutdown();
3263
+ });
3264
+ const createGracefulShutdown = (mode) => {
3265
+ const stop = MODE_STOP[mode];
3266
+ const handler = singleshot(async () => {
3267
+ process.off("SIGINT", handler);
3268
+ notifyShutdown();
3269
+ await stop();
3270
+ });
3271
+ return singleshot(() => {
3272
+ process.on("SIGINT", handler);
3273
+ });
3274
+ };
3275
+ const main$a = async () => {
3276
+ if (!getEntry(import.meta.url)) {
3277
+ return;
3278
+ }
3279
+ const { values } = getArgs();
3280
+ if (!values.entry) {
3281
+ return;
3282
+ }
3283
+ const mode = resolveMode(values);
3284
+ if (!mode) {
3285
+ console.error("--entry requires exactly one of --backtest, --live, --paper, --walker");
3286
+ process.exit(1);
3287
+ return;
3288
+ }
3289
+ const [entryPoint = null] = getPositionals();
3290
+ if (!entryPoint) {
3291
+ throw new Error("Entry point is required");
3292
+ }
3293
+ if (!values.noFlush) {
3294
+ await flush(entryPoint);
3295
+ }
3296
+ await cli.configService.waitForInit();
3297
+ Setup.enable();
3298
+ cli.frontendProviderService.connect();
3299
+ cli.telegramProviderService.connect();
3300
+ await cli.moduleConnectionService.loadModule(MODE_MODULE[mode]);
3301
+ listenFinish();
3302
+ createGracefulShutdown(mode)();
3303
+ await cli.resolveService.attachJavascript(entryPoint);
3304
+ };
3176
3305
  main$a();
3177
3306
 
3178
3307
  const BEFORE_EXIT_FN$1 = singleshot(async () => {
@@ -3693,183 +3822,183 @@ const main$2 = async () => {
3693
3822
  main$2();
3694
3823
 
3695
3824
  const ENTRY_PATH = "./node_modules/@backtest-kit/cli/build/index.mjs";
3696
- const HELP_TEXT = `
3697
- Usage:
3698
- node index.mjs --<mode> [flags] [entry-point]
3699
-
3700
- Modes:
3701
-
3702
- --backtest <entry> Run strategy against historical candle data
3703
- --walker <entry...> Run Walker A/B strategy comparison across multiple strategies
3704
- --paper <entry> Paper trading (live prices, no real orders)
3705
- --live <entry> Live trading with real orders
3706
- --pine <entry> Execute a local .pine indicator file
3707
- --editor Open the Pine Script visual editor in the browser
3708
- --dump Fetch and save raw OHLCV candles
3709
- --pnldebug Simulate PnL per minute for a given entry price and direction
3710
- --brokerdebug Fire a single broker commit against the live broker adapter
3711
- --flush <entry...> Delete report/log/markdown/agent folders from strategy dump dir
3712
- --init Scaffold a new project in the current directory
3713
- --docker Scaffold a Docker workspace for running strategies in a container
3714
- --help Print this help message
3715
-
3716
- Backtest flags:
3717
-
3718
- --symbol <string> Trading pair (default: BTCUSDT)
3719
- --strategy <string> Strategy name from addStrategySchema (default: first registered)
3720
- --exchange <string> Exchange name from addExchangeSchema (default: first registered)
3721
- --frame <string> Frame name from addFrameSchema (default: first registered)
3722
- --cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
3723
- --noCache Skip candle cache warming before the run
3724
- --noFlush Skip removing report/log/markdown/agent folders before backtest run
3725
- --verbose Log every candle fetch to stdout
3726
- --ui Start web dashboard at http://localhost:60050
3727
- --telegram Send trade notifications to Telegram
3728
-
3729
- Walker flags (--walker):
3730
-
3731
- --symbol <string> Trading pair (default: BTCUSDT)
3732
- --cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
3733
- --noCache Skip candle cache warming before the run
3734
- --noFlush Skip removing report/log/markdown/agent folders before walker run
3735
- --verbose Log every candle fetch to stdout
3736
- --output <string> Output file base name (default: walker_{SYMBOL}_{TIMESTAMP})
3737
- --json Save results as JSON to ./dump/<output>.json
3738
- --markdown Save report as Markdown to ./dump/<output>.md
3739
-
3740
- Each positional argument is a strategy entry point. All strategy files are loaded without
3741
- changing process.cwd() — .env is read from the working directory only.
3742
- addWalkerSchema is called automatically using the registered exchange and frame.
3743
- After comparison completes the report is printed to stdout (or saved if --json/--markdown).
3744
-
3745
- Module file ./modules/walker.module is loaded automatically if it exists.
3746
-
3747
- Paper / Live flags:
3748
-
3749
- --symbol <string> Trading pair (default: BTCUSDT)
3750
- --strategy <string> Strategy name (default: first registered)
3751
- --exchange <string> Exchange name (default: first registered)
3752
- --verbose Log every candle fetch to stdout
3753
- --ui Start web dashboard
3754
- --telegram Send Telegram notifications
3755
-
3756
- PineScript flags (--pine):
3757
-
3758
- --symbol <string> Trading pair (default: BTCUSDT)
3759
- --timeframe <string> Candle interval (default: 15m)
3760
- --limit <string> Number of candles to fetch (default: 250)
3761
- --when <string> End date — ISO 8601 or Unix ms (default: now)
3762
- --exchange <string> Exchange name (default: first registered)
3763
- --output <string> Output file base name without extension
3764
- --json Save output as JSON array to <pine-dir>/dump/<output>.json
3765
- --jsonl Save output as JSONL to <pine-dir>/dump/<output>.jsonl
3766
- --markdown Save output as Markdown table to <pine-dir>/dump/<output>.md
3767
-
3768
- Only plot() calls with display=display.data_window produce output columns.
3769
- Module file ./modules/pine.module is loaded automatically if it exists.
3770
-
3771
- Candle dump flags (--dump):
3772
-
3773
- --symbol <string> Trading pair (default: BTCUSDT)
3774
- --timeframe <string> Candle interval (default: 15m)
3775
- --limit <string> Number of candles (default: 250)
3776
- --when <string> End date — ISO 8601 or Unix ms (default: now)
3777
- --exchange <string> Exchange name (default: first registered)
3778
- --output <string> Output file base name (default: {SYMBOL}_{LIMIT}_{TIMEFRAME}_{TIMESTAMP})
3779
- --json Save as JSON array to ./dump/<output>.json
3780
- --jsonl Save as JSONL to ./dump/<output>.jsonl
3781
-
3782
- Module file ./modules/dump.module is loaded automatically if it exists.
3783
-
3784
- PnL debug flags (--pnldebug):
3785
-
3786
- --symbol <string> Trading pair (default: BTCUSDT)
3787
- --priceopen <number> Entry price (required)
3788
- --direction <string> Position direction: long or short (default: long)
3789
- --when <string> Start timestamp — ISO 8601 or Unix ms (default: now)
3790
- --minutes <string> Number of 1m candles to simulate (default: 60)
3791
- --exchange <string> Exchange name (default: first registered)
3792
- --output <string> Output file base name (default: {SYMBOL}_{DIRECTION}_{PRICEOPEN}_{TIMESTAMP})
3793
- --json Save as JSON array to ./dump/<output>.json
3794
- --jsonl Save as JSONL to ./dump/<output>.jsonl
3795
- --markdown Save as Markdown table to ./dump/<output>.md
3796
-
3797
- Module file ./modules/pnldebug.module is loaded automatically if it exists.
3798
-
3799
- Broker debug flags (--brokerdebug):
3800
-
3801
- --symbol <string> Trading pair (default: BTCUSDT)
3802
- --exchange <string> Exchange name (default: first registered)
3803
- --commit <string> Commit type to fire: signal-open, signal-close, partial-profit,
3804
- partial-loss, average-buy, trailing-stop, trailing-take, breakeven
3805
- (default: signal-open)
3806
-
3807
- Loads ./live.module, fetches the last candle for --symbol/--timeframe, and calls
3808
- the selected broker commit with synthetic payload values derived from current price.
3809
-
3810
- Flush flags (--flush):
3811
-
3812
- One or more positional entry points. For each entry point the following
3813
- subdirectories are removed from <entry-dir>/dump/:
3814
-
3815
- report log markdown agent
3816
-
3817
- Init flags (--init):
3818
-
3819
- --output <string> Target directory name (default: backtest-kit-project)
3820
-
3821
- Scaffolds a project and runs scripts/fetch_docs.mjs to download library docs.
3822
-
3823
- Docker flags (--docker):
3824
-
3825
- --output <string> Target directory name (default: backtest-kit-docker)
3826
-
3827
- Scaffolds a Docker workspace: docker-compose.yaml, .env.example, package.json,
3828
- tsconfig.json, and a sample strategy under content/. Run npm install then
3829
- docker compose up to start the container.
3830
-
3831
- Module hooks (loaded automatically by each mode):
3832
-
3833
- modules/backtest.module --backtest Broker adapter for backtest
3834
- modules/walker.module --walker Broker adapter for walker comparison
3835
- modules/paper.module --paper Broker adapter for paper trading
3836
- modules/live.module --live Broker adapter for live trading
3837
- modules/pine.module --pine Exchange schema for PineScript runs
3838
- modules/editor.module --editor Exchange schema for the visual Pine editor
3839
- modules/dump.module --dump Exchange schema for candle dumps
3840
- modules/pnldebug.module --pnldebug Exchange schema for PnL debug runs
3841
- modules/brokerdebug.module --brokerdebug Broker adapter used for broker commit testing
3842
-
3843
- --flush has no associated module. It only removes dump subdirectories.
3844
-
3845
- Extensions .ts, .mjs, .cjs are tried automatically. Missing module = soft warning.
3846
-
3847
- Environment variables:
3848
-
3849
- CC_TELEGRAM_TOKEN Telegram bot token (required for --telegram)
3850
- CC_TELEGRAM_CHANNEL Telegram channel or chat ID (required for --telegram)
3851
- CC_WWWROOT_HOST UI server bind address (default: 0.0.0.0)
3852
- CC_WWWROOT_PORT UI server port (default: 60050)
3853
-
3854
- Examples:
3855
-
3856
- node ${ENTRY_PATH} --backtest ./content/feb_2026.strategy.ts
3857
- node ${ENTRY_PATH} --backtest --symbol BTCUSDT --noCache --noFlush --ui ./content/feb_2026.strategy.ts
3858
- node ${ENTRY_PATH} --walker ./content/feb_2026_v1.strategy.ts ./content/feb_2026_v2.strategy.ts ./content/feb_2026_v3.strategy.ts
3859
- node ${ENTRY_PATH} --walker --symbol BTCUSDT --noCache --noFlush --markdown ./content/feb_2026_v1.ts ./content/feb_2026_v2.ts
3860
- node ${ENTRY_PATH} --paper --symbol ETHUSDT ./content/feb_2026.strategy.ts
3861
- node ${ENTRY_PATH} --live --ui --telegram ./content/feb_2026.strategy.ts
3862
- node ${ENTRY_PATH} --pine ./math/feb_2026.pine --timeframe 15m --limit 500 --jsonl
3863
- node ${ENTRY_PATH} --editor
3864
- node ${ENTRY_PATH} --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl
3865
- node ${ENTRY_PATH} --pnldebug --symbol BTCUSDT --priceopen 64069.50 --direction short --when "2025-02-25" --minutes 120
3866
- node ${ENTRY_PATH} --pnldebug --priceopen 67956.73 --direction long --when 1772064000000 --minutes 60 --markdown
3867
- node ${ENTRY_PATH} --brokerdebug --commit signal-open --symbol BTCUSDT
3868
- node ${ENTRY_PATH} --brokerdebug --commit partial-profit --symbol ETHUSDT
3869
- node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts
3870
- node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts ./content/feb_2026.strategy/feb_2026.test.ts
3871
- node ${ENTRY_PATH} --init --output my-trading-bot
3872
- node ${ENTRY_PATH} --docker --output my-docker-workspace
3825
+ const HELP_TEXT = `
3826
+ Usage:
3827
+ node index.mjs --<mode> [flags] [entry-point]
3828
+
3829
+ Modes:
3830
+
3831
+ --backtest <entry> Run strategy against historical candle data
3832
+ --walker <entry...> Run Walker A/B strategy comparison across multiple strategies
3833
+ --paper <entry> Paper trading (live prices, no real orders)
3834
+ --live <entry> Live trading with real orders
3835
+ --pine <entry> Execute a local .pine indicator file
3836
+ --editor Open the Pine Script visual editor in the browser
3837
+ --dump Fetch and save raw OHLCV candles
3838
+ --pnldebug Simulate PnL per minute for a given entry price and direction
3839
+ --brokerdebug Fire a single broker commit against the live broker adapter
3840
+ --flush <entry...> Delete report/log/markdown/agent folders from strategy dump dir
3841
+ --init Scaffold a new project in the current directory
3842
+ --docker Scaffold a Docker workspace for running strategies in a container
3843
+ --help Print this help message
3844
+
3845
+ Backtest flags:
3846
+
3847
+ --symbol <string> Trading pair (default: BTCUSDT)
3848
+ --strategy <string> Strategy name from addStrategySchema (default: first registered)
3849
+ --exchange <string> Exchange name from addExchangeSchema (default: first registered)
3850
+ --frame <string> Frame name from addFrameSchema (default: first registered)
3851
+ --cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
3852
+ --noCache Skip candle cache warming before the run
3853
+ --noFlush Skip removing report/log/markdown/agent folders before backtest run
3854
+ --verbose Log every candle fetch to stdout
3855
+ --ui Start web dashboard at http://localhost:60050
3856
+ --telegram Send trade notifications to Telegram
3857
+
3858
+ Walker flags (--walker):
3859
+
3860
+ --symbol <string> Trading pair (default: BTCUSDT)
3861
+ --cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
3862
+ --noCache Skip candle cache warming before the run
3863
+ --noFlush Skip removing report/log/markdown/agent folders before walker run
3864
+ --verbose Log every candle fetch to stdout
3865
+ --output <string> Output file base name (default: walker_{SYMBOL}_{TIMESTAMP})
3866
+ --json Save results as JSON to ./dump/<output>.json
3867
+ --markdown Save report as Markdown to ./dump/<output>.md
3868
+
3869
+ Each positional argument is a strategy entry point. All strategy files are loaded without
3870
+ changing process.cwd() — .env is read from the working directory only.
3871
+ addWalkerSchema is called automatically using the registered exchange and frame.
3872
+ After comparison completes the report is printed to stdout (or saved if --json/--markdown).
3873
+
3874
+ Module file ./modules/walker.module is loaded automatically if it exists.
3875
+
3876
+ Paper / Live flags:
3877
+
3878
+ --symbol <string> Trading pair (default: BTCUSDT)
3879
+ --strategy <string> Strategy name (default: first registered)
3880
+ --exchange <string> Exchange name (default: first registered)
3881
+ --verbose Log every candle fetch to stdout
3882
+ --ui Start web dashboard
3883
+ --telegram Send Telegram notifications
3884
+
3885
+ PineScript flags (--pine):
3886
+
3887
+ --symbol <string> Trading pair (default: BTCUSDT)
3888
+ --timeframe <string> Candle interval (default: 15m)
3889
+ --limit <string> Number of candles to fetch (default: 250)
3890
+ --when <string> End date — ISO 8601 or Unix ms (default: now)
3891
+ --exchange <string> Exchange name (default: first registered)
3892
+ --output <string> Output file base name without extension
3893
+ --json Save output as JSON array to <pine-dir>/dump/<output>.json
3894
+ --jsonl Save output as JSONL to <pine-dir>/dump/<output>.jsonl
3895
+ --markdown Save output as Markdown table to <pine-dir>/dump/<output>.md
3896
+
3897
+ Only plot() calls with display=display.data_window produce output columns.
3898
+ Module file ./modules/pine.module is loaded automatically if it exists.
3899
+
3900
+ Candle dump flags (--dump):
3901
+
3902
+ --symbol <string> Trading pair (default: BTCUSDT)
3903
+ --timeframe <string> Candle interval (default: 15m)
3904
+ --limit <string> Number of candles (default: 250)
3905
+ --when <string> End date — ISO 8601 or Unix ms (default: now)
3906
+ --exchange <string> Exchange name (default: first registered)
3907
+ --output <string> Output file base name (default: {SYMBOL}_{LIMIT}_{TIMEFRAME}_{TIMESTAMP})
3908
+ --json Save as JSON array to ./dump/<output>.json
3909
+ --jsonl Save as JSONL to ./dump/<output>.jsonl
3910
+
3911
+ Module file ./modules/dump.module is loaded automatically if it exists.
3912
+
3913
+ PnL debug flags (--pnldebug):
3914
+
3915
+ --symbol <string> Trading pair (default: BTCUSDT)
3916
+ --priceopen <number> Entry price (required)
3917
+ --direction <string> Position direction: long or short (default: long)
3918
+ --when <string> Start timestamp — ISO 8601 or Unix ms (default: now)
3919
+ --minutes <string> Number of 1m candles to simulate (default: 60)
3920
+ --exchange <string> Exchange name (default: first registered)
3921
+ --output <string> Output file base name (default: {SYMBOL}_{DIRECTION}_{PRICEOPEN}_{TIMESTAMP})
3922
+ --json Save as JSON array to ./dump/<output>.json
3923
+ --jsonl Save as JSONL to ./dump/<output>.jsonl
3924
+ --markdown Save as Markdown table to ./dump/<output>.md
3925
+
3926
+ Module file ./modules/pnldebug.module is loaded automatically if it exists.
3927
+
3928
+ Broker debug flags (--brokerdebug):
3929
+
3930
+ --symbol <string> Trading pair (default: BTCUSDT)
3931
+ --exchange <string> Exchange name (default: first registered)
3932
+ --commit <string> Commit type to fire: signal-open, signal-close, partial-profit,
3933
+ partial-loss, average-buy, trailing-stop, trailing-take, breakeven
3934
+ (default: signal-open)
3935
+
3936
+ Loads ./live.module, fetches the last candle for --symbol/--timeframe, and calls
3937
+ the selected broker commit with synthetic payload values derived from current price.
3938
+
3939
+ Flush flags (--flush):
3940
+
3941
+ One or more positional entry points. For each entry point the following
3942
+ subdirectories are removed from <entry-dir>/dump/:
3943
+
3944
+ report log markdown agent
3945
+
3946
+ Init flags (--init):
3947
+
3948
+ --output <string> Target directory name (default: backtest-kit-project)
3949
+
3950
+ Scaffolds a project and runs scripts/fetch_docs.mjs to download library docs.
3951
+
3952
+ Docker flags (--docker):
3953
+
3954
+ --output <string> Target directory name (default: backtest-kit-docker)
3955
+
3956
+ Scaffolds a Docker workspace: docker-compose.yaml, .env.example, package.json,
3957
+ tsconfig.json, and a sample strategy under content/. Run npm install then
3958
+ docker compose up to start the container.
3959
+
3960
+ Module hooks (loaded automatically by each mode):
3961
+
3962
+ modules/backtest.module --backtest Broker adapter for backtest
3963
+ modules/walker.module --walker Broker adapter for walker comparison
3964
+ modules/paper.module --paper Broker adapter for paper trading
3965
+ modules/live.module --live Broker adapter for live trading
3966
+ modules/pine.module --pine Exchange schema for PineScript runs
3967
+ modules/editor.module --editor Exchange schema for the visual Pine editor
3968
+ modules/dump.module --dump Exchange schema for candle dumps
3969
+ modules/pnldebug.module --pnldebug Exchange schema for PnL debug runs
3970
+ modules/brokerdebug.module --brokerdebug Broker adapter used for broker commit testing
3971
+
3972
+ --flush has no associated module. It only removes dump subdirectories.
3973
+
3974
+ Extensions .ts, .mjs, .cjs are tried automatically. Missing module = soft warning.
3975
+
3976
+ Environment variables:
3977
+
3978
+ CC_TELEGRAM_TOKEN Telegram bot token (required for --telegram)
3979
+ CC_TELEGRAM_CHANNEL Telegram channel or chat ID (required for --telegram)
3980
+ CC_WWWROOT_HOST UI server bind address (default: 0.0.0.0)
3981
+ CC_WWWROOT_PORT UI server port (default: 60050)
3982
+
3983
+ Examples:
3984
+
3985
+ node ${ENTRY_PATH} --backtest ./content/feb_2026.strategy.ts
3986
+ node ${ENTRY_PATH} --backtest --symbol BTCUSDT --noCache --noFlush --ui ./content/feb_2026.strategy.ts
3987
+ node ${ENTRY_PATH} --walker ./content/feb_2026_v1.strategy.ts ./content/feb_2026_v2.strategy.ts ./content/feb_2026_v3.strategy.ts
3988
+ node ${ENTRY_PATH} --walker --symbol BTCUSDT --noCache --noFlush --markdown ./content/feb_2026_v1.ts ./content/feb_2026_v2.ts
3989
+ node ${ENTRY_PATH} --paper --symbol ETHUSDT ./content/feb_2026.strategy.ts
3990
+ node ${ENTRY_PATH} --live --ui --telegram ./content/feb_2026.strategy.ts
3991
+ node ${ENTRY_PATH} --pine ./math/feb_2026.pine --timeframe 15m --limit 500 --jsonl
3992
+ node ${ENTRY_PATH} --editor
3993
+ node ${ENTRY_PATH} --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl
3994
+ node ${ENTRY_PATH} --pnldebug --symbol BTCUSDT --priceopen 64069.50 --direction short --when "2025-02-25" --minutes 120
3995
+ node ${ENTRY_PATH} --pnldebug --priceopen 67956.73 --direction long --when 1772064000000 --minutes 60 --markdown
3996
+ node ${ENTRY_PATH} --brokerdebug --commit signal-open --symbol BTCUSDT
3997
+ node ${ENTRY_PATH} --brokerdebug --commit partial-profit --symbol ETHUSDT
3998
+ node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts
3999
+ node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts ./content/feb_2026.strategy/feb_2026.test.ts
4000
+ node ${ENTRY_PATH} --init --output my-trading-bot
4001
+ node ${ENTRY_PATH} --docker --output my-docker-workspace
3873
4002
  `.trimStart();
3874
4003
  const main$1 = async () => {
3875
4004
  if (!getEntry(import.meta.url)) {
@@ -3879,7 +4008,7 @@ const main$1 = async () => {
3879
4008
  if (!values.help) {
3880
4009
  return;
3881
4010
  }
3882
- process.stdout.write(`@backtest-kit/cli ${"8.4.3"}\n\n`);
4011
+ process.stdout.write(`@backtest-kit/cli ${"9.0.0"}\n\n`);
3883
4012
  process.stdout.write(HELP_TEXT);
3884
4013
  process.exit(0);
3885
4014
  };
@@ -3893,7 +4022,7 @@ const main = async () => {
3893
4022
  if (!values.version) {
3894
4023
  return;
3895
4024
  }
3896
- process.stdout.write(`@backtest-kit/cli ${"8.4.3"}\n`);
4025
+ process.stdout.write(`@backtest-kit/cli ${"9.0.0"}\n`);
3897
4026
  process.exit(0);
3898
4027
  };
3899
4028
  main();