@backtest-kit/cli 6.13.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 +99 -11
- package/build/index.mjs +102 -14
- package/package.json +14 -14
- package/template/project/package.mustache +7 -7
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();
|
|
@@ -2503,6 +2524,23 @@ class SetupUtils {
|
|
|
2503
2524
|
return;
|
|
2504
2525
|
}
|
|
2505
2526
|
this.enable.clear();
|
|
2527
|
+
{
|
|
2528
|
+
BacktestKit.Recent.disable();
|
|
2529
|
+
BacktestKit.Storage.disable();
|
|
2530
|
+
BacktestKit.Notification.disable();
|
|
2531
|
+
}
|
|
2532
|
+
{
|
|
2533
|
+
BacktestKit.Markdown.disable();
|
|
2534
|
+
BacktestKit.Report.disable();
|
|
2535
|
+
BacktestKit.Dump.disable();
|
|
2536
|
+
BacktestKit.Memory.disable();
|
|
2537
|
+
}
|
|
2538
|
+
{
|
|
2539
|
+
BacktestKit.Markdown.clear();
|
|
2540
|
+
BacktestKit.Report.clear();
|
|
2541
|
+
BacktestKit.MarkdownWriter.clear();
|
|
2542
|
+
BacktestKit.ReportWriter.clear();
|
|
2543
|
+
}
|
|
2506
2544
|
{
|
|
2507
2545
|
BacktestKit.PersistSignalAdapter.clear();
|
|
2508
2546
|
BacktestKit.PersistRiskAdapter.clear();
|
|
@@ -2522,8 +2560,6 @@ class SetupUtils {
|
|
|
2522
2560
|
BacktestKit.Dump.clear();
|
|
2523
2561
|
BacktestKit.Log.clear();
|
|
2524
2562
|
BacktestKit.Markdown.clear();
|
|
2525
|
-
BacktestKit.Memory.clear();
|
|
2526
|
-
BacktestKit.Report.clear();
|
|
2527
2563
|
}
|
|
2528
2564
|
{
|
|
2529
2565
|
BacktestKit.StorageLive.clear();
|
|
@@ -2575,14 +2611,14 @@ BacktestKit.setConfig({
|
|
|
2575
2611
|
CC_WALKER_MARKDOWN_TOP_N: 10,
|
|
2576
2612
|
});
|
|
2577
2613
|
|
|
2578
|
-
const MODES = ["backtest", "walker", "paper", "live", "pine", "dump", "init", "help", "version"];
|
|
2614
|
+
const MODES = ["backtest", "walker", "paper", "live", "pine", "dump", "flush", "init", "help", "version"];
|
|
2579
2615
|
const ENTRY_PATH$1 = "./node_modules/@backtest-kit/cli/build/index.mjs";
|
|
2580
2616
|
const HELP_TEXT$1 = `
|
|
2581
2617
|
Example:
|
|
2582
2618
|
|
|
2583
2619
|
node ${ENTRY_PATH$1} --help
|
|
2584
2620
|
`.trimStart();
|
|
2585
|
-
const main$
|
|
2621
|
+
const main$c = async () => {
|
|
2586
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)))) {
|
|
2587
2623
|
return;
|
|
2588
2624
|
}
|
|
@@ -2590,19 +2626,48 @@ const main$b = async () => {
|
|
|
2590
2626
|
if (MODES.some((mode) => values[mode])) {
|
|
2591
2627
|
return;
|
|
2592
2628
|
}
|
|
2593
|
-
process.stdout.write(`@backtest-kit/cli ${"6.
|
|
2629
|
+
process.stdout.write(`@backtest-kit/cli ${"6.15.0"}\n`);
|
|
2594
2630
|
process.stdout.write("\n");
|
|
2595
2631
|
process.stdout.write(`Run with --help to see available commands.\n`);
|
|
2596
2632
|
process.stdout.write("\n");
|
|
2597
2633
|
process.stdout.write(HELP_TEXT$1);
|
|
2598
2634
|
process.exit(0);
|
|
2599
2635
|
};
|
|
2600
|
-
main$
|
|
2636
|
+
main$c();
|
|
2601
2637
|
|
|
2602
2638
|
const notifyShutdown = functoolsKit.singleshot(async () => {
|
|
2603
2639
|
console.log("Graceful shutdown initiated. Press Ctrl+C again to force quit.");
|
|
2604
2640
|
});
|
|
2605
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
|
+
|
|
2606
2671
|
const BEFORE_EXIT_FN$5 = functoolsKit.singleshot(async () => {
|
|
2607
2672
|
process.off("SIGINT", BEFORE_EXIT_FN$5);
|
|
2608
2673
|
const [running = null] = await BacktestKit.Backtest.list();
|
|
@@ -2631,6 +2696,10 @@ const main$a = async () => {
|
|
|
2631
2696
|
if (!values.backtest) {
|
|
2632
2697
|
return;
|
|
2633
2698
|
}
|
|
2699
|
+
if (!values.noFlush) {
|
|
2700
|
+
const [entryPoint = null] = getPositionals();
|
|
2701
|
+
entryPoint && await flush(entryPoint);
|
|
2702
|
+
}
|
|
2634
2703
|
await cli.backtestMainService.connect();
|
|
2635
2704
|
listenGracefulShutdown$5();
|
|
2636
2705
|
};
|
|
@@ -2660,6 +2729,11 @@ const main$9 = async () => {
|
|
|
2660
2729
|
if (!values.walker) {
|
|
2661
2730
|
return;
|
|
2662
2731
|
}
|
|
2732
|
+
if (!values.noFlush) {
|
|
2733
|
+
for (const entryPoint of getPositionals()) {
|
|
2734
|
+
await flush(entryPoint);
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2663
2737
|
listenGracefulShutdown$4();
|
|
2664
2738
|
await cli.walkerMainService.connect();
|
|
2665
2739
|
};
|
|
@@ -3045,6 +3119,7 @@ Modes:
|
|
|
3045
3119
|
--live <entry> Live trading with real orders
|
|
3046
3120
|
--pine <entry> Execute a local .pine indicator file
|
|
3047
3121
|
--dump Fetch and save raw OHLCV candles
|
|
3122
|
+
--flush <entry...> Delete report/log/markdown/agent folders from strategy dump dir
|
|
3048
3123
|
--init Scaffold a new project in the current directory
|
|
3049
3124
|
--help Print this help message
|
|
3050
3125
|
|
|
@@ -3056,6 +3131,7 @@ Backtest flags:
|
|
|
3056
3131
|
--frame <string> Frame name from addFrameSchema (default: first registered)
|
|
3057
3132
|
--cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
|
|
3058
3133
|
--noCache Skip candle cache warming before the run
|
|
3134
|
+
--noFlush Skip removing report/log/markdown/agent folders before backtest run
|
|
3059
3135
|
--verbose Log every candle fetch to stdout
|
|
3060
3136
|
--ui Start web dashboard at http://localhost:60050
|
|
3061
3137
|
--telegram Send trade notifications to Telegram
|
|
@@ -3065,6 +3141,7 @@ Walker flags (--walker):
|
|
|
3065
3141
|
--symbol <string> Trading pair (default: BTCUSDT)
|
|
3066
3142
|
--cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
|
|
3067
3143
|
--noCache Skip candle cache warming before the run
|
|
3144
|
+
--noFlush Skip removing report/log/markdown/agent folders before walker run
|
|
3068
3145
|
--verbose Log every candle fetch to stdout
|
|
3069
3146
|
--output <string> Output file base name (default: walker_{SYMBOL}_{TIMESTAMP})
|
|
3070
3147
|
--json Save results as JSON to ./dump/<output>.json
|
|
@@ -3114,6 +3191,13 @@ Candle dump flags (--dump):
|
|
|
3114
3191
|
|
|
3115
3192
|
Module file ./modules/dump.module is loaded automatically if it exists.
|
|
3116
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
|
+
|
|
3117
3201
|
Init flags (--init):
|
|
3118
3202
|
|
|
3119
3203
|
--output <string> Target directory name (default: backtest-kit-project)
|
|
@@ -3129,6 +3213,8 @@ Module hooks (loaded automatically by each mode):
|
|
|
3129
3213
|
modules/pine.module --pine Exchange schema for PineScript runs
|
|
3130
3214
|
modules/dump.module --dump Exchange schema for candle dumps
|
|
3131
3215
|
|
|
3216
|
+
--flush has no associated module. It only removes dump subdirectories.
|
|
3217
|
+
|
|
3132
3218
|
Extensions .ts, .mjs, .cjs are tried automatically. Missing module = soft warning.
|
|
3133
3219
|
|
|
3134
3220
|
Environment variables:
|
|
@@ -3141,13 +3227,15 @@ Environment variables:
|
|
|
3141
3227
|
Examples:
|
|
3142
3228
|
|
|
3143
3229
|
node ${ENTRY_PATH} --backtest ./content/feb_2026.strategy.ts
|
|
3144
|
-
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
|
|
3145
3231
|
node ${ENTRY_PATH} --walker ./content/feb_2026_v1.strategy.ts ./content/feb_2026_v2.strategy.ts ./content/feb_2026_v3.strategy.ts
|
|
3146
|
-
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
|
|
3147
3233
|
node ${ENTRY_PATH} --paper --symbol ETHUSDT ./content/feb_2026.strategy.ts
|
|
3148
3234
|
node ${ENTRY_PATH} --live --ui --telegram ./content/feb_2026.strategy.ts
|
|
3149
3235
|
node ${ENTRY_PATH} --pine ./math/feb_2026.pine --timeframe 15m --limit 500 --jsonl
|
|
3150
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
|
|
3151
3239
|
node ${ENTRY_PATH} --init --output my-trading-bot
|
|
3152
3240
|
`.trimStart();
|
|
3153
3241
|
const main$1 = async () => {
|
|
@@ -3158,7 +3246,7 @@ const main$1 = async () => {
|
|
|
3158
3246
|
if (!values.help) {
|
|
3159
3247
|
return;
|
|
3160
3248
|
}
|
|
3161
|
-
process.stdout.write(`@backtest-kit/cli ${"6.
|
|
3249
|
+
process.stdout.write(`@backtest-kit/cli ${"6.15.0"}\n\n`);
|
|
3162
3250
|
process.stdout.write(HELP_TEXT);
|
|
3163
3251
|
process.exit(0);
|
|
3164
3252
|
};
|
|
@@ -3172,7 +3260,7 @@ const main = async () => {
|
|
|
3172
3260
|
if (!values.version) {
|
|
3173
3261
|
return;
|
|
3174
3262
|
}
|
|
3175
|
-
process.stdout.write(`@backtest-kit/cli ${"6.
|
|
3263
|
+
process.stdout.write(`@backtest-kit/cli ${"6.15.0"}\n`);
|
|
3176
3264
|
process.exit(0);
|
|
3177
3265
|
};
|
|
3178
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();
|
|
@@ -2474,6 +2495,23 @@ class SetupUtils {
|
|
|
2474
2495
|
return;
|
|
2475
2496
|
}
|
|
2476
2497
|
this.enable.clear();
|
|
2498
|
+
{
|
|
2499
|
+
Recent.disable();
|
|
2500
|
+
Storage.disable();
|
|
2501
|
+
Notification.disable();
|
|
2502
|
+
}
|
|
2503
|
+
{
|
|
2504
|
+
Markdown.disable();
|
|
2505
|
+
Report.disable();
|
|
2506
|
+
Dump.disable();
|
|
2507
|
+
Memory.disable();
|
|
2508
|
+
}
|
|
2509
|
+
{
|
|
2510
|
+
Markdown.clear();
|
|
2511
|
+
Report.clear();
|
|
2512
|
+
MarkdownWriter.clear();
|
|
2513
|
+
ReportWriter.clear();
|
|
2514
|
+
}
|
|
2477
2515
|
{
|
|
2478
2516
|
PersistSignalAdapter.clear();
|
|
2479
2517
|
PersistRiskAdapter.clear();
|
|
@@ -2493,8 +2531,6 @@ class SetupUtils {
|
|
|
2493
2531
|
Dump.clear();
|
|
2494
2532
|
Log.clear();
|
|
2495
2533
|
Markdown.clear();
|
|
2496
|
-
Memory.clear();
|
|
2497
|
-
Report.clear();
|
|
2498
2534
|
}
|
|
2499
2535
|
{
|
|
2500
2536
|
StorageLive.clear();
|
|
@@ -2546,14 +2582,14 @@ setConfig({
|
|
|
2546
2582
|
CC_WALKER_MARKDOWN_TOP_N: 10,
|
|
2547
2583
|
});
|
|
2548
2584
|
|
|
2549
|
-
const MODES = ["backtest", "walker", "paper", "live", "pine", "dump", "init", "help", "version"];
|
|
2585
|
+
const MODES = ["backtest", "walker", "paper", "live", "pine", "dump", "flush", "init", "help", "version"];
|
|
2550
2586
|
const ENTRY_PATH$1 = "./node_modules/@backtest-kit/cli/build/index.mjs";
|
|
2551
2587
|
const HELP_TEXT$1 = `
|
|
2552
2588
|
Example:
|
|
2553
2589
|
|
|
2554
2590
|
node ${ENTRY_PATH$1} --help
|
|
2555
2591
|
`.trimStart();
|
|
2556
|
-
const main$
|
|
2592
|
+
const main$c = async () => {
|
|
2557
2593
|
if (!getEntry(import.meta.url)) {
|
|
2558
2594
|
return;
|
|
2559
2595
|
}
|
|
@@ -2561,19 +2597,48 @@ const main$b = async () => {
|
|
|
2561
2597
|
if (MODES.some((mode) => values[mode])) {
|
|
2562
2598
|
return;
|
|
2563
2599
|
}
|
|
2564
|
-
process.stdout.write(`@backtest-kit/cli ${"6.
|
|
2600
|
+
process.stdout.write(`@backtest-kit/cli ${"6.15.0"}\n`);
|
|
2565
2601
|
process.stdout.write("\n");
|
|
2566
2602
|
process.stdout.write(`Run with --help to see available commands.\n`);
|
|
2567
2603
|
process.stdout.write("\n");
|
|
2568
2604
|
process.stdout.write(HELP_TEXT$1);
|
|
2569
2605
|
process.exit(0);
|
|
2570
2606
|
};
|
|
2571
|
-
main$
|
|
2607
|
+
main$c();
|
|
2572
2608
|
|
|
2573
2609
|
const notifyShutdown = singleshot(async () => {
|
|
2574
2610
|
console.log("Graceful shutdown initiated. Press Ctrl+C again to force quit.");
|
|
2575
2611
|
});
|
|
2576
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
|
+
|
|
2577
2642
|
const BEFORE_EXIT_FN$5 = singleshot(async () => {
|
|
2578
2643
|
process.off("SIGINT", BEFORE_EXIT_FN$5);
|
|
2579
2644
|
const [running = null] = await Backtest.list();
|
|
@@ -2602,6 +2667,10 @@ const main$a = async () => {
|
|
|
2602
2667
|
if (!values.backtest) {
|
|
2603
2668
|
return;
|
|
2604
2669
|
}
|
|
2670
|
+
if (!values.noFlush) {
|
|
2671
|
+
const [entryPoint = null] = getPositionals();
|
|
2672
|
+
entryPoint && await flush(entryPoint);
|
|
2673
|
+
}
|
|
2605
2674
|
await cli.backtestMainService.connect();
|
|
2606
2675
|
listenGracefulShutdown$5();
|
|
2607
2676
|
};
|
|
@@ -2631,6 +2700,11 @@ const main$9 = async () => {
|
|
|
2631
2700
|
if (!values.walker) {
|
|
2632
2701
|
return;
|
|
2633
2702
|
}
|
|
2703
|
+
if (!values.noFlush) {
|
|
2704
|
+
for (const entryPoint of getPositionals()) {
|
|
2705
|
+
await flush(entryPoint);
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2634
2708
|
listenGracefulShutdown$4();
|
|
2635
2709
|
await cli.walkerMainService.connect();
|
|
2636
2710
|
};
|
|
@@ -3016,6 +3090,7 @@ Modes:
|
|
|
3016
3090
|
--live <entry> Live trading with real orders
|
|
3017
3091
|
--pine <entry> Execute a local .pine indicator file
|
|
3018
3092
|
--dump Fetch and save raw OHLCV candles
|
|
3093
|
+
--flush <entry...> Delete report/log/markdown/agent folders from strategy dump dir
|
|
3019
3094
|
--init Scaffold a new project in the current directory
|
|
3020
3095
|
--help Print this help message
|
|
3021
3096
|
|
|
@@ -3027,6 +3102,7 @@ Backtest flags:
|
|
|
3027
3102
|
--frame <string> Frame name from addFrameSchema (default: first registered)
|
|
3028
3103
|
--cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
|
|
3029
3104
|
--noCache Skip candle cache warming before the run
|
|
3105
|
+
--noFlush Skip removing report/log/markdown/agent folders before backtest run
|
|
3030
3106
|
--verbose Log every candle fetch to stdout
|
|
3031
3107
|
--ui Start web dashboard at http://localhost:60050
|
|
3032
3108
|
--telegram Send trade notifications to Telegram
|
|
@@ -3036,6 +3112,7 @@ Walker flags (--walker):
|
|
|
3036
3112
|
--symbol <string> Trading pair (default: BTCUSDT)
|
|
3037
3113
|
--cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
|
|
3038
3114
|
--noCache Skip candle cache warming before the run
|
|
3115
|
+
--noFlush Skip removing report/log/markdown/agent folders before walker run
|
|
3039
3116
|
--verbose Log every candle fetch to stdout
|
|
3040
3117
|
--output <string> Output file base name (default: walker_{SYMBOL}_{TIMESTAMP})
|
|
3041
3118
|
--json Save results as JSON to ./dump/<output>.json
|
|
@@ -3085,6 +3162,13 @@ Candle dump flags (--dump):
|
|
|
3085
3162
|
|
|
3086
3163
|
Module file ./modules/dump.module is loaded automatically if it exists.
|
|
3087
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
|
+
|
|
3088
3172
|
Init flags (--init):
|
|
3089
3173
|
|
|
3090
3174
|
--output <string> Target directory name (default: backtest-kit-project)
|
|
@@ -3100,6 +3184,8 @@ Module hooks (loaded automatically by each mode):
|
|
|
3100
3184
|
modules/pine.module --pine Exchange schema for PineScript runs
|
|
3101
3185
|
modules/dump.module --dump Exchange schema for candle dumps
|
|
3102
3186
|
|
|
3187
|
+
--flush has no associated module. It only removes dump subdirectories.
|
|
3188
|
+
|
|
3103
3189
|
Extensions .ts, .mjs, .cjs are tried automatically. Missing module = soft warning.
|
|
3104
3190
|
|
|
3105
3191
|
Environment variables:
|
|
@@ -3112,13 +3198,15 @@ Environment variables:
|
|
|
3112
3198
|
Examples:
|
|
3113
3199
|
|
|
3114
3200
|
node ${ENTRY_PATH} --backtest ./content/feb_2026.strategy.ts
|
|
3115
|
-
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
|
|
3116
3202
|
node ${ENTRY_PATH} --walker ./content/feb_2026_v1.strategy.ts ./content/feb_2026_v2.strategy.ts ./content/feb_2026_v3.strategy.ts
|
|
3117
|
-
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
|
|
3118
3204
|
node ${ENTRY_PATH} --paper --symbol ETHUSDT ./content/feb_2026.strategy.ts
|
|
3119
3205
|
node ${ENTRY_PATH} --live --ui --telegram ./content/feb_2026.strategy.ts
|
|
3120
3206
|
node ${ENTRY_PATH} --pine ./math/feb_2026.pine --timeframe 15m --limit 500 --jsonl
|
|
3121
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
|
|
3122
3210
|
node ${ENTRY_PATH} --init --output my-trading-bot
|
|
3123
3211
|
`.trimStart();
|
|
3124
3212
|
const main$1 = async () => {
|
|
@@ -3129,7 +3217,7 @@ const main$1 = async () => {
|
|
|
3129
3217
|
if (!values.help) {
|
|
3130
3218
|
return;
|
|
3131
3219
|
}
|
|
3132
|
-
process.stdout.write(`@backtest-kit/cli ${"6.
|
|
3220
|
+
process.stdout.write(`@backtest-kit/cli ${"6.15.0"}\n\n`);
|
|
3133
3221
|
process.stdout.write(HELP_TEXT);
|
|
3134
3222
|
process.exit(0);
|
|
3135
3223
|
};
|
|
@@ -3143,7 +3231,7 @@ const main = async () => {
|
|
|
3143
3231
|
if (!values.version) {
|
|
3144
3232
|
return;
|
|
3145
3233
|
}
|
|
3146
|
-
process.stdout.write(`@backtest-kit/cli ${"6.
|
|
3234
|
+
process.stdout.write(`@backtest-kit/cli ${"6.15.0"}\n`);
|
|
3147
3235
|
process.exit(0);
|
|
3148
3236
|
};
|
|
3149
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
|
},
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"di-kit": "1.1.1",
|
|
103
103
|
"di-scoped": "1.0.21",
|
|
104
104
|
"dotenv": "17.3.1",
|
|
105
|
-
"functools-kit": "2.0
|
|
105
|
+
"functools-kit": "2.2.0",
|
|
106
106
|
"get-moment-stamp": "1.1.2",
|
|
107
107
|
"image-size": "1.1.1",
|
|
108
108
|
"jsdom": "26.1.0",
|
|
@@ -12,13 +12,13 @@
|
|
|
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.
|
|
19
|
-
"agent-swarm-kit": "^2.
|
|
20
|
-
"backtest-kit": "^6.
|
|
21
|
-
"functools-kit": "^2.0
|
|
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
|
+
"agent-swarm-kit": "^2.5.0",
|
|
20
|
+
"backtest-kit": "^6.15.0",
|
|
21
|
+
"functools-kit": "^2.2.0",
|
|
22
22
|
"garch": "^1.2.3",
|
|
23
23
|
"get-moment-stamp": "^1.1.2",
|
|
24
24
|
"ollama": "^0.6.3",
|