@backtest-kit/cli 6.14.0 → 6.16.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 CHANGED
@@ -40,7 +40,9 @@ Point the CLI at your strategy file, choose a mode, and it handles exchange conn
40
40
  | **UI Dashboard** | `--ui` | Web dashboard at `http://localhost:60050` |
41
41
  | **Telegram** | `--telegram` | Trade notifications with price charts |
42
42
  | **PineScript** | `--pine` | Run a local `.pine` indicator against exchange data |
43
+ | **Pine Editor** | `--editor` | Open the visual Pine Script editor in the browser |
43
44
  | **Candle Dump** | `--dump` | Fetch and save raw OHLCV candles to a file |
45
+ | **Flush** | `--flush` | Delete report/log/markdown/agent folders from strategy dump dir |
44
46
  | **Init Project** | `--init` | Scaffold a new backtest-kit project |
45
47
 
46
48
  ## 🚀 Installation
@@ -144,6 +146,7 @@ npm start -- --symbol BTCUSDT --ui
144
146
  | `--telegram` | boolean | Enable Telegram notifications (default: `false`) |
145
147
  | `--verbose` | boolean | Log each candle fetch (default: `false`) |
146
148
  | `--noCache` | boolean | Skip candle cache warming before backtest (default: `false`) |
149
+ | `--noFlush` | boolean | Skip removing report/log/markdown/agent folders before backtest run (default: `false`) |
147
150
  | `--symbol` | string | Trading pair (default: `"BTCUSDT"`) |
148
151
  | `--strategy` | string | Strategy name (default: first registered) |
149
152
  | `--exchange` | string | Exchange name (default: first registered) |
@@ -178,7 +181,7 @@ Runs the strategy against historical candle data using a registered `FrameSchema
178
181
  npm run backtest
179
182
  ```
180
183
 
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 this step entirely.
184
+ 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
185
 
183
186
  ### Paper Trading
184
187
 
@@ -228,7 +231,7 @@ Runs the same historical period against multiple strategy files and prints a ran
228
231
  npm run walker
229
232
  ```
230
233
 
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.
234
+ 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
235
 
233
236
  If no frame is registered, the CLI falls back to the last 31 days from `Date.now()` with a console warning.
234
237
 
@@ -240,6 +243,7 @@ If no frame is registered, the CLI falls back to the last 31 days from `Date.now
240
243
  | `--symbol` | string | Trading pair (default: `"BTCUSDT"`) |
241
244
  | `--cacheInterval` | string | Intervals to pre-cache (default: `"1m, 15m, 30m, 4h"`) |
242
245
  | `--noCache` | boolean | Skip candle cache warming (default: `false`) |
246
+ | `--noFlush` | boolean | Skip removing report/log/markdown/agent folders before walker run (default: `false`) |
243
247
  | `--verbose` | boolean | Log each candle fetch and strategy progress (default: `false`) |
244
248
  | `--output` | string | Output file base name (default: `walker_{SYMBOL}_{TIMESTAMP}`) |
245
249
  | `--json` | boolean | Save results as JSON to `./dump/<output>.json` |
@@ -736,6 +740,68 @@ Print to stdout (no flag):
736
740
  npx @backtest-kit/cli --pine ./math/impulse_trend_15m.pine
737
741
  ```
738
742
 
743
+ ## 🎨 Visual Pine Script Editor
744
+
745
+ `@backtest-kit/cli` ships a browser-based Pine Script editor powered by `@backtest-kit/ui`. It lets you write, run, and iterate on indicators interactively — with a live chart that updates as you hit **▶ Run** — without leaving the terminal.
746
+
747
+ ### Usage
748
+
749
+ ```bash
750
+ npx @backtest-kit/cli --editor
751
+ ```
752
+
753
+ The CLI will:
754
+
755
+ 1. Load `./modules/editor.module` (if it exists) — use it to register your exchange schema, identical to `pine.module`
756
+ 2. Start the `@backtest-kit/ui` server on `http://localhost:60050` (or `CC_WWWROOT_PORT`)
757
+ 3. Open `http://localhost:{CC_WWWROOT_PORT}?pine=1` automatically in your default browser
758
+
759
+ Press **Ctrl+C** to stop the server.
760
+
761
+ ### Exchange via `editor.module`
762
+
763
+ Drop a `modules/editor.module.ts` next to your project to register the exchange that the editor's candle provider will use:
764
+
765
+ ```typescript
766
+ // modules/editor.module.ts
767
+ import { addExchangeSchema } from "backtest-kit";
768
+ import ccxt from "ccxt";
769
+
770
+ addExchangeSchema({
771
+ exchangeName: "my-exchange",
772
+ getCandles: async (symbol, interval, since, limit) => {
773
+ const exchange = new ccxt.bybit({ enableRateLimit: true });
774
+ const ohlcv = await exchange.fetchOHLCV(symbol, interval, since.getTime(), limit);
775
+ return ohlcv.map(([timestamp, open, high, low, close, volume]) => ({
776
+ timestamp, open, high, low, close, volume,
777
+ }));
778
+ },
779
+ formatPrice: (symbol, price) => price.toFixed(2),
780
+ formatQuantity: (symbol, quantity) => quantity.toFixed(8),
781
+ });
782
+ ```
783
+
784
+ ### Environment Variables
785
+
786
+ | Variable | Default | Description |
787
+ |-------------------|-----------|----------------------------------|
788
+ | `CC_WWWROOT_HOST` | `0.0.0.0` | UI server bind address |
789
+ | `CC_WWWROOT_PORT` | `60050` | UI server port |
790
+
791
+ ### `package.json` script
792
+
793
+ ```json
794
+ {
795
+ "scripts": {
796
+ "editor": "npx @backtest-kit/cli --editor"
797
+ }
798
+ }
799
+ ```
800
+
801
+ ```bash
802
+ npm run editor
803
+ ```
804
+
739
805
  ## 💾 Dumping Raw Candles
740
806
 
741
807
  `@backtest-kit/cli` can fetch raw OHLCV candles from any registered exchange and save them to a file — no strategy file required.
@@ -828,6 +894,57 @@ Or add it to `package.json`:
828
894
  npx @backtest-kit/cli --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl
829
895
  ```
830
896
 
897
+ ## 🗑️ Flushing Strategy Output (`--flush`)
898
+
899
+ `@backtest-kit/cli` can delete generated output folders from one or more strategy dump directories without touching cached candle data.
900
+
901
+ ### CLI Flags
902
+
903
+ | Flag | Type | Description |
904
+ |------|------|-------------|
905
+ | `--flush` | boolean | Enable flush mode |
906
+
907
+ **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/`:
908
+
909
+ | Folder | Contents |
910
+ |--------|----------|
911
+ | `report` | Backtest report files (`.jsonl`) |
912
+ | `log` | Run logs (`log.jsonl`) |
913
+ | `markdown` | Exported Markdown reports |
914
+ | `agent` | Agent outline files |
915
+
916
+ Candle cache (`dump/data/`) and AI forecast outlines (`dump/outline/`) are **not** removed.
917
+
918
+ ### Usage
919
+
920
+ Flush a single strategy:
921
+
922
+ ```bash
923
+ npx @backtest-kit/cli --flush ./content/feb_2026.strategy/modules/backtest.module.ts
924
+ ```
925
+
926
+ Flush multiple strategies at once:
927
+
928
+ ```bash
929
+ npx @backtest-kit/cli --flush \
930
+ ./content/feb_2026.strategy/modules/backtest.module.ts \
931
+ ./content/mar_2026.strategy/modules/backtest.module.ts
932
+ ```
933
+
934
+ Or add it to `package.json`:
935
+
936
+ ```json
937
+ {
938
+ "scripts": {
939
+ "flush": "npx @backtest-kit/cli --flush ./content/feb_2026.strategy/modules/backtest.module.ts"
940
+ }
941
+ }
942
+ ```
943
+
944
+ ```bash
945
+ npm run flush
946
+ ```
947
+
831
948
  ## 🗂️ Scaffolding a New Project (`--init`)
832
949
 
833
950
  `@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 +1102,7 @@ await run(mode, args);
985
1102
  | `frame` | `string` | Frame name (default: first registered) |
986
1103
  | `cacheInterval` | `CandleInterval[]` | Intervals to pre-cache (default: `["1m","15m","30m","1h","4h"]`) |
987
1104
  | `noCache` | `boolean` | Skip candle cache warming (default: `false`) |
1105
+ | `noFlush` | `boolean` | Skip removing report/log/markdown/agent folders before the run (default: `false`) |
988
1106
  | `verbose` | `boolean` | Log each candle fetch (default: `false`) |
989
1107
 
990
1108
  **Paper** and **Live** (`mode: "paper"` / `mode: "live"`):
package/build/index.cjs CHANGED
@@ -29,6 +29,7 @@ var BacktestKitGraph = require('@backtest-kit/graph');
29
29
  var BacktestKitOllama = require('@backtest-kit/ollama');
30
30
  var BacktestKitPinets = require('@backtest-kit/pinets');
31
31
  var BacktestKitSignals = require('@backtest-kit/signals');
32
+ var open = require('open');
32
33
  var child_process = require('child_process');
33
34
 
34
35
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
@@ -473,11 +474,19 @@ const getArgs = functoolsKit.singleshot(() => {
473
474
  type: "boolean",
474
475
  default: false,
475
476
  },
477
+ noFlush: {
478
+ type: "boolean",
479
+ default: false,
480
+ },
476
481
  cacheInterval: {
477
482
  type: "string",
478
483
  default: "1m, 15m, 30m, 4h",
479
484
  },
480
485
  // pinescript entry
486
+ editor: {
487
+ type: "boolean",
488
+ default: false,
489
+ },
481
490
  pine: {
482
491
  type: "boolean",
483
492
  default: false,
@@ -486,6 +495,10 @@ const getArgs = functoolsKit.singleshot(() => {
486
495
  type: "boolean",
487
496
  default: false,
488
497
  },
498
+ flush: {
499
+ type: "boolean",
500
+ default: false,
501
+ },
489
502
  timeframe: {
490
503
  type: "string",
491
504
  default: "",
@@ -2461,14 +2474,27 @@ const cli = {
2461
2474
  };
2462
2475
  init();
2463
2476
 
2477
+ const NOTIFICATION_CONFIG = {
2478
+ signal: true,
2479
+ risk: true,
2480
+ info: true,
2481
+ breakeven: true,
2482
+ common_error: true,
2483
+ critical_error: true,
2484
+ validation_error: true,
2485
+ partial_loss: false,
2486
+ partial_profit: false,
2487
+ signal_sync: false,
2488
+ strategy_commit: true,
2489
+ };
2464
2490
  class SetupUtils {
2465
2491
  constructor() {
2466
2492
  this.enable = functoolsKit.singleshot(() => {
2467
2493
  cli.loggerService.debug("SetupUtils enable");
2494
+ BacktestKit.Notification.enable(NOTIFICATION_CONFIG);
2468
2495
  {
2469
2496
  BacktestKit.Recent.enable();
2470
2497
  BacktestKit.Storage.enable();
2471
- BacktestKit.Notification.enable();
2472
2498
  }
2473
2499
  {
2474
2500
  BacktestKit.Markdown.enable();
@@ -2590,14 +2616,14 @@ BacktestKit.setConfig({
2590
2616
  CC_WALKER_MARKDOWN_TOP_N: 10,
2591
2617
  });
2592
2618
 
2593
- const MODES = ["backtest", "walker", "paper", "live", "pine", "dump", "init", "help", "version"];
2619
+ const MODES = ["backtest", "walker", "paper", "live", "pine", "editor", "dump", "flush", "init", "help", "version"];
2594
2620
  const ENTRY_PATH$1 = "./node_modules/@backtest-kit/cli/build/index.mjs";
2595
2621
  const HELP_TEXT$1 = `
2596
2622
  Example:
2597
2623
 
2598
2624
  node ${ENTRY_PATH$1} --help
2599
2625
  `.trimStart();
2600
- const main$b = async () => {
2626
+ const main$d = async () => {
2601
2627
  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
2628
  return;
2603
2629
  }
@@ -2605,19 +2631,48 @@ const main$b = async () => {
2605
2631
  if (MODES.some((mode) => values[mode])) {
2606
2632
  return;
2607
2633
  }
2608
- process.stdout.write(`@backtest-kit/cli ${"6.14.0"}\n`);
2634
+ process.stdout.write(`@backtest-kit/cli ${"6.16.0"}\n`);
2609
2635
  process.stdout.write("\n");
2610
2636
  process.stdout.write(`Run with --help to see available commands.\n`);
2611
2637
  process.stdout.write("\n");
2612
2638
  process.stdout.write(HELP_TEXT$1);
2613
2639
  process.exit(0);
2614
2640
  };
2615
- main$b();
2641
+ main$d();
2616
2642
 
2617
2643
  const notifyShutdown = functoolsKit.singleshot(async () => {
2618
2644
  console.log("Graceful shutdown initiated. Press Ctrl+C again to force quit.");
2619
2645
  });
2620
2646
 
2647
+ const FLUSH_DIRS = ["report", "log", "markdown", "agent"];
2648
+ const flush = async (entryPoint) => {
2649
+ const moduleRoot = path.dirname(path.resolve(process.cwd(), entryPoint));
2650
+ const dumpDir = path.join(moduleRoot, "dump");
2651
+ for (const dir of FLUSH_DIRS) {
2652
+ const target = path.join(dumpDir, dir);
2653
+ await fs$1.rm(target, { recursive: true, force: true });
2654
+ console.log(`Removed: ${target}`);
2655
+ }
2656
+ };
2657
+ const main$c = async () => {
2658
+ 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)))) {
2659
+ return;
2660
+ }
2661
+ const { values } = getArgs();
2662
+ if (!values.flush) {
2663
+ return;
2664
+ }
2665
+ const entryPoints = getPositionals();
2666
+ if (!entryPoints.length) {
2667
+ throw new Error("Entry point is required");
2668
+ }
2669
+ for (const entryPoint of entryPoints) {
2670
+ await flush(entryPoint);
2671
+ }
2672
+ process.exit(0);
2673
+ };
2674
+ main$c();
2675
+
2621
2676
  const BEFORE_EXIT_FN$5 = functoolsKit.singleshot(async () => {
2622
2677
  process.off("SIGINT", BEFORE_EXIT_FN$5);
2623
2678
  const [running = null] = await BacktestKit.Backtest.list();
@@ -2638,7 +2693,7 @@ const BEFORE_EXIT_FN$5 = functoolsKit.singleshot(async () => {
2638
2693
  const listenGracefulShutdown$5 = functoolsKit.singleshot(() => {
2639
2694
  process.on("SIGINT", BEFORE_EXIT_FN$5);
2640
2695
  });
2641
- const main$a = async () => {
2696
+ const main$b = async () => {
2642
2697
  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)))) {
2643
2698
  return;
2644
2699
  }
@@ -2646,10 +2701,14 @@ const main$a = async () => {
2646
2701
  if (!values.backtest) {
2647
2702
  return;
2648
2703
  }
2704
+ if (!values.noFlush) {
2705
+ const [entryPoint = null] = getPositionals();
2706
+ entryPoint && await flush(entryPoint);
2707
+ }
2649
2708
  await cli.backtestMainService.connect();
2650
2709
  listenGracefulShutdown$5();
2651
2710
  };
2652
- main$a();
2711
+ main$b();
2653
2712
 
2654
2713
  const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
2655
2714
  process.off("SIGINT", BEFORE_EXIT_FN$4);
@@ -2667,7 +2726,7 @@ const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
2667
2726
  const listenGracefulShutdown$4 = functoolsKit.singleshot(() => {
2668
2727
  process.on("SIGINT", BEFORE_EXIT_FN$4);
2669
2728
  });
2670
- const main$9 = async () => {
2729
+ const main$a = async () => {
2671
2730
  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)))) {
2672
2731
  return;
2673
2732
  }
@@ -2675,10 +2734,15 @@ const main$9 = async () => {
2675
2734
  if (!values.walker) {
2676
2735
  return;
2677
2736
  }
2737
+ if (!values.noFlush) {
2738
+ for (const entryPoint of getPositionals()) {
2739
+ await flush(entryPoint);
2740
+ }
2741
+ }
2678
2742
  listenGracefulShutdown$4();
2679
2743
  await cli.walkerMainService.connect();
2680
2744
  };
2681
- main$9();
2745
+ main$a();
2682
2746
 
2683
2747
  const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
2684
2748
  process.off("SIGINT", BEFORE_EXIT_FN$3);
@@ -2699,7 +2763,7 @@ const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
2699
2763
  const listenGracefulShutdown$3 = functoolsKit.singleshot(() => {
2700
2764
  process.on("SIGINT", BEFORE_EXIT_FN$3);
2701
2765
  });
2702
- const main$8 = async () => {
2766
+ const main$9 = async () => {
2703
2767
  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)))) {
2704
2768
  return;
2705
2769
  }
@@ -2710,7 +2774,7 @@ const main$8 = async () => {
2710
2774
  cli.paperMainService.connect();
2711
2775
  listenGracefulShutdown$3();
2712
2776
  };
2713
- main$8();
2777
+ main$9();
2714
2778
 
2715
2779
  const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
2716
2780
  process.off("SIGINT", BEFORE_EXIT_FN$2);
@@ -2731,7 +2795,7 @@ const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
2731
2795
  const listenGracefulShutdown$2 = functoolsKit.singleshot(() => {
2732
2796
  process.on("SIGINT", BEFORE_EXIT_FN$2);
2733
2797
  });
2734
- const main$7 = async () => {
2798
+ const main$8 = async () => {
2735
2799
  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)))) {
2736
2800
  return;
2737
2801
  }
@@ -2742,7 +2806,7 @@ const main$7 = async () => {
2742
2806
  await cli.liveMainService.connect();
2743
2807
  listenGracefulShutdown$2();
2744
2808
  };
2745
- main$7();
2809
+ main$8();
2746
2810
 
2747
2811
  const BEFORE_EXIT_FN$1 = functoolsKit.singleshot(async () => {
2748
2812
  process.off("SIGINT", BEFORE_EXIT_FN$1);
@@ -2752,7 +2816,7 @@ const BEFORE_EXIT_FN$1 = functoolsKit.singleshot(async () => {
2752
2816
  const listenGracefulShutdown$1 = functoolsKit.singleshot(() => {
2753
2817
  process.on("SIGINT", BEFORE_EXIT_FN$1);
2754
2818
  });
2755
- const main$6 = async () => {
2819
+ const main$7 = async () => {
2756
2820
  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)))) {
2757
2821
  return;
2758
2822
  }
@@ -2762,7 +2826,7 @@ const main$6 = async () => {
2762
2826
  }
2763
2827
  listenGracefulShutdown$1();
2764
2828
  };
2765
- main$6();
2829
+ main$7();
2766
2830
 
2767
2831
  const BEFORE_EXIT_FN = functoolsKit.singleshot(async () => {
2768
2832
  process.off("SIGINT", BEFORE_EXIT_FN);
@@ -2772,7 +2836,7 @@ const BEFORE_EXIT_FN = functoolsKit.singleshot(async () => {
2772
2836
  const listenGracefulShutdown = functoolsKit.singleshot(() => {
2773
2837
  process.on("SIGINT", BEFORE_EXIT_FN);
2774
2838
  });
2775
- const main$5 = async () => {
2839
+ const main$6 = async () => {
2776
2840
  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)))) {
2777
2841
  return;
2778
2842
  }
@@ -2782,7 +2846,7 @@ const main$5 = async () => {
2782
2846
  }
2783
2847
  listenGracefulShutdown();
2784
2848
  };
2785
- main$5();
2849
+ main$6();
2786
2850
 
2787
2851
  const EXTRACT_ROWS_FN = (plots, schema) => {
2788
2852
  const keys = Object.keys(schema);
@@ -2804,7 +2868,7 @@ const EXTRACT_ROWS_FN = (plots, schema) => {
2804
2868
  }
2805
2869
  return rows;
2806
2870
  };
2807
- const main$4 = async () => {
2871
+ const main$5 = async () => {
2808
2872
  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)))) {
2809
2873
  return;
2810
2874
  }
@@ -2812,6 +2876,10 @@ const main$4 = async () => {
2812
2876
  if (!values.pine) {
2813
2877
  return;
2814
2878
  }
2879
+ if (values.editor) {
2880
+ console.warn("--pine and --editor are mutually exclusive. Use one at a time.");
2881
+ process.exit(1);
2882
+ }
2815
2883
  const [entryPoint = null] = getPositionals();
2816
2884
  if (!entryPoint) {
2817
2885
  return;
@@ -2880,6 +2948,36 @@ const main$4 = async () => {
2880
2948
  console.log(await BacktestKitPinets.toMarkdown(signalId, plots, signalSchema));
2881
2949
  process.exit(0);
2882
2950
  };
2951
+ main$5();
2952
+
2953
+ const main$4 = async () => {
2954
+ 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)))) {
2955
+ return;
2956
+ }
2957
+ const { values } = getArgs();
2958
+ if (!values.editor) {
2959
+ return;
2960
+ }
2961
+ if (values.pine) {
2962
+ console.warn("--editor and --pine are mutually exclusive. Use one at a time.");
2963
+ process.exit(1);
2964
+ }
2965
+ await cli.moduleConnectionService.loadModule("./editor.module");
2966
+ const { CC_WWWROOT_HOST, CC_WWWROOT_PORT } = getEnv();
2967
+ const unServer = BacktestKitUi.serve(CC_WWWROOT_HOST, CC_WWWROOT_PORT, cli.resolveService.PROJECT_ROOT_DIR);
2968
+ try {
2969
+ await open(`http://localhost:${CC_WWWROOT_PORT}?pine=1`);
2970
+ }
2971
+ catch {
2972
+ console.log(`Editor launched: http://localhost:${CC_WWWROOT_PORT}?pine=1`);
2973
+ }
2974
+ const beforeExit = () => {
2975
+ process.off("SIGINT", beforeExit);
2976
+ unServer();
2977
+ process.exit(0);
2978
+ };
2979
+ process.on("SIGINT", beforeExit);
2980
+ };
2883
2981
  main$4();
2884
2982
 
2885
2983
  const main$3 = async () => {
@@ -3059,7 +3157,9 @@ Modes:
3059
3157
  --paper <entry> Paper trading (live prices, no real orders)
3060
3158
  --live <entry> Live trading with real orders
3061
3159
  --pine <entry> Execute a local .pine indicator file
3160
+ --editor Open the Pine Script visual editor in the browser
3062
3161
  --dump Fetch and save raw OHLCV candles
3162
+ --flush <entry...> Delete report/log/markdown/agent folders from strategy dump dir
3063
3163
  --init Scaffold a new project in the current directory
3064
3164
  --help Print this help message
3065
3165
 
@@ -3071,6 +3171,7 @@ Backtest flags:
3071
3171
  --frame <string> Frame name from addFrameSchema (default: first registered)
3072
3172
  --cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
3073
3173
  --noCache Skip candle cache warming before the run
3174
+ --noFlush Skip removing report/log/markdown/agent folders before backtest run
3074
3175
  --verbose Log every candle fetch to stdout
3075
3176
  --ui Start web dashboard at http://localhost:60050
3076
3177
  --telegram Send trade notifications to Telegram
@@ -3080,6 +3181,7 @@ Walker flags (--walker):
3080
3181
  --symbol <string> Trading pair (default: BTCUSDT)
3081
3182
  --cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
3082
3183
  --noCache Skip candle cache warming before the run
3184
+ --noFlush Skip removing report/log/markdown/agent folders before walker run
3083
3185
  --verbose Log every candle fetch to stdout
3084
3186
  --output <string> Output file base name (default: walker_{SYMBOL}_{TIMESTAMP})
3085
3187
  --json Save results as JSON to ./dump/<output>.json
@@ -3129,6 +3231,13 @@ Candle dump flags (--dump):
3129
3231
 
3130
3232
  Module file ./modules/dump.module is loaded automatically if it exists.
3131
3233
 
3234
+ Flush flags (--flush):
3235
+
3236
+ One or more positional entry points. For each entry point the following
3237
+ subdirectories are removed from <entry-dir>/dump/:
3238
+
3239
+ report log markdown agent
3240
+
3132
3241
  Init flags (--init):
3133
3242
 
3134
3243
  --output <string> Target directory name (default: backtest-kit-project)
@@ -3142,8 +3251,11 @@ Module hooks (loaded automatically by each mode):
3142
3251
  modules/paper.module --paper Broker adapter for paper trading
3143
3252
  modules/live.module --live Broker adapter for live trading
3144
3253
  modules/pine.module --pine Exchange schema for PineScript runs
3254
+ modules/editor.module --editor Exchange schema for the visual Pine editor
3145
3255
  modules/dump.module --dump Exchange schema for candle dumps
3146
3256
 
3257
+ --flush has no associated module. It only removes dump subdirectories.
3258
+
3147
3259
  Extensions .ts, .mjs, .cjs are tried automatically. Missing module = soft warning.
3148
3260
 
3149
3261
  Environment variables:
@@ -3156,13 +3268,16 @@ Environment variables:
3156
3268
  Examples:
3157
3269
 
3158
3270
  node ${ENTRY_PATH} --backtest ./content/feb_2026.strategy.ts
3159
- node ${ENTRY_PATH} --backtest --symbol BTCUSDT --noCache --ui ./content/feb_2026.strategy.ts
3271
+ node ${ENTRY_PATH} --backtest --symbol BTCUSDT --noCache --noFlush --ui ./content/feb_2026.strategy.ts
3160
3272
  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
3273
+ node ${ENTRY_PATH} --walker --symbol BTCUSDT --noCache --noFlush --markdown ./content/feb_2026_v1.ts ./content/feb_2026_v2.ts
3162
3274
  node ${ENTRY_PATH} --paper --symbol ETHUSDT ./content/feb_2026.strategy.ts
3163
3275
  node ${ENTRY_PATH} --live --ui --telegram ./content/feb_2026.strategy.ts
3164
3276
  node ${ENTRY_PATH} --pine ./math/feb_2026.pine --timeframe 15m --limit 500 --jsonl
3277
+ node ${ENTRY_PATH} --editor
3165
3278
  node ${ENTRY_PATH} --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl
3279
+ node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts
3280
+ node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts ./content/feb_2026.strategy/feb_2026.test.ts
3166
3281
  node ${ENTRY_PATH} --init --output my-trading-bot
3167
3282
  `.trimStart();
3168
3283
  const main$1 = async () => {
@@ -3173,7 +3288,7 @@ const main$1 = async () => {
3173
3288
  if (!values.help) {
3174
3289
  return;
3175
3290
  }
3176
- process.stdout.write(`@backtest-kit/cli ${"6.14.0"}\n\n`);
3291
+ process.stdout.write(`@backtest-kit/cli ${"6.16.0"}\n\n`);
3177
3292
  process.stdout.write(HELP_TEXT);
3178
3293
  process.exit(0);
3179
3294
  };
@@ -3187,7 +3302,7 @@ const main = async () => {
3187
3302
  if (!values.version) {
3188
3303
  return;
3189
3304
  }
3190
- process.stdout.write(`@backtest-kit/cli ${"6.14.0"}\n`);
3305
+ process.stdout.write(`@backtest-kit/cli ${"6.16.0"}\n`);
3191
3306
  process.exit(0);
3192
3307
  };
3193
3308
  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, Notification, 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';
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, dirname } from 'path';
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';
@@ -30,6 +30,7 @@ import * as BacktestKitOllama from '@backtest-kit/ollama';
30
30
  import * as BacktestKitPinets from '@backtest-kit/pinets';
31
31
  import { run as run$1, Code, toMarkdown } from '@backtest-kit/pinets';
32
32
  import * as BacktestKitSignals from '@backtest-kit/signals';
33
+ import open from 'open';
33
34
  import { spawn } from 'child_process';
34
35
 
35
36
  /**
@@ -448,11 +449,19 @@ const getArgs = singleshot(() => {
448
449
  type: "boolean",
449
450
  default: false,
450
451
  },
452
+ noFlush: {
453
+ type: "boolean",
454
+ default: false,
455
+ },
451
456
  cacheInterval: {
452
457
  type: "string",
453
458
  default: "1m, 15m, 30m, 4h",
454
459
  },
455
460
  // pinescript entry
461
+ editor: {
462
+ type: "boolean",
463
+ default: false,
464
+ },
456
465
  pine: {
457
466
  type: "boolean",
458
467
  default: false,
@@ -461,6 +470,10 @@ const getArgs = singleshot(() => {
461
470
  type: "boolean",
462
471
  default: false,
463
472
  },
473
+ flush: {
474
+ type: "boolean",
475
+ default: false,
476
+ },
464
477
  timeframe: {
465
478
  type: "string",
466
479
  default: "",
@@ -2432,14 +2445,27 @@ const cli = {
2432
2445
  };
2433
2446
  init();
2434
2447
 
2448
+ const NOTIFICATION_CONFIG = {
2449
+ signal: true,
2450
+ risk: true,
2451
+ info: true,
2452
+ breakeven: true,
2453
+ common_error: true,
2454
+ critical_error: true,
2455
+ validation_error: true,
2456
+ partial_loss: false,
2457
+ partial_profit: false,
2458
+ signal_sync: false,
2459
+ strategy_commit: true,
2460
+ };
2435
2461
  class SetupUtils {
2436
2462
  constructor() {
2437
2463
  this.enable = singleshot(() => {
2438
2464
  cli.loggerService.debug("SetupUtils enable");
2465
+ Notification.enable(NOTIFICATION_CONFIG);
2439
2466
  {
2440
2467
  Recent.enable();
2441
2468
  Storage.enable();
2442
- Notification.enable();
2443
2469
  }
2444
2470
  {
2445
2471
  Markdown.enable();
@@ -2561,14 +2587,14 @@ setConfig({
2561
2587
  CC_WALKER_MARKDOWN_TOP_N: 10,
2562
2588
  });
2563
2589
 
2564
- const MODES = ["backtest", "walker", "paper", "live", "pine", "dump", "init", "help", "version"];
2590
+ const MODES = ["backtest", "walker", "paper", "live", "pine", "editor", "dump", "flush", "init", "help", "version"];
2565
2591
  const ENTRY_PATH$1 = "./node_modules/@backtest-kit/cli/build/index.mjs";
2566
2592
  const HELP_TEXT$1 = `
2567
2593
  Example:
2568
2594
 
2569
2595
  node ${ENTRY_PATH$1} --help
2570
2596
  `.trimStart();
2571
- const main$b = async () => {
2597
+ const main$d = async () => {
2572
2598
  if (!getEntry(import.meta.url)) {
2573
2599
  return;
2574
2600
  }
@@ -2576,19 +2602,48 @@ const main$b = async () => {
2576
2602
  if (MODES.some((mode) => values[mode])) {
2577
2603
  return;
2578
2604
  }
2579
- process.stdout.write(`@backtest-kit/cli ${"6.14.0"}\n`);
2605
+ process.stdout.write(`@backtest-kit/cli ${"6.16.0"}\n`);
2580
2606
  process.stdout.write("\n");
2581
2607
  process.stdout.write(`Run with --help to see available commands.\n`);
2582
2608
  process.stdout.write("\n");
2583
2609
  process.stdout.write(HELP_TEXT$1);
2584
2610
  process.exit(0);
2585
2611
  };
2586
- main$b();
2612
+ main$d();
2587
2613
 
2588
2614
  const notifyShutdown = singleshot(async () => {
2589
2615
  console.log("Graceful shutdown initiated. Press Ctrl+C again to force quit.");
2590
2616
  });
2591
2617
 
2618
+ const FLUSH_DIRS = ["report", "log", "markdown", "agent"];
2619
+ const flush = async (entryPoint) => {
2620
+ const moduleRoot = dirname(resolve(process.cwd(), entryPoint));
2621
+ const dumpDir = join(moduleRoot, "dump");
2622
+ for (const dir of FLUSH_DIRS) {
2623
+ const target = join(dumpDir, dir);
2624
+ await rm(target, { recursive: true, force: true });
2625
+ console.log(`Removed: ${target}`);
2626
+ }
2627
+ };
2628
+ const main$c = async () => {
2629
+ if (!getEntry(import.meta.url)) {
2630
+ return;
2631
+ }
2632
+ const { values } = getArgs();
2633
+ if (!values.flush) {
2634
+ return;
2635
+ }
2636
+ const entryPoints = getPositionals();
2637
+ if (!entryPoints.length) {
2638
+ throw new Error("Entry point is required");
2639
+ }
2640
+ for (const entryPoint of entryPoints) {
2641
+ await flush(entryPoint);
2642
+ }
2643
+ process.exit(0);
2644
+ };
2645
+ main$c();
2646
+
2592
2647
  const BEFORE_EXIT_FN$5 = singleshot(async () => {
2593
2648
  process.off("SIGINT", BEFORE_EXIT_FN$5);
2594
2649
  const [running = null] = await Backtest.list();
@@ -2609,7 +2664,7 @@ const BEFORE_EXIT_FN$5 = singleshot(async () => {
2609
2664
  const listenGracefulShutdown$5 = singleshot(() => {
2610
2665
  process.on("SIGINT", BEFORE_EXIT_FN$5);
2611
2666
  });
2612
- const main$a = async () => {
2667
+ const main$b = async () => {
2613
2668
  if (!getEntry(import.meta.url)) {
2614
2669
  return;
2615
2670
  }
@@ -2617,10 +2672,14 @@ const main$a = async () => {
2617
2672
  if (!values.backtest) {
2618
2673
  return;
2619
2674
  }
2675
+ if (!values.noFlush) {
2676
+ const [entryPoint = null] = getPositionals();
2677
+ entryPoint && await flush(entryPoint);
2678
+ }
2620
2679
  await cli.backtestMainService.connect();
2621
2680
  listenGracefulShutdown$5();
2622
2681
  };
2623
- main$a();
2682
+ main$b();
2624
2683
 
2625
2684
  const BEFORE_EXIT_FN$4 = singleshot(async () => {
2626
2685
  process.off("SIGINT", BEFORE_EXIT_FN$4);
@@ -2638,7 +2697,7 @@ const BEFORE_EXIT_FN$4 = singleshot(async () => {
2638
2697
  const listenGracefulShutdown$4 = singleshot(() => {
2639
2698
  process.on("SIGINT", BEFORE_EXIT_FN$4);
2640
2699
  });
2641
- const main$9 = async () => {
2700
+ const main$a = async () => {
2642
2701
  if (!getEntry(import.meta.url)) {
2643
2702
  return;
2644
2703
  }
@@ -2646,10 +2705,15 @@ const main$9 = async () => {
2646
2705
  if (!values.walker) {
2647
2706
  return;
2648
2707
  }
2708
+ if (!values.noFlush) {
2709
+ for (const entryPoint of getPositionals()) {
2710
+ await flush(entryPoint);
2711
+ }
2712
+ }
2649
2713
  listenGracefulShutdown$4();
2650
2714
  await cli.walkerMainService.connect();
2651
2715
  };
2652
- main$9();
2716
+ main$a();
2653
2717
 
2654
2718
  const BEFORE_EXIT_FN$3 = singleshot(async () => {
2655
2719
  process.off("SIGINT", BEFORE_EXIT_FN$3);
@@ -2670,7 +2734,7 @@ const BEFORE_EXIT_FN$3 = singleshot(async () => {
2670
2734
  const listenGracefulShutdown$3 = singleshot(() => {
2671
2735
  process.on("SIGINT", BEFORE_EXIT_FN$3);
2672
2736
  });
2673
- const main$8 = async () => {
2737
+ const main$9 = async () => {
2674
2738
  if (!getEntry(import.meta.url)) {
2675
2739
  return;
2676
2740
  }
@@ -2681,7 +2745,7 @@ const main$8 = async () => {
2681
2745
  cli.paperMainService.connect();
2682
2746
  listenGracefulShutdown$3();
2683
2747
  };
2684
- main$8();
2748
+ main$9();
2685
2749
 
2686
2750
  const BEFORE_EXIT_FN$2 = singleshot(async () => {
2687
2751
  process.off("SIGINT", BEFORE_EXIT_FN$2);
@@ -2702,7 +2766,7 @@ const BEFORE_EXIT_FN$2 = singleshot(async () => {
2702
2766
  const listenGracefulShutdown$2 = singleshot(() => {
2703
2767
  process.on("SIGINT", BEFORE_EXIT_FN$2);
2704
2768
  });
2705
- const main$7 = async () => {
2769
+ const main$8 = async () => {
2706
2770
  if (!getEntry(import.meta.url)) {
2707
2771
  return;
2708
2772
  }
@@ -2713,7 +2777,7 @@ const main$7 = async () => {
2713
2777
  await cli.liveMainService.connect();
2714
2778
  listenGracefulShutdown$2();
2715
2779
  };
2716
- main$7();
2780
+ main$8();
2717
2781
 
2718
2782
  const BEFORE_EXIT_FN$1 = singleshot(async () => {
2719
2783
  process.off("SIGINT", BEFORE_EXIT_FN$1);
@@ -2723,7 +2787,7 @@ const BEFORE_EXIT_FN$1 = singleshot(async () => {
2723
2787
  const listenGracefulShutdown$1 = singleshot(() => {
2724
2788
  process.on("SIGINT", BEFORE_EXIT_FN$1);
2725
2789
  });
2726
- const main$6 = async () => {
2790
+ const main$7 = async () => {
2727
2791
  if (!getEntry(import.meta.url)) {
2728
2792
  return;
2729
2793
  }
@@ -2733,7 +2797,7 @@ const main$6 = async () => {
2733
2797
  }
2734
2798
  listenGracefulShutdown$1();
2735
2799
  };
2736
- main$6();
2800
+ main$7();
2737
2801
 
2738
2802
  const BEFORE_EXIT_FN = singleshot(async () => {
2739
2803
  process.off("SIGINT", BEFORE_EXIT_FN);
@@ -2743,7 +2807,7 @@ const BEFORE_EXIT_FN = singleshot(async () => {
2743
2807
  const listenGracefulShutdown = singleshot(() => {
2744
2808
  process.on("SIGINT", BEFORE_EXIT_FN);
2745
2809
  });
2746
- const main$5 = async () => {
2810
+ const main$6 = async () => {
2747
2811
  if (!getEntry(import.meta.url)) {
2748
2812
  return;
2749
2813
  }
@@ -2753,7 +2817,7 @@ const main$5 = async () => {
2753
2817
  }
2754
2818
  listenGracefulShutdown();
2755
2819
  };
2756
- main$5();
2820
+ main$6();
2757
2821
 
2758
2822
  const EXTRACT_ROWS_FN = (plots, schema) => {
2759
2823
  const keys = Object.keys(schema);
@@ -2775,7 +2839,7 @@ const EXTRACT_ROWS_FN = (plots, schema) => {
2775
2839
  }
2776
2840
  return rows;
2777
2841
  };
2778
- const main$4 = async () => {
2842
+ const main$5 = async () => {
2779
2843
  if (!getEntry(import.meta.url)) {
2780
2844
  return;
2781
2845
  }
@@ -2783,6 +2847,10 @@ const main$4 = async () => {
2783
2847
  if (!values.pine) {
2784
2848
  return;
2785
2849
  }
2850
+ if (values.editor) {
2851
+ console.warn("--pine and --editor are mutually exclusive. Use one at a time.");
2852
+ process.exit(1);
2853
+ }
2786
2854
  const [entryPoint = null] = getPositionals();
2787
2855
  if (!entryPoint) {
2788
2856
  return;
@@ -2851,6 +2919,36 @@ const main$4 = async () => {
2851
2919
  console.log(await toMarkdown(signalId, plots, signalSchema));
2852
2920
  process.exit(0);
2853
2921
  };
2922
+ main$5();
2923
+
2924
+ const main$4 = async () => {
2925
+ if (!getEntry(import.meta.url)) {
2926
+ return;
2927
+ }
2928
+ const { values } = getArgs();
2929
+ if (!values.editor) {
2930
+ return;
2931
+ }
2932
+ if (values.pine) {
2933
+ console.warn("--editor and --pine are mutually exclusive. Use one at a time.");
2934
+ process.exit(1);
2935
+ }
2936
+ await cli.moduleConnectionService.loadModule("./editor.module");
2937
+ const { CC_WWWROOT_HOST, CC_WWWROOT_PORT } = getEnv();
2938
+ const unServer = serve(CC_WWWROOT_HOST, CC_WWWROOT_PORT, cli.resolveService.PROJECT_ROOT_DIR);
2939
+ try {
2940
+ await open(`http://localhost:${CC_WWWROOT_PORT}?pine=1`);
2941
+ }
2942
+ catch {
2943
+ console.log(`Editor launched: http://localhost:${CC_WWWROOT_PORT}?pine=1`);
2944
+ }
2945
+ const beforeExit = () => {
2946
+ process.off("SIGINT", beforeExit);
2947
+ unServer();
2948
+ process.exit(0);
2949
+ };
2950
+ process.on("SIGINT", beforeExit);
2951
+ };
2854
2952
  main$4();
2855
2953
 
2856
2954
  const main$3 = async () => {
@@ -3030,7 +3128,9 @@ Modes:
3030
3128
  --paper <entry> Paper trading (live prices, no real orders)
3031
3129
  --live <entry> Live trading with real orders
3032
3130
  --pine <entry> Execute a local .pine indicator file
3131
+ --editor Open the Pine Script visual editor in the browser
3033
3132
  --dump Fetch and save raw OHLCV candles
3133
+ --flush <entry...> Delete report/log/markdown/agent folders from strategy dump dir
3034
3134
  --init Scaffold a new project in the current directory
3035
3135
  --help Print this help message
3036
3136
 
@@ -3042,6 +3142,7 @@ Backtest flags:
3042
3142
  --frame <string> Frame name from addFrameSchema (default: first registered)
3043
3143
  --cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
3044
3144
  --noCache Skip candle cache warming before the run
3145
+ --noFlush Skip removing report/log/markdown/agent folders before backtest run
3045
3146
  --verbose Log every candle fetch to stdout
3046
3147
  --ui Start web dashboard at http://localhost:60050
3047
3148
  --telegram Send trade notifications to Telegram
@@ -3051,6 +3152,7 @@ Walker flags (--walker):
3051
3152
  --symbol <string> Trading pair (default: BTCUSDT)
3052
3153
  --cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
3053
3154
  --noCache Skip candle cache warming before the run
3155
+ --noFlush Skip removing report/log/markdown/agent folders before walker run
3054
3156
  --verbose Log every candle fetch to stdout
3055
3157
  --output <string> Output file base name (default: walker_{SYMBOL}_{TIMESTAMP})
3056
3158
  --json Save results as JSON to ./dump/<output>.json
@@ -3100,6 +3202,13 @@ Candle dump flags (--dump):
3100
3202
 
3101
3203
  Module file ./modules/dump.module is loaded automatically if it exists.
3102
3204
 
3205
+ Flush flags (--flush):
3206
+
3207
+ One or more positional entry points. For each entry point the following
3208
+ subdirectories are removed from <entry-dir>/dump/:
3209
+
3210
+ report log markdown agent
3211
+
3103
3212
  Init flags (--init):
3104
3213
 
3105
3214
  --output <string> Target directory name (default: backtest-kit-project)
@@ -3113,8 +3222,11 @@ Module hooks (loaded automatically by each mode):
3113
3222
  modules/paper.module --paper Broker adapter for paper trading
3114
3223
  modules/live.module --live Broker adapter for live trading
3115
3224
  modules/pine.module --pine Exchange schema for PineScript runs
3225
+ modules/editor.module --editor Exchange schema for the visual Pine editor
3116
3226
  modules/dump.module --dump Exchange schema for candle dumps
3117
3227
 
3228
+ --flush has no associated module. It only removes dump subdirectories.
3229
+
3118
3230
  Extensions .ts, .mjs, .cjs are tried automatically. Missing module = soft warning.
3119
3231
 
3120
3232
  Environment variables:
@@ -3127,13 +3239,16 @@ Environment variables:
3127
3239
  Examples:
3128
3240
 
3129
3241
  node ${ENTRY_PATH} --backtest ./content/feb_2026.strategy.ts
3130
- node ${ENTRY_PATH} --backtest --symbol BTCUSDT --noCache --ui ./content/feb_2026.strategy.ts
3242
+ node ${ENTRY_PATH} --backtest --symbol BTCUSDT --noCache --noFlush --ui ./content/feb_2026.strategy.ts
3131
3243
  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
3244
+ node ${ENTRY_PATH} --walker --symbol BTCUSDT --noCache --noFlush --markdown ./content/feb_2026_v1.ts ./content/feb_2026_v2.ts
3133
3245
  node ${ENTRY_PATH} --paper --symbol ETHUSDT ./content/feb_2026.strategy.ts
3134
3246
  node ${ENTRY_PATH} --live --ui --telegram ./content/feb_2026.strategy.ts
3135
3247
  node ${ENTRY_PATH} --pine ./math/feb_2026.pine --timeframe 15m --limit 500 --jsonl
3248
+ node ${ENTRY_PATH} --editor
3136
3249
  node ${ENTRY_PATH} --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl
3250
+ node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts
3251
+ node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts ./content/feb_2026.strategy/feb_2026.test.ts
3137
3252
  node ${ENTRY_PATH} --init --output my-trading-bot
3138
3253
  `.trimStart();
3139
3254
  const main$1 = async () => {
@@ -3144,7 +3259,7 @@ const main$1 = async () => {
3144
3259
  if (!values.help) {
3145
3260
  return;
3146
3261
  }
3147
- process.stdout.write(`@backtest-kit/cli ${"6.14.0"}\n\n`);
3262
+ process.stdout.write(`@backtest-kit/cli ${"6.16.0"}\n\n`);
3148
3263
  process.stdout.write(HELP_TEXT);
3149
3264
  process.exit(0);
3150
3265
  };
@@ -3158,7 +3273,7 @@ const main = async () => {
3158
3273
  if (!values.version) {
3159
3274
  return;
3160
3275
  }
3161
- process.stdout.write(`@backtest-kit/cli ${"6.14.0"}\n`);
3276
+ process.stdout.write(`@backtest-kit/cli ${"6.16.0"}\n`);
3162
3277
  process.exit(0);
3163
3278
  };
3164
3279
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backtest-kit/cli",
3
- "version": "6.14.0",
3
+ "version": "6.16.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.14.0",
65
- "@backtest-kit/graph": "6.14.0",
66
- "@backtest-kit/ollama": "6.14.0",
67
- "@backtest-kit/pinets": "6.14.0",
68
- "@backtest-kit/signals": "6.14.0",
64
+ "@backtest-kit/graph": "6.16.0",
65
+ "@backtest-kit/ollama": "6.16.0",
66
+ "@backtest-kit/pinets": "6.16.0",
67
+ "@backtest-kit/signals": "6.16.0",
68
+ "@backtest-kit/ui": "6.16.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.14.0",
76
+ "backtest-kit": "6.16.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.14.0",
92
- "@backtest-kit/graph": "^6.14.0",
93
- "@backtest-kit/ollama": "^6.14.0",
94
- "@backtest-kit/pinets": "^6.14.0",
95
- "@backtest-kit/signals": "^6.14.0",
96
- "backtest-kit": "^6.14.0",
91
+ "@backtest-kit/graph": "^6.16.0",
92
+ "@backtest-kit/ollama": "^6.16.0",
93
+ "@backtest-kit/pinets": "^6.16.0",
94
+ "@backtest-kit/signals": "^6.16.0",
95
+ "@backtest-kit/ui": "^6.16.0",
96
+ "backtest-kit": "^6.16.0",
97
97
  "markdown-it": "^14.1.1",
98
98
  "typescript": "^5.0.0"
99
99
  },
@@ -108,6 +108,7 @@
108
108
  "jsdom": "26.1.0",
109
109
  "markdownlint": "0.38.0",
110
110
  "mustache": "4.2.0",
111
+ "open": "11.0.0",
111
112
  "quickchart-js": "3.1.3",
112
113
  "resize-image-buffer": "1.0.0",
113
114
  "sanitize-html": "2.17.0",
@@ -12,12 +12,12 @@
12
12
  "license": "ISC",
13
13
  "type": "commonjs",
14
14
  "dependencies": {
15
- "@backtest-kit/cli": "^6.14.0",
16
- "@backtest-kit/graph": "^6.14.0",
17
- "@backtest-kit/pinets": "^6.14.0",
18
- "@backtest-kit/ui": "^6.14.0",
19
- "agent-swarm-kit": "^2.5.0",
20
- "backtest-kit": "^6.14.0",
15
+ "@backtest-kit/cli": "^6.16.0",
16
+ "@backtest-kit/graph": "^6.16.0",
17
+ "@backtest-kit/pinets": "^6.16.0",
18
+ "@backtest-kit/ui": "^6.16.0",
19
+ "agent-swarm-kit": "^2.5.1",
20
+ "backtest-kit": "^6.16.0",
21
21
  "functools-kit": "^2.2.0",
22
22
  "garch": "^1.2.3",
23
23
  "get-moment-stamp": "^1.1.2",