@backtest-kit/cli 6.14.0 → 6.15.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 +57 -2
- package/build/index.cjs +82 -9
- package/build/index.mjs +85 -12
- package/package.json +13 -13
- package/template/project/package.mustache +5 -5
package/README.md
CHANGED
|
@@ -41,6 +41,7 @@ Point the CLI at your strategy file, choose a mode, and it handles exchange conn
|
|
|
41
41
|
| **Telegram** | `--telegram` | Trade notifications with price charts |
|
|
42
42
|
| **PineScript** | `--pine` | Run a local `.pine` indicator against exchange data |
|
|
43
43
|
| **Candle Dump** | `--dump` | Fetch and save raw OHLCV candles to a file |
|
|
44
|
+
| **Flush** | `--flush` | Delete report/log/markdown/agent folders from strategy dump dir |
|
|
44
45
|
| **Init Project** | `--init` | Scaffold a new backtest-kit project |
|
|
45
46
|
|
|
46
47
|
## 🚀 Installation
|
|
@@ -144,6 +145,7 @@ npm start -- --symbol BTCUSDT --ui
|
|
|
144
145
|
| `--telegram` | boolean | Enable Telegram notifications (default: `false`) |
|
|
145
146
|
| `--verbose` | boolean | Log each candle fetch (default: `false`) |
|
|
146
147
|
| `--noCache` | boolean | Skip candle cache warming before backtest (default: `false`) |
|
|
148
|
+
| `--noFlush` | boolean | Skip removing report/log/markdown/agent folders before backtest run (default: `false`) |
|
|
147
149
|
| `--symbol` | string | Trading pair (default: `"BTCUSDT"`) |
|
|
148
150
|
| `--strategy` | string | Strategy name (default: first registered) |
|
|
149
151
|
| `--exchange` | string | Exchange name (default: first registered) |
|
|
@@ -178,7 +180,7 @@ Runs the strategy against historical candle data using a registered `FrameSchema
|
|
|
178
180
|
npm run backtest
|
|
179
181
|
```
|
|
180
182
|
|
|
181
|
-
Before running, the CLI warms the candle cache for every interval in `--cacheInterval`. On the next run, cached data is used directly — no API calls needed. Pass `--noCache` to skip
|
|
183
|
+
Before running, the CLI removes the `report`, `log`, `markdown`, and `agent` folders from the strategy's `dump/` directory, then warms the candle cache for every interval in `--cacheInterval`. On the next run, cached data is used directly — no API calls needed. Pass `--noCache` to skip cache warming, `--noFlush` to keep existing output folders.
|
|
182
184
|
|
|
183
185
|
### Paper Trading
|
|
184
186
|
|
|
@@ -228,7 +230,7 @@ Runs the same historical period against multiple strategy files and prints a ran
|
|
|
228
230
|
npm run walker
|
|
229
231
|
```
|
|
230
232
|
|
|
231
|
-
Each positional argument is a separate strategy entry point. All files are loaded without changing `process.cwd()` — `.env` is read from the working directory only. After loading, `addWalkerSchema` is called automatically using the exchange and frame registered by the strategy files.
|
|
233
|
+
Each positional argument is a separate strategy entry point. Before loading them, the CLI removes the `report`, `log`, `markdown`, and `agent` folders from each entry point's `dump/` directory. Pass `--noFlush` to keep existing output. All files are loaded without changing `process.cwd()` — `.env` is read from the working directory only. After loading, `addWalkerSchema` is called automatically using the exchange and frame registered by the strategy files.
|
|
232
234
|
|
|
233
235
|
If no frame is registered, the CLI falls back to the last 31 days from `Date.now()` with a console warning.
|
|
234
236
|
|
|
@@ -240,6 +242,7 @@ If no frame is registered, the CLI falls back to the last 31 days from `Date.now
|
|
|
240
242
|
| `--symbol` | string | Trading pair (default: `"BTCUSDT"`) |
|
|
241
243
|
| `--cacheInterval` | string | Intervals to pre-cache (default: `"1m, 15m, 30m, 4h"`) |
|
|
242
244
|
| `--noCache` | boolean | Skip candle cache warming (default: `false`) |
|
|
245
|
+
| `--noFlush` | boolean | Skip removing report/log/markdown/agent folders before walker run (default: `false`) |
|
|
243
246
|
| `--verbose` | boolean | Log each candle fetch and strategy progress (default: `false`) |
|
|
244
247
|
| `--output` | string | Output file base name (default: `walker_{SYMBOL}_{TIMESTAMP}`) |
|
|
245
248
|
| `--json` | boolean | Save results as JSON to `./dump/<output>.json` |
|
|
@@ -828,6 +831,57 @@ Or add it to `package.json`:
|
|
|
828
831
|
npx @backtest-kit/cli --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl
|
|
829
832
|
```
|
|
830
833
|
|
|
834
|
+
## 🗑️ Flushing Strategy Output (`--flush`)
|
|
835
|
+
|
|
836
|
+
`@backtest-kit/cli` can delete generated output folders from one or more strategy dump directories without touching cached candle data.
|
|
837
|
+
|
|
838
|
+
### CLI Flags
|
|
839
|
+
|
|
840
|
+
| Flag | Type | Description |
|
|
841
|
+
|------|------|-------------|
|
|
842
|
+
| `--flush` | boolean | Enable flush mode |
|
|
843
|
+
|
|
844
|
+
**Positional arguments (required):** one or more strategy entry point files. For each entry point the CLI resolves its directory and removes the following subdirectories from `<entry-dir>/dump/`:
|
|
845
|
+
|
|
846
|
+
| Folder | Contents |
|
|
847
|
+
|--------|----------|
|
|
848
|
+
| `report` | Backtest report files (`.jsonl`) |
|
|
849
|
+
| `log` | Run logs (`log.jsonl`) |
|
|
850
|
+
| `markdown` | Exported Markdown reports |
|
|
851
|
+
| `agent` | Agent outline files |
|
|
852
|
+
|
|
853
|
+
Candle cache (`dump/data/`) and AI forecast outlines (`dump/outline/`) are **not** removed.
|
|
854
|
+
|
|
855
|
+
### Usage
|
|
856
|
+
|
|
857
|
+
Flush a single strategy:
|
|
858
|
+
|
|
859
|
+
```bash
|
|
860
|
+
npx @backtest-kit/cli --flush ./content/feb_2026.strategy/modules/backtest.module.ts
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
Flush multiple strategies at once:
|
|
864
|
+
|
|
865
|
+
```bash
|
|
866
|
+
npx @backtest-kit/cli --flush \
|
|
867
|
+
./content/feb_2026.strategy/modules/backtest.module.ts \
|
|
868
|
+
./content/mar_2026.strategy/modules/backtest.module.ts
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
Or add it to `package.json`:
|
|
872
|
+
|
|
873
|
+
```json
|
|
874
|
+
{
|
|
875
|
+
"scripts": {
|
|
876
|
+
"flush": "npx @backtest-kit/cli --flush ./content/feb_2026.strategy/modules/backtest.module.ts"
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
```bash
|
|
882
|
+
npm run flush
|
|
883
|
+
```
|
|
884
|
+
|
|
831
885
|
## 🗂️ Scaffolding a New Project (`--init`)
|
|
832
886
|
|
|
833
887
|
`@backtest-kit/cli` can bootstrap a ready-to-use project directory with a pre-configured layout, example strategy files, and all documentation fetched automatically.
|
|
@@ -985,6 +1039,7 @@ await run(mode, args);
|
|
|
985
1039
|
| `frame` | `string` | Frame name (default: first registered) |
|
|
986
1040
|
| `cacheInterval` | `CandleInterval[]` | Intervals to pre-cache (default: `["1m","15m","30m","1h","4h"]`) |
|
|
987
1041
|
| `noCache` | `boolean` | Skip candle cache warming (default: `false`) |
|
|
1042
|
+
| `noFlush` | `boolean` | Skip removing report/log/markdown/agent folders before the run (default: `false`) |
|
|
988
1043
|
| `verbose` | `boolean` | Log each candle fetch (default: `false`) |
|
|
989
1044
|
|
|
990
1045
|
**Paper** and **Live** (`mode: "paper"` / `mode: "live"`):
|
package/build/index.cjs
CHANGED
|
@@ -473,6 +473,10 @@ const getArgs = functoolsKit.singleshot(() => {
|
|
|
473
473
|
type: "boolean",
|
|
474
474
|
default: false,
|
|
475
475
|
},
|
|
476
|
+
noFlush: {
|
|
477
|
+
type: "boolean",
|
|
478
|
+
default: false,
|
|
479
|
+
},
|
|
476
480
|
cacheInterval: {
|
|
477
481
|
type: "string",
|
|
478
482
|
default: "1m, 15m, 30m, 4h",
|
|
@@ -486,6 +490,10 @@ const getArgs = functoolsKit.singleshot(() => {
|
|
|
486
490
|
type: "boolean",
|
|
487
491
|
default: false,
|
|
488
492
|
},
|
|
493
|
+
flush: {
|
|
494
|
+
type: "boolean",
|
|
495
|
+
default: false,
|
|
496
|
+
},
|
|
489
497
|
timeframe: {
|
|
490
498
|
type: "string",
|
|
491
499
|
default: "",
|
|
@@ -2461,14 +2469,27 @@ const cli = {
|
|
|
2461
2469
|
};
|
|
2462
2470
|
init();
|
|
2463
2471
|
|
|
2472
|
+
const NOTIFICATION_CONFIG = {
|
|
2473
|
+
signal: true,
|
|
2474
|
+
risk: true,
|
|
2475
|
+
info: true,
|
|
2476
|
+
breakeven: true,
|
|
2477
|
+
common_error: true,
|
|
2478
|
+
critical_error: true,
|
|
2479
|
+
validation_error: true,
|
|
2480
|
+
partial_loss: false,
|
|
2481
|
+
partial_profit: false,
|
|
2482
|
+
signal_sync: false,
|
|
2483
|
+
strategy_commit: true,
|
|
2484
|
+
};
|
|
2464
2485
|
class SetupUtils {
|
|
2465
2486
|
constructor() {
|
|
2466
2487
|
this.enable = functoolsKit.singleshot(() => {
|
|
2467
2488
|
cli.loggerService.debug("SetupUtils enable");
|
|
2489
|
+
BacktestKit.Notification.enable(NOTIFICATION_CONFIG);
|
|
2468
2490
|
{
|
|
2469
2491
|
BacktestKit.Recent.enable();
|
|
2470
2492
|
BacktestKit.Storage.enable();
|
|
2471
|
-
BacktestKit.Notification.enable();
|
|
2472
2493
|
}
|
|
2473
2494
|
{
|
|
2474
2495
|
BacktestKit.Markdown.enable();
|
|
@@ -2590,14 +2611,14 @@ BacktestKit.setConfig({
|
|
|
2590
2611
|
CC_WALKER_MARKDOWN_TOP_N: 10,
|
|
2591
2612
|
});
|
|
2592
2613
|
|
|
2593
|
-
const MODES = ["backtest", "walker", "paper", "live", "pine", "dump", "init", "help", "version"];
|
|
2614
|
+
const MODES = ["backtest", "walker", "paper", "live", "pine", "dump", "flush", "init", "help", "version"];
|
|
2594
2615
|
const ENTRY_PATH$1 = "./node_modules/@backtest-kit/cli/build/index.mjs";
|
|
2595
2616
|
const HELP_TEXT$1 = `
|
|
2596
2617
|
Example:
|
|
2597
2618
|
|
|
2598
2619
|
node ${ENTRY_PATH$1} --help
|
|
2599
2620
|
`.trimStart();
|
|
2600
|
-
const main$
|
|
2621
|
+
const main$c = async () => {
|
|
2601
2622
|
if (!getEntry((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))) {
|
|
2602
2623
|
return;
|
|
2603
2624
|
}
|
|
@@ -2605,19 +2626,48 @@ const main$b = async () => {
|
|
|
2605
2626
|
if (MODES.some((mode) => values[mode])) {
|
|
2606
2627
|
return;
|
|
2607
2628
|
}
|
|
2608
|
-
process.stdout.write(`@backtest-kit/cli ${"6.
|
|
2629
|
+
process.stdout.write(`@backtest-kit/cli ${"6.15.0"}\n`);
|
|
2609
2630
|
process.stdout.write("\n");
|
|
2610
2631
|
process.stdout.write(`Run with --help to see available commands.\n`);
|
|
2611
2632
|
process.stdout.write("\n");
|
|
2612
2633
|
process.stdout.write(HELP_TEXT$1);
|
|
2613
2634
|
process.exit(0);
|
|
2614
2635
|
};
|
|
2615
|
-
main$
|
|
2636
|
+
main$c();
|
|
2616
2637
|
|
|
2617
2638
|
const notifyShutdown = functoolsKit.singleshot(async () => {
|
|
2618
2639
|
console.log("Graceful shutdown initiated. Press Ctrl+C again to force quit.");
|
|
2619
2640
|
});
|
|
2620
2641
|
|
|
2642
|
+
const FLUSH_DIRS = ["report", "log", "markdown", "agent"];
|
|
2643
|
+
const flush = async (entryPoint) => {
|
|
2644
|
+
const moduleRoot = path.dirname(path.resolve(process.cwd(), entryPoint));
|
|
2645
|
+
const dumpDir = path.join(moduleRoot, "dump");
|
|
2646
|
+
for (const dir of FLUSH_DIRS) {
|
|
2647
|
+
const target = path.join(dumpDir, dir);
|
|
2648
|
+
await fs$1.rm(target, { recursive: true, force: true });
|
|
2649
|
+
console.log(`Removed: ${target}`);
|
|
2650
|
+
}
|
|
2651
|
+
};
|
|
2652
|
+
const main$b = async () => {
|
|
2653
|
+
if (!getEntry((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))) {
|
|
2654
|
+
return;
|
|
2655
|
+
}
|
|
2656
|
+
const { values } = getArgs();
|
|
2657
|
+
if (!values.flush) {
|
|
2658
|
+
return;
|
|
2659
|
+
}
|
|
2660
|
+
const entryPoints = getPositionals();
|
|
2661
|
+
if (!entryPoints.length) {
|
|
2662
|
+
throw new Error("Entry point is required");
|
|
2663
|
+
}
|
|
2664
|
+
for (const entryPoint of entryPoints) {
|
|
2665
|
+
await flush(entryPoint);
|
|
2666
|
+
}
|
|
2667
|
+
process.exit(0);
|
|
2668
|
+
};
|
|
2669
|
+
main$b();
|
|
2670
|
+
|
|
2621
2671
|
const BEFORE_EXIT_FN$5 = functoolsKit.singleshot(async () => {
|
|
2622
2672
|
process.off("SIGINT", BEFORE_EXIT_FN$5);
|
|
2623
2673
|
const [running = null] = await BacktestKit.Backtest.list();
|
|
@@ -2646,6 +2696,10 @@ const main$a = async () => {
|
|
|
2646
2696
|
if (!values.backtest) {
|
|
2647
2697
|
return;
|
|
2648
2698
|
}
|
|
2699
|
+
if (!values.noFlush) {
|
|
2700
|
+
const [entryPoint = null] = getPositionals();
|
|
2701
|
+
entryPoint && await flush(entryPoint);
|
|
2702
|
+
}
|
|
2649
2703
|
await cli.backtestMainService.connect();
|
|
2650
2704
|
listenGracefulShutdown$5();
|
|
2651
2705
|
};
|
|
@@ -2675,6 +2729,11 @@ const main$9 = async () => {
|
|
|
2675
2729
|
if (!values.walker) {
|
|
2676
2730
|
return;
|
|
2677
2731
|
}
|
|
2732
|
+
if (!values.noFlush) {
|
|
2733
|
+
for (const entryPoint of getPositionals()) {
|
|
2734
|
+
await flush(entryPoint);
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2678
2737
|
listenGracefulShutdown$4();
|
|
2679
2738
|
await cli.walkerMainService.connect();
|
|
2680
2739
|
};
|
|
@@ -3060,6 +3119,7 @@ Modes:
|
|
|
3060
3119
|
--live <entry> Live trading with real orders
|
|
3061
3120
|
--pine <entry> Execute a local .pine indicator file
|
|
3062
3121
|
--dump Fetch and save raw OHLCV candles
|
|
3122
|
+
--flush <entry...> Delete report/log/markdown/agent folders from strategy dump dir
|
|
3063
3123
|
--init Scaffold a new project in the current directory
|
|
3064
3124
|
--help Print this help message
|
|
3065
3125
|
|
|
@@ -3071,6 +3131,7 @@ Backtest flags:
|
|
|
3071
3131
|
--frame <string> Frame name from addFrameSchema (default: first registered)
|
|
3072
3132
|
--cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
|
|
3073
3133
|
--noCache Skip candle cache warming before the run
|
|
3134
|
+
--noFlush Skip removing report/log/markdown/agent folders before backtest run
|
|
3074
3135
|
--verbose Log every candle fetch to stdout
|
|
3075
3136
|
--ui Start web dashboard at http://localhost:60050
|
|
3076
3137
|
--telegram Send trade notifications to Telegram
|
|
@@ -3080,6 +3141,7 @@ Walker flags (--walker):
|
|
|
3080
3141
|
--symbol <string> Trading pair (default: BTCUSDT)
|
|
3081
3142
|
--cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
|
|
3082
3143
|
--noCache Skip candle cache warming before the run
|
|
3144
|
+
--noFlush Skip removing report/log/markdown/agent folders before walker run
|
|
3083
3145
|
--verbose Log every candle fetch to stdout
|
|
3084
3146
|
--output <string> Output file base name (default: walker_{SYMBOL}_{TIMESTAMP})
|
|
3085
3147
|
--json Save results as JSON to ./dump/<output>.json
|
|
@@ -3129,6 +3191,13 @@ Candle dump flags (--dump):
|
|
|
3129
3191
|
|
|
3130
3192
|
Module file ./modules/dump.module is loaded automatically if it exists.
|
|
3131
3193
|
|
|
3194
|
+
Flush flags (--flush):
|
|
3195
|
+
|
|
3196
|
+
One or more positional entry points. For each entry point the following
|
|
3197
|
+
subdirectories are removed from <entry-dir>/dump/:
|
|
3198
|
+
|
|
3199
|
+
report log markdown agent
|
|
3200
|
+
|
|
3132
3201
|
Init flags (--init):
|
|
3133
3202
|
|
|
3134
3203
|
--output <string> Target directory name (default: backtest-kit-project)
|
|
@@ -3144,6 +3213,8 @@ Module hooks (loaded automatically by each mode):
|
|
|
3144
3213
|
modules/pine.module --pine Exchange schema for PineScript runs
|
|
3145
3214
|
modules/dump.module --dump Exchange schema for candle dumps
|
|
3146
3215
|
|
|
3216
|
+
--flush has no associated module. It only removes dump subdirectories.
|
|
3217
|
+
|
|
3147
3218
|
Extensions .ts, .mjs, .cjs are tried automatically. Missing module = soft warning.
|
|
3148
3219
|
|
|
3149
3220
|
Environment variables:
|
|
@@ -3156,13 +3227,15 @@ Environment variables:
|
|
|
3156
3227
|
Examples:
|
|
3157
3228
|
|
|
3158
3229
|
node ${ENTRY_PATH} --backtest ./content/feb_2026.strategy.ts
|
|
3159
|
-
node ${ENTRY_PATH} --backtest --symbol BTCUSDT --noCache --ui ./content/feb_2026.strategy.ts
|
|
3230
|
+
node ${ENTRY_PATH} --backtest --symbol BTCUSDT --noCache --noFlush --ui ./content/feb_2026.strategy.ts
|
|
3160
3231
|
node ${ENTRY_PATH} --walker ./content/feb_2026_v1.strategy.ts ./content/feb_2026_v2.strategy.ts ./content/feb_2026_v3.strategy.ts
|
|
3161
|
-
node ${ENTRY_PATH} --walker --symbol BTCUSDT --noCache --markdown ./content/feb_2026_v1.ts ./content/feb_2026_v2.ts
|
|
3232
|
+
node ${ENTRY_PATH} --walker --symbol BTCUSDT --noCache --noFlush --markdown ./content/feb_2026_v1.ts ./content/feb_2026_v2.ts
|
|
3162
3233
|
node ${ENTRY_PATH} --paper --symbol ETHUSDT ./content/feb_2026.strategy.ts
|
|
3163
3234
|
node ${ENTRY_PATH} --live --ui --telegram ./content/feb_2026.strategy.ts
|
|
3164
3235
|
node ${ENTRY_PATH} --pine ./math/feb_2026.pine --timeframe 15m --limit 500 --jsonl
|
|
3165
3236
|
node ${ENTRY_PATH} --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl
|
|
3237
|
+
node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts
|
|
3238
|
+
node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts ./content/feb_2026.strategy/feb_2026.test.ts
|
|
3166
3239
|
node ${ENTRY_PATH} --init --output my-trading-bot
|
|
3167
3240
|
`.trimStart();
|
|
3168
3241
|
const main$1 = async () => {
|
|
@@ -3173,7 +3246,7 @@ const main$1 = async () => {
|
|
|
3173
3246
|
if (!values.help) {
|
|
3174
3247
|
return;
|
|
3175
3248
|
}
|
|
3176
|
-
process.stdout.write(`@backtest-kit/cli ${"6.
|
|
3249
|
+
process.stdout.write(`@backtest-kit/cli ${"6.15.0"}\n\n`);
|
|
3177
3250
|
process.stdout.write(HELP_TEXT);
|
|
3178
3251
|
process.exit(0);
|
|
3179
3252
|
};
|
|
@@ -3187,7 +3260,7 @@ const main = async () => {
|
|
|
3187
3260
|
if (!values.version) {
|
|
3188
3261
|
return;
|
|
3189
3262
|
}
|
|
3190
|
-
process.stdout.write(`@backtest-kit/cli ${"6.
|
|
3263
|
+
process.stdout.write(`@backtest-kit/cli ${"6.15.0"}\n`);
|
|
3191
3264
|
process.exit(0);
|
|
3192
3265
|
};
|
|
3193
3266
|
main();
|
package/build/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as BacktestKit from 'backtest-kit';
|
|
3
|
-
import { Log, listExchangeSchema, addExchangeSchema, roundTicks, listFrameSchema, addFrameSchema, listenDoneLive, listenDoneBacktest, shutdown, listenSignal, listStrategySchema, overrideExchangeSchema, Backtest, Session, Cache, Interval, alignToInterval, addWalkerSchema, overrideWalkerSchema, Walker, listenDoneWalker, Live, getCandles, checkCandles, warmCandles, listenRisk, listenStrategyCommit, listenSync, Recent, Storage,
|
|
3
|
+
import { Log, listExchangeSchema, addExchangeSchema, roundTicks, listFrameSchema, addFrameSchema, listenDoneLive, listenDoneBacktest, shutdown, listenSignal, listStrategySchema, overrideExchangeSchema, Backtest, Session, Cache, Interval, alignToInterval, addWalkerSchema, overrideWalkerSchema, Walker, listenDoneWalker, Live, getCandles, checkCandles, warmCandles, listenRisk, listenStrategyCommit, listenSync, Notification, Recent, Storage, Markdown, Report, Dump, Memory, StorageLive, StorageBacktest, RecentLive, RecentBacktest, NotificationLive, NotificationBacktest, MarkdownWriter, ReportWriter, PersistSignalAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistPartialAdapter, PersistBreakevenAdapter, PersistCandleAdapter, PersistStorageAdapter, PersistNotificationAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistIntervalAdapter, PersistMemoryAdapter, PersistRecentAdapter, setConfig, Exchange } from 'backtest-kit';
|
|
4
4
|
import { getErrorMessage, errorData, singleshot, str, BehaviorSubject, compose, createAwaiter, execpool, queued, sleep, randomString, TIMEOUT_SYMBOL, typo, retry, trycatch, memoize } from 'functools-kit';
|
|
5
5
|
import fs, { constants } from 'fs';
|
|
6
6
|
import * as stackTrace from 'stack-trace';
|
|
7
|
-
import path, { join, resolve, basename, extname
|
|
8
|
-
import fs$1, { access, readFile, mkdir, writeFile, readdir, copyFile } from 'fs/promises';
|
|
7
|
+
import path, { join, resolve, dirname, basename, extname } from 'path';
|
|
8
|
+
import fs$1, { access, readFile, mkdir, writeFile, rm, readdir, copyFile } from 'fs/promises';
|
|
9
9
|
import dotenv from 'dotenv';
|
|
10
10
|
import { createActivator } from 'di-kit';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
@@ -448,6 +448,10 @@ const getArgs = singleshot(() => {
|
|
|
448
448
|
type: "boolean",
|
|
449
449
|
default: false,
|
|
450
450
|
},
|
|
451
|
+
noFlush: {
|
|
452
|
+
type: "boolean",
|
|
453
|
+
default: false,
|
|
454
|
+
},
|
|
451
455
|
cacheInterval: {
|
|
452
456
|
type: "string",
|
|
453
457
|
default: "1m, 15m, 30m, 4h",
|
|
@@ -461,6 +465,10 @@ const getArgs = singleshot(() => {
|
|
|
461
465
|
type: "boolean",
|
|
462
466
|
default: false,
|
|
463
467
|
},
|
|
468
|
+
flush: {
|
|
469
|
+
type: "boolean",
|
|
470
|
+
default: false,
|
|
471
|
+
},
|
|
464
472
|
timeframe: {
|
|
465
473
|
type: "string",
|
|
466
474
|
default: "",
|
|
@@ -2432,14 +2440,27 @@ const cli = {
|
|
|
2432
2440
|
};
|
|
2433
2441
|
init();
|
|
2434
2442
|
|
|
2443
|
+
const NOTIFICATION_CONFIG = {
|
|
2444
|
+
signal: true,
|
|
2445
|
+
risk: true,
|
|
2446
|
+
info: true,
|
|
2447
|
+
breakeven: true,
|
|
2448
|
+
common_error: true,
|
|
2449
|
+
critical_error: true,
|
|
2450
|
+
validation_error: true,
|
|
2451
|
+
partial_loss: false,
|
|
2452
|
+
partial_profit: false,
|
|
2453
|
+
signal_sync: false,
|
|
2454
|
+
strategy_commit: true,
|
|
2455
|
+
};
|
|
2435
2456
|
class SetupUtils {
|
|
2436
2457
|
constructor() {
|
|
2437
2458
|
this.enable = singleshot(() => {
|
|
2438
2459
|
cli.loggerService.debug("SetupUtils enable");
|
|
2460
|
+
Notification.enable(NOTIFICATION_CONFIG);
|
|
2439
2461
|
{
|
|
2440
2462
|
Recent.enable();
|
|
2441
2463
|
Storage.enable();
|
|
2442
|
-
Notification.enable();
|
|
2443
2464
|
}
|
|
2444
2465
|
{
|
|
2445
2466
|
Markdown.enable();
|
|
@@ -2561,14 +2582,14 @@ setConfig({
|
|
|
2561
2582
|
CC_WALKER_MARKDOWN_TOP_N: 10,
|
|
2562
2583
|
});
|
|
2563
2584
|
|
|
2564
|
-
const MODES = ["backtest", "walker", "paper", "live", "pine", "dump", "init", "help", "version"];
|
|
2585
|
+
const MODES = ["backtest", "walker", "paper", "live", "pine", "dump", "flush", "init", "help", "version"];
|
|
2565
2586
|
const ENTRY_PATH$1 = "./node_modules/@backtest-kit/cli/build/index.mjs";
|
|
2566
2587
|
const HELP_TEXT$1 = `
|
|
2567
2588
|
Example:
|
|
2568
2589
|
|
|
2569
2590
|
node ${ENTRY_PATH$1} --help
|
|
2570
2591
|
`.trimStart();
|
|
2571
|
-
const main$
|
|
2592
|
+
const main$c = async () => {
|
|
2572
2593
|
if (!getEntry(import.meta.url)) {
|
|
2573
2594
|
return;
|
|
2574
2595
|
}
|
|
@@ -2576,19 +2597,48 @@ const main$b = async () => {
|
|
|
2576
2597
|
if (MODES.some((mode) => values[mode])) {
|
|
2577
2598
|
return;
|
|
2578
2599
|
}
|
|
2579
|
-
process.stdout.write(`@backtest-kit/cli ${"6.
|
|
2600
|
+
process.stdout.write(`@backtest-kit/cli ${"6.15.0"}\n`);
|
|
2580
2601
|
process.stdout.write("\n");
|
|
2581
2602
|
process.stdout.write(`Run with --help to see available commands.\n`);
|
|
2582
2603
|
process.stdout.write("\n");
|
|
2583
2604
|
process.stdout.write(HELP_TEXT$1);
|
|
2584
2605
|
process.exit(0);
|
|
2585
2606
|
};
|
|
2586
|
-
main$
|
|
2607
|
+
main$c();
|
|
2587
2608
|
|
|
2588
2609
|
const notifyShutdown = singleshot(async () => {
|
|
2589
2610
|
console.log("Graceful shutdown initiated. Press Ctrl+C again to force quit.");
|
|
2590
2611
|
});
|
|
2591
2612
|
|
|
2613
|
+
const FLUSH_DIRS = ["report", "log", "markdown", "agent"];
|
|
2614
|
+
const flush = async (entryPoint) => {
|
|
2615
|
+
const moduleRoot = dirname(resolve(process.cwd(), entryPoint));
|
|
2616
|
+
const dumpDir = join(moduleRoot, "dump");
|
|
2617
|
+
for (const dir of FLUSH_DIRS) {
|
|
2618
|
+
const target = join(dumpDir, dir);
|
|
2619
|
+
await rm(target, { recursive: true, force: true });
|
|
2620
|
+
console.log(`Removed: ${target}`);
|
|
2621
|
+
}
|
|
2622
|
+
};
|
|
2623
|
+
const main$b = async () => {
|
|
2624
|
+
if (!getEntry(import.meta.url)) {
|
|
2625
|
+
return;
|
|
2626
|
+
}
|
|
2627
|
+
const { values } = getArgs();
|
|
2628
|
+
if (!values.flush) {
|
|
2629
|
+
return;
|
|
2630
|
+
}
|
|
2631
|
+
const entryPoints = getPositionals();
|
|
2632
|
+
if (!entryPoints.length) {
|
|
2633
|
+
throw new Error("Entry point is required");
|
|
2634
|
+
}
|
|
2635
|
+
for (const entryPoint of entryPoints) {
|
|
2636
|
+
await flush(entryPoint);
|
|
2637
|
+
}
|
|
2638
|
+
process.exit(0);
|
|
2639
|
+
};
|
|
2640
|
+
main$b();
|
|
2641
|
+
|
|
2592
2642
|
const BEFORE_EXIT_FN$5 = singleshot(async () => {
|
|
2593
2643
|
process.off("SIGINT", BEFORE_EXIT_FN$5);
|
|
2594
2644
|
const [running = null] = await Backtest.list();
|
|
@@ -2617,6 +2667,10 @@ const main$a = async () => {
|
|
|
2617
2667
|
if (!values.backtest) {
|
|
2618
2668
|
return;
|
|
2619
2669
|
}
|
|
2670
|
+
if (!values.noFlush) {
|
|
2671
|
+
const [entryPoint = null] = getPositionals();
|
|
2672
|
+
entryPoint && await flush(entryPoint);
|
|
2673
|
+
}
|
|
2620
2674
|
await cli.backtestMainService.connect();
|
|
2621
2675
|
listenGracefulShutdown$5();
|
|
2622
2676
|
};
|
|
@@ -2646,6 +2700,11 @@ const main$9 = async () => {
|
|
|
2646
2700
|
if (!values.walker) {
|
|
2647
2701
|
return;
|
|
2648
2702
|
}
|
|
2703
|
+
if (!values.noFlush) {
|
|
2704
|
+
for (const entryPoint of getPositionals()) {
|
|
2705
|
+
await flush(entryPoint);
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2649
2708
|
listenGracefulShutdown$4();
|
|
2650
2709
|
await cli.walkerMainService.connect();
|
|
2651
2710
|
};
|
|
@@ -3031,6 +3090,7 @@ Modes:
|
|
|
3031
3090
|
--live <entry> Live trading with real orders
|
|
3032
3091
|
--pine <entry> Execute a local .pine indicator file
|
|
3033
3092
|
--dump Fetch and save raw OHLCV candles
|
|
3093
|
+
--flush <entry...> Delete report/log/markdown/agent folders from strategy dump dir
|
|
3034
3094
|
--init Scaffold a new project in the current directory
|
|
3035
3095
|
--help Print this help message
|
|
3036
3096
|
|
|
@@ -3042,6 +3102,7 @@ Backtest flags:
|
|
|
3042
3102
|
--frame <string> Frame name from addFrameSchema (default: first registered)
|
|
3043
3103
|
--cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
|
|
3044
3104
|
--noCache Skip candle cache warming before the run
|
|
3105
|
+
--noFlush Skip removing report/log/markdown/agent folders before backtest run
|
|
3045
3106
|
--verbose Log every candle fetch to stdout
|
|
3046
3107
|
--ui Start web dashboard at http://localhost:60050
|
|
3047
3108
|
--telegram Send trade notifications to Telegram
|
|
@@ -3051,6 +3112,7 @@ Walker flags (--walker):
|
|
|
3051
3112
|
--symbol <string> Trading pair (default: BTCUSDT)
|
|
3052
3113
|
--cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
|
|
3053
3114
|
--noCache Skip candle cache warming before the run
|
|
3115
|
+
--noFlush Skip removing report/log/markdown/agent folders before walker run
|
|
3054
3116
|
--verbose Log every candle fetch to stdout
|
|
3055
3117
|
--output <string> Output file base name (default: walker_{SYMBOL}_{TIMESTAMP})
|
|
3056
3118
|
--json Save results as JSON to ./dump/<output>.json
|
|
@@ -3100,6 +3162,13 @@ Candle dump flags (--dump):
|
|
|
3100
3162
|
|
|
3101
3163
|
Module file ./modules/dump.module is loaded automatically if it exists.
|
|
3102
3164
|
|
|
3165
|
+
Flush flags (--flush):
|
|
3166
|
+
|
|
3167
|
+
One or more positional entry points. For each entry point the following
|
|
3168
|
+
subdirectories are removed from <entry-dir>/dump/:
|
|
3169
|
+
|
|
3170
|
+
report log markdown agent
|
|
3171
|
+
|
|
3103
3172
|
Init flags (--init):
|
|
3104
3173
|
|
|
3105
3174
|
--output <string> Target directory name (default: backtest-kit-project)
|
|
@@ -3115,6 +3184,8 @@ Module hooks (loaded automatically by each mode):
|
|
|
3115
3184
|
modules/pine.module --pine Exchange schema for PineScript runs
|
|
3116
3185
|
modules/dump.module --dump Exchange schema for candle dumps
|
|
3117
3186
|
|
|
3187
|
+
--flush has no associated module. It only removes dump subdirectories.
|
|
3188
|
+
|
|
3118
3189
|
Extensions .ts, .mjs, .cjs are tried automatically. Missing module = soft warning.
|
|
3119
3190
|
|
|
3120
3191
|
Environment variables:
|
|
@@ -3127,13 +3198,15 @@ Environment variables:
|
|
|
3127
3198
|
Examples:
|
|
3128
3199
|
|
|
3129
3200
|
node ${ENTRY_PATH} --backtest ./content/feb_2026.strategy.ts
|
|
3130
|
-
node ${ENTRY_PATH} --backtest --symbol BTCUSDT --noCache --ui ./content/feb_2026.strategy.ts
|
|
3201
|
+
node ${ENTRY_PATH} --backtest --symbol BTCUSDT --noCache --noFlush --ui ./content/feb_2026.strategy.ts
|
|
3131
3202
|
node ${ENTRY_PATH} --walker ./content/feb_2026_v1.strategy.ts ./content/feb_2026_v2.strategy.ts ./content/feb_2026_v3.strategy.ts
|
|
3132
|
-
node ${ENTRY_PATH} --walker --symbol BTCUSDT --noCache --markdown ./content/feb_2026_v1.ts ./content/feb_2026_v2.ts
|
|
3203
|
+
node ${ENTRY_PATH} --walker --symbol BTCUSDT --noCache --noFlush --markdown ./content/feb_2026_v1.ts ./content/feb_2026_v2.ts
|
|
3133
3204
|
node ${ENTRY_PATH} --paper --symbol ETHUSDT ./content/feb_2026.strategy.ts
|
|
3134
3205
|
node ${ENTRY_PATH} --live --ui --telegram ./content/feb_2026.strategy.ts
|
|
3135
3206
|
node ${ENTRY_PATH} --pine ./math/feb_2026.pine --timeframe 15m --limit 500 --jsonl
|
|
3136
3207
|
node ${ENTRY_PATH} --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl
|
|
3208
|
+
node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts
|
|
3209
|
+
node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts ./content/feb_2026.strategy/feb_2026.test.ts
|
|
3137
3210
|
node ${ENTRY_PATH} --init --output my-trading-bot
|
|
3138
3211
|
`.trimStart();
|
|
3139
3212
|
const main$1 = async () => {
|
|
@@ -3144,7 +3217,7 @@ const main$1 = async () => {
|
|
|
3144
3217
|
if (!values.help) {
|
|
3145
3218
|
return;
|
|
3146
3219
|
}
|
|
3147
|
-
process.stdout.write(`@backtest-kit/cli ${"6.
|
|
3220
|
+
process.stdout.write(`@backtest-kit/cli ${"6.15.0"}\n\n`);
|
|
3148
3221
|
process.stdout.write(HELP_TEXT);
|
|
3149
3222
|
process.exit(0);
|
|
3150
3223
|
};
|
|
@@ -3158,7 +3231,7 @@ const main = async () => {
|
|
|
3158
3231
|
if (!values.version) {
|
|
3159
3232
|
return;
|
|
3160
3233
|
}
|
|
3161
|
-
process.stdout.write(`@backtest-kit/cli ${"6.
|
|
3234
|
+
process.stdout.write(`@backtest-kit/cli ${"6.15.0"}\n`);
|
|
3162
3235
|
process.exit(0);
|
|
3163
3236
|
};
|
|
3164
3237
|
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backtest-kit/cli",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.15.0",
|
|
4
4
|
"description": "Zero-boilerplate CLI runner for backtest-kit strategies. Run backtests, paper trading, and live bots with candle cache warming, web dashboard, and Telegram notifications — no setup code required.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Petr Tripolsky",
|
|
@@ -61,11 +61,11 @@
|
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@babel/plugin-transform-modules-umd": "7.27.1",
|
|
63
63
|
"@babel/standalone": "7.29.1",
|
|
64
|
-
"@backtest-kit/ui": "6.
|
|
65
|
-
"@backtest-kit/graph": "6.
|
|
66
|
-
"@backtest-kit/ollama": "6.
|
|
67
|
-
"@backtest-kit/pinets": "6.
|
|
68
|
-
"@backtest-kit/signals": "6.
|
|
64
|
+
"@backtest-kit/ui": "6.15.0",
|
|
65
|
+
"@backtest-kit/graph": "6.15.0",
|
|
66
|
+
"@backtest-kit/ollama": "6.15.0",
|
|
67
|
+
"@backtest-kit/pinets": "6.15.0",
|
|
68
|
+
"@backtest-kit/signals": "6.15.0",
|
|
69
69
|
"@rollup/plugin-replace": "6.0.3",
|
|
70
70
|
"@rollup/plugin-typescript": "11.1.6",
|
|
71
71
|
"@types/image-size": "0.7.0",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"@types/mustache": "4.2.6",
|
|
74
74
|
"@types/node": "22.9.0",
|
|
75
75
|
"@types/stack-trace": "0.0.33",
|
|
76
|
-
"backtest-kit": "6.
|
|
76
|
+
"backtest-kit": "6.15.0",
|
|
77
77
|
"glob": "11.0.1",
|
|
78
78
|
"markdown-it": "14.1.1",
|
|
79
79
|
"rimraf": "6.0.1",
|
|
@@ -88,12 +88,12 @@
|
|
|
88
88
|
"peerDependencies": {
|
|
89
89
|
"@babel/plugin-transform-modules-umd": "^7.27.1",
|
|
90
90
|
"@babel/standalone": "^7.29.1",
|
|
91
|
-
"@backtest-kit/ui": "^6.
|
|
92
|
-
"@backtest-kit/graph": "^6.
|
|
93
|
-
"@backtest-kit/ollama": "^6.
|
|
94
|
-
"@backtest-kit/pinets": "^6.
|
|
95
|
-
"@backtest-kit/signals": "^6.
|
|
96
|
-
"backtest-kit": "^6.
|
|
91
|
+
"@backtest-kit/ui": "^6.15.0",
|
|
92
|
+
"@backtest-kit/graph": "^6.15.0",
|
|
93
|
+
"@backtest-kit/ollama": "^6.15.0",
|
|
94
|
+
"@backtest-kit/pinets": "^6.15.0",
|
|
95
|
+
"@backtest-kit/signals": "^6.15.0",
|
|
96
|
+
"backtest-kit": "^6.15.0",
|
|
97
97
|
"markdown-it": "^14.1.1",
|
|
98
98
|
"typescript": "^5.0.0"
|
|
99
99
|
},
|
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
"license": "ISC",
|
|
13
13
|
"type": "commonjs",
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@backtest-kit/cli": "^6.
|
|
16
|
-
"@backtest-kit/graph": "^6.
|
|
17
|
-
"@backtest-kit/pinets": "^6.
|
|
18
|
-
"@backtest-kit/ui": "^6.
|
|
15
|
+
"@backtest-kit/cli": "^6.15.0",
|
|
16
|
+
"@backtest-kit/graph": "^6.15.0",
|
|
17
|
+
"@backtest-kit/pinets": "^6.15.0",
|
|
18
|
+
"@backtest-kit/ui": "^6.15.0",
|
|
19
19
|
"agent-swarm-kit": "^2.5.0",
|
|
20
|
-
"backtest-kit": "^6.
|
|
20
|
+
"backtest-kit": "^6.15.0",
|
|
21
21
|
"functools-kit": "^2.2.0",
|
|
22
22
|
"garch": "^1.2.3",
|
|
23
23
|
"get-moment-stamp": "^1.1.2",
|