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