@backtest-kit/cli 0.0.4 → 0.0.5

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
@@ -137,11 +137,12 @@ npm start -- --symbol BTCUSDT --ui
137
137
  | `--ui` | boolean | Start web UI dashboard (default: `false`) |
138
138
  | `--telegram` | boolean | Enable Telegram notifications (default: `false`) |
139
139
  | `--verbose` | boolean | Log each candle fetch (default: `false`) |
140
+ | `--noCache` | boolean | Skip candle cache warming before backtest (default: `false`) |
140
141
  | `--symbol` | string | Trading pair (default: `"BTCUSDT"`) |
141
142
  | `--strategy` | string | Strategy name (default: first registered) |
142
143
  | `--exchange` | string | Exchange name (default: first registered) |
143
144
  | `--frame` | string | Backtest frame name (default: first registered) |
144
- | `--cache` | string | Intervals to pre-cache before backtest (default: `"1m, 15m, 30m, 4h"`) |
145
+ | `--cacheInterval` | string | Intervals to pre-cache before backtest (default: `"1m, 15m, 30m, 4h"`) |
145
146
 
146
147
  **Positional argument (required):** path to your strategy entry point file (set once in `package.json` scripts).
147
148
 
@@ -162,7 +163,7 @@ Runs the strategy against historical candle data using a registered `FrameSchema
162
163
  ```json
163
164
  {
164
165
  "scripts": {
165
- "backtest": "@backtest-kit/cli --backtest --symbol ETHUSDT --strategy my-strategy --exchange binance --frame feb-2024 --cache \"1m, 15m, 1h, 4h\" ./src/index.mjs"
166
+ "backtest": "@backtest-kit/cli --backtest --symbol ETHUSDT --strategy my-strategy --exchange binance --frame feb-2024 --cacheInterval \"1m, 15m, 1h, 4h\" ./src/index.mjs"
166
167
  }
167
168
  }
168
169
  ```
@@ -171,7 +172,7 @@ Runs the strategy against historical candle data using a registered `FrameSchema
171
172
  npm run backtest
172
173
  ```
173
174
 
174
- Before running, the CLI warms the candle cache for every interval in `--cache`. On the next run, cached data is used directly — no API calls needed.
175
+ 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.
175
176
 
176
177
  ### Paper Trading
177
178
 
@@ -389,7 +390,7 @@ When your strategy module does not register an exchange, frame, or strategy name
389
390
  | **Exchange** | CCXT Binance (`default_exchange`) | `Warning: The default exchange schema is set to CCXT Binance...` |
390
391
  | **Frame** | February 2024 (`default_frame`) | `Warning: The default frame schema is set to February 2024...` |
391
392
  | **Symbol** | `BTCUSDT` | — |
392
- | **Cache intervals** | `1m, 15m, 30m, 4h` | Shown if `--cache` not provided |
393
+ | **Cache intervals** | `1m, 15m, 30m, 4h` | Used if `--cacheInterval` not provided; skip entirely with `--noCache` |
393
394
 
394
395
  > **Note:** The default exchange schema **does not support order book fetching in backtest mode**. If your strategy calls `getOrderBook()` during backtest, you must register a custom exchange schema with your own snapshot storage.
395
396
 
@@ -424,7 +425,8 @@ await run(mode, args);
424
425
  | `strategy` | `string` | Strategy name (default: first registered) |
425
426
  | `exchange` | `string` | Exchange name (default: first registered) |
426
427
  | `frame` | `string` | Frame name (default: first registered) |
427
- | `cacheList` | `CandleInterval[]` | Intervals to pre-cache (default: `["1m","15m","30m","1h","4h"]`) |
428
+ | `cacheInterval` | `CandleInterval[]` | Intervals to pre-cache (default: `["1m","15m","30m","1h","4h"]`) |
429
+ | `noCache` | `boolean` | Skip candle cache warming (default: `false`) |
428
430
  | `verbose` | `boolean` | Log each candle fetch (default: `false`) |
429
431
 
430
432
  **Paper** and **Live** (`mode: "paper"` / `mode: "live"`):
@@ -448,7 +450,7 @@ await run('backtest', {
448
450
  entryPoint: './src/index.mjs',
449
451
  symbol: 'ETHUSDT',
450
452
  frame: 'feb-2024',
451
- cacheList: ['1m', '15m', '1h'],
453
+ cacheInterval: ['1m', '15m', '1h'],
452
454
  verbose: true,
453
455
  });
454
456
  ```
package/build/index.cjs CHANGED
@@ -6,6 +6,7 @@ var functoolsKit = require('functools-kit');
6
6
  var fs = require('fs');
7
7
  var stackTrace = require('stack-trace');
8
8
  var url = require('url');
9
+ var module$1 = require('module');
9
10
  var path = require('path');
10
11
  var fs$1 = require('fs/promises');
11
12
  var dotenv = require('dotenv');
@@ -22,7 +23,6 @@ var MarkdownIt = require('markdown-it');
22
23
  var sanitizeHtml = require('sanitize-html');
23
24
  var jsdom = require('jsdom');
24
25
  var Mustache = require('mustache');
25
- var module$1 = require('module');
26
26
 
27
27
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
28
28
  function _interopNamespaceDefault(e) {
@@ -84,7 +84,7 @@ var stackTrace__namespace = /*#__PURE__*/_interopNamespaceDefault(stackTrace);
84
84
  }
85
85
  {
86
86
  backtestKit.StorageLive.usePersist();
87
- backtestKit.StorageBacktest.useDummy();
87
+ backtestKit.StorageBacktest.useMemory();
88
88
  }
89
89
  {
90
90
  backtestKit.NotificationLive.useDummy();
@@ -232,6 +232,24 @@ const entrySubject = new functoolsKit.BehaviorSubject();
232
232
 
233
233
  const __filename$1 = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
234
234
  const __dirname$1 = path.dirname(__filename$1);
235
+ const require$2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
236
+ const REQUIRE_ENTRY_FACTORY = (filePath) => {
237
+ try {
238
+ require$2(filePath);
239
+ return true;
240
+ }
241
+ catch {
242
+ return false;
243
+ }
244
+ };
245
+ const IMPORT_ENTRY_FACTORY = async (filePath) => {
246
+ await import(url.pathToFileURL(filePath).href);
247
+ };
248
+ const LOAD_ENTRY_FN = async (filePath) => {
249
+ if (!REQUIRE_ENTRY_FACTORY(filePath)) {
250
+ await IMPORT_ENTRY_FACTORY(filePath);
251
+ }
252
+ };
235
253
  let _is_launched = false;
236
254
  class ResolveService {
237
255
  constructor() {
@@ -250,9 +268,9 @@ class ResolveService {
250
268
  {
251
269
  const cwd = process.cwd();
252
270
  process.chdir(moduleRoot);
253
- dotenv.config({ path: path.join(cwd, '.env') });
254
- dotenv.config({ path: path.join(moduleRoot, '.env'), override: true });
255
- await import(url.pathToFileURL(absolutePath).href);
271
+ dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
272
+ dotenv.config({ path: path.join(moduleRoot, '.env'), override: true, quiet: true });
273
+ await LOAD_ENTRY_FN(absolutePath);
256
274
  await entrySubject.next(absolutePath);
257
275
  }
258
276
  _is_launched = true;
@@ -395,7 +413,11 @@ const getArgs = functoolsKit.singleshot(() => {
395
413
  type: "boolean",
396
414
  default: false,
397
415
  },
398
- cache: {
416
+ noCache: {
417
+ type: "boolean",
418
+ default: false,
419
+ },
420
+ cacheInterval: {
399
421
  type: "string",
400
422
  default: "1m, 15m, 30m, 4h",
401
423
  },
@@ -459,17 +481,64 @@ const notifyFinish = functoolsKit.singleshot(() => {
459
481
  });
460
482
 
461
483
  const getEntry = (metaUrl) => {
462
- return process.argv[1] === new URL(metaUrl).pathname;
484
+ const metaPath = url.fileURLToPath(metaUrl);
485
+ return path.resolve(process.argv[1]) === path.resolve(metaPath);
463
486
  };
464
487
 
488
+ const notifyVerbose = functoolsKit.singleshot(() => {
489
+ console.log("Using verbose logging...");
490
+ backtestKit.listenSignal((event) => {
491
+ if (event.action === "scheduled") {
492
+ console.log(`[POSITION SCHEDULED] ${event.symbol}`);
493
+ console.log(` Strategy: ${event.strategyName}`);
494
+ console.log(` Current Price: ${event.currentPrice}`);
495
+ console.log(` Entry Price: ${event.signal.priceOpen}`);
496
+ console.log(` Signal ID: ${event.signal.id}`);
497
+ console.log(` Direction: ${event.signal.position}`);
498
+ console.log(` Stop Loss: ${event.signal.priceStopLoss}`);
499
+ console.log(` Take Profit: ${event.signal.priceTakeProfit}`);
500
+ return;
501
+ }
502
+ if (event.action === "opened") {
503
+ console.log(`[POSITION OPENED] ${event.symbol}`);
504
+ console.log(` Strategy: ${event.strategyName}`);
505
+ console.log(` Entry Price: ${event.currentPrice}`);
506
+ console.log(` Signal ID: ${event.signal.id}`);
507
+ console.log(` Direction: ${event.signal.position}`);
508
+ console.log(` Stop Loss: ${event.signal.priceStopLoss}`);
509
+ console.log(` Take Profit: ${event.signal.priceTakeProfit}`);
510
+ return;
511
+ }
512
+ if (event.action === "closed") {
513
+ console.log(`[POSITION CLOSED] ${event.symbol}`);
514
+ console.log(` Strategy: ${event.strategyName}`);
515
+ console.log(` Entry Price (adj): ${event.pnl.priceOpen}`);
516
+ console.log(` Exit Price (adj): ${event.pnl.priceClose}`);
517
+ console.log(` Signal ID: ${event.signal.id}`);
518
+ console.log(` Close Reason: ${event.closeReason}`);
519
+ console.log(` PnL: ${event.pnl.pnlPercentage.toFixed(2)}%`);
520
+ console.log(` Win: ${event.pnl.pnlPercentage > 0 ? "YES" : "NO"}`);
521
+ return;
522
+ }
523
+ if (event.action === "cancelled") {
524
+ console.log(`[POSITION CANCELLED] ${event.symbol}`);
525
+ console.log(` Strategy: ${event.strategyName}`);
526
+ console.log(` Signal ID: ${event.signal.id}`);
527
+ console.log(` Current Price: ${event.currentPrice}`);
528
+ console.log(` Cancel Reason: ${event.reason}`);
529
+ console.log(` Cancelled At: ${new Date(event.closeTimestamp).toISOString()}`);
530
+ return;
531
+ }
532
+ });
533
+ });
534
+
465
535
  const DEFAULT_CACHE_LIST = ["1m", "15m", "30m", "1h", "4h"];
466
- const GET_CACHE_LIST_FN = () => {
536
+ const GET_CACHE_INTERVAL_LIST_FN = () => {
467
537
  const { values } = getArgs();
468
- if (!values.cache) {
469
- console.warn(`Warning: No cache timeframes provided. Using default timeframes: ${DEFAULT_CACHE_LIST.join(", ")}`);
538
+ if (!values.cacheInterval) {
470
539
  return DEFAULT_CACHE_LIST;
471
540
  }
472
- return String(values.cache)
541
+ return String(values.cacheInterval)
473
542
  .split(",")
474
543
  .map((timeframe) => timeframe.trim());
475
544
  };
@@ -513,8 +582,8 @@ class BacktestMainService {
513
582
  if (!frameName) {
514
583
  throw new Error("Frame name is required");
515
584
  }
516
- {
517
- await this.cacheLogicService.execute(payload.cacheList, {
585
+ if (!payload.noCache) {
586
+ await this.cacheLogicService.execute(payload.cacheInterval, {
518
587
  exchangeName,
519
588
  frameName,
520
589
  symbol,
@@ -529,6 +598,7 @@ class BacktestMainService {
529
598
  },
530
599
  },
531
600
  });
601
+ notifyVerbose();
532
602
  }
533
603
  backtestKit.Backtest.background(symbol, {
534
604
  strategyName,
@@ -546,19 +616,20 @@ class BacktestMainService {
546
616
  if (!values.backtest) {
547
617
  return;
548
618
  }
549
- const [entryPoint = null] = positionals;
619
+ const [entryPoint = null] = positionals.slice(-1);
550
620
  if (!entryPoint) {
551
621
  throw new Error("Entry point is required");
552
622
  }
553
- const cacheList = GET_CACHE_LIST_FN();
623
+ const cacheInterval = GET_CACHE_INTERVAL_LIST_FN();
554
624
  return await this.run({
555
625
  symbol: values.symbol,
556
626
  entryPoint,
557
- cacheList,
627
+ cacheInterval,
558
628
  exchange: values.exchange,
559
629
  frame: values.frame,
560
630
  strategy: values.strategy,
561
631
  verbose: values.verbose,
632
+ noCache: values.noCache,
562
633
  });
563
634
  });
564
635
  }
@@ -607,6 +678,7 @@ class LiveMainService {
607
678
  },
608
679
  },
609
680
  });
681
+ notifyVerbose();
610
682
  }
611
683
  backtestKit.Live.background(symbol, {
612
684
  strategyName,
@@ -623,7 +695,7 @@ class LiveMainService {
623
695
  if (!values.live) {
624
696
  return;
625
697
  }
626
- const [entryPoint = null] = positionals;
698
+ const [entryPoint = null] = positionals.slice(-1);
627
699
  if (!entryPoint) {
628
700
  throw new Error("Entry point is required");
629
701
  }
@@ -677,6 +749,7 @@ class PaperMainService {
677
749
  },
678
750
  },
679
751
  });
752
+ notifyVerbose();
680
753
  }
681
754
  backtestKit.Live.background(symbol, {
682
755
  strategyName,
@@ -693,7 +766,7 @@ class PaperMainService {
693
766
  if (!values.paper) {
694
767
  return;
695
768
  }
696
- const [entryPoint = null] = positionals;
769
+ const [entryPoint = null] = positionals.slice(-1);
697
770
  if (!entryPoint) {
698
771
  throw new Error("Entry point is required");
699
772
  }
package/build/index.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { Storage, Notification, Markdown, Report, StorageLive, StorageBacktest, NotificationLive, NotificationBacktest, listExchangeSchema, addExchangeSchema, roundTicks, listFrameSchema, addFrameSchema, listenDoneLive, listenDoneBacktest, listStrategySchema, overrideExchangeSchema, Backtest, Live, getCandles, checkCandles, warmCandles, listenRisk, listenSignal, listenStrategyCommit } from 'backtest-kit';
2
+ import { Storage, Notification, Markdown, Report, StorageLive, StorageBacktest, NotificationLive, NotificationBacktest, listExchangeSchema, addExchangeSchema, roundTicks, listFrameSchema, addFrameSchema, listenDoneLive, listenDoneBacktest, listenSignal, listStrategySchema, overrideExchangeSchema, Backtest, Live, getCandles, checkCandles, warmCandles, listenRisk, listenStrategyCommit } from 'backtest-kit';
3
3
  import { getErrorMessage, errorData, singleshot, str, BehaviorSubject, compose, execpool, queued, sleep, randomString, createAwaiter, TIMEOUT_SYMBOL, typo, retry, memoize, trycatch } from 'functools-kit';
4
4
  import fs, { constants } from 'fs';
5
5
  import * as stackTrace from 'stack-trace';
6
6
  import { fileURLToPath, pathToFileURL } from 'url';
7
+ import { createRequire } from 'module';
7
8
  import path from 'path';
8
9
  import fs$1, { access } from 'fs/promises';
9
10
  import dotenv from 'dotenv';
@@ -20,7 +21,6 @@ import MarkdownIt from 'markdown-it';
20
21
  import sanitizeHtml from 'sanitize-html';
21
22
  import { JSDOM } from 'jsdom';
22
23
  import Mustache from 'mustache';
23
- import { createRequire } from 'module';
24
24
 
25
25
  /**
26
26
  * Fix for `Attempted to assign to readonly property (at redactToken)`
@@ -62,7 +62,7 @@ import { createRequire } from 'module';
62
62
  }
63
63
  {
64
64
  StorageLive.usePersist();
65
- StorageBacktest.useDummy();
65
+ StorageBacktest.useMemory();
66
66
  }
67
67
  {
68
68
  NotificationLive.useDummy();
@@ -210,6 +210,24 @@ const entrySubject = new BehaviorSubject();
210
210
 
211
211
  const __filename = fileURLToPath(import.meta.url);
212
212
  const __dirname = path.dirname(__filename);
213
+ const require$1 = createRequire(import.meta.url);
214
+ const REQUIRE_ENTRY_FACTORY = (filePath) => {
215
+ try {
216
+ require$1(filePath);
217
+ return true;
218
+ }
219
+ catch {
220
+ return false;
221
+ }
222
+ };
223
+ const IMPORT_ENTRY_FACTORY = async (filePath) => {
224
+ await import(pathToFileURL(filePath).href);
225
+ };
226
+ const LOAD_ENTRY_FN = async (filePath) => {
227
+ if (!REQUIRE_ENTRY_FACTORY(filePath)) {
228
+ await IMPORT_ENTRY_FACTORY(filePath);
229
+ }
230
+ };
213
231
  let _is_launched = false;
214
232
  class ResolveService {
215
233
  constructor() {
@@ -228,9 +246,9 @@ class ResolveService {
228
246
  {
229
247
  const cwd = process.cwd();
230
248
  process.chdir(moduleRoot);
231
- dotenv.config({ path: path.join(cwd, '.env') });
232
- dotenv.config({ path: path.join(moduleRoot, '.env'), override: true });
233
- await import(pathToFileURL(absolutePath).href);
249
+ dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
250
+ dotenv.config({ path: path.join(moduleRoot, '.env'), override: true, quiet: true });
251
+ await LOAD_ENTRY_FN(absolutePath);
234
252
  await entrySubject.next(absolutePath);
235
253
  }
236
254
  _is_launched = true;
@@ -373,7 +391,11 @@ const getArgs = singleshot(() => {
373
391
  type: "boolean",
374
392
  default: false,
375
393
  },
376
- cache: {
394
+ noCache: {
395
+ type: "boolean",
396
+ default: false,
397
+ },
398
+ cacheInterval: {
377
399
  type: "string",
378
400
  default: "1m, 15m, 30m, 4h",
379
401
  },
@@ -437,17 +459,64 @@ const notifyFinish = singleshot(() => {
437
459
  });
438
460
 
439
461
  const getEntry = (metaUrl) => {
440
- return process.argv[1] === new URL(metaUrl).pathname;
462
+ const metaPath = fileURLToPath(metaUrl);
463
+ return path.resolve(process.argv[1]) === path.resolve(metaPath);
441
464
  };
442
465
 
466
+ const notifyVerbose = singleshot(() => {
467
+ console.log("Using verbose logging...");
468
+ listenSignal((event) => {
469
+ if (event.action === "scheduled") {
470
+ console.log(`[POSITION SCHEDULED] ${event.symbol}`);
471
+ console.log(` Strategy: ${event.strategyName}`);
472
+ console.log(` Current Price: ${event.currentPrice}`);
473
+ console.log(` Entry Price: ${event.signal.priceOpen}`);
474
+ console.log(` Signal ID: ${event.signal.id}`);
475
+ console.log(` Direction: ${event.signal.position}`);
476
+ console.log(` Stop Loss: ${event.signal.priceStopLoss}`);
477
+ console.log(` Take Profit: ${event.signal.priceTakeProfit}`);
478
+ return;
479
+ }
480
+ if (event.action === "opened") {
481
+ console.log(`[POSITION OPENED] ${event.symbol}`);
482
+ console.log(` Strategy: ${event.strategyName}`);
483
+ console.log(` Entry Price: ${event.currentPrice}`);
484
+ console.log(` Signal ID: ${event.signal.id}`);
485
+ console.log(` Direction: ${event.signal.position}`);
486
+ console.log(` Stop Loss: ${event.signal.priceStopLoss}`);
487
+ console.log(` Take Profit: ${event.signal.priceTakeProfit}`);
488
+ return;
489
+ }
490
+ if (event.action === "closed") {
491
+ console.log(`[POSITION CLOSED] ${event.symbol}`);
492
+ console.log(` Strategy: ${event.strategyName}`);
493
+ console.log(` Entry Price (adj): ${event.pnl.priceOpen}`);
494
+ console.log(` Exit Price (adj): ${event.pnl.priceClose}`);
495
+ console.log(` Signal ID: ${event.signal.id}`);
496
+ console.log(` Close Reason: ${event.closeReason}`);
497
+ console.log(` PnL: ${event.pnl.pnlPercentage.toFixed(2)}%`);
498
+ console.log(` Win: ${event.pnl.pnlPercentage > 0 ? "YES" : "NO"}`);
499
+ return;
500
+ }
501
+ if (event.action === "cancelled") {
502
+ console.log(`[POSITION CANCELLED] ${event.symbol}`);
503
+ console.log(` Strategy: ${event.strategyName}`);
504
+ console.log(` Signal ID: ${event.signal.id}`);
505
+ console.log(` Current Price: ${event.currentPrice}`);
506
+ console.log(` Cancel Reason: ${event.reason}`);
507
+ console.log(` Cancelled At: ${new Date(event.closeTimestamp).toISOString()}`);
508
+ return;
509
+ }
510
+ });
511
+ });
512
+
443
513
  const DEFAULT_CACHE_LIST = ["1m", "15m", "30m", "1h", "4h"];
444
- const GET_CACHE_LIST_FN = () => {
514
+ const GET_CACHE_INTERVAL_LIST_FN = () => {
445
515
  const { values } = getArgs();
446
- if (!values.cache) {
447
- console.warn(`Warning: No cache timeframes provided. Using default timeframes: ${DEFAULT_CACHE_LIST.join(", ")}`);
516
+ if (!values.cacheInterval) {
448
517
  return DEFAULT_CACHE_LIST;
449
518
  }
450
- return String(values.cache)
519
+ return String(values.cacheInterval)
451
520
  .split(",")
452
521
  .map((timeframe) => timeframe.trim());
453
522
  };
@@ -491,8 +560,8 @@ class BacktestMainService {
491
560
  if (!frameName) {
492
561
  throw new Error("Frame name is required");
493
562
  }
494
- {
495
- await this.cacheLogicService.execute(payload.cacheList, {
563
+ if (!payload.noCache) {
564
+ await this.cacheLogicService.execute(payload.cacheInterval, {
496
565
  exchangeName,
497
566
  frameName,
498
567
  symbol,
@@ -507,6 +576,7 @@ class BacktestMainService {
507
576
  },
508
577
  },
509
578
  });
579
+ notifyVerbose();
510
580
  }
511
581
  Backtest.background(symbol, {
512
582
  strategyName,
@@ -524,19 +594,20 @@ class BacktestMainService {
524
594
  if (!values.backtest) {
525
595
  return;
526
596
  }
527
- const [entryPoint = null] = positionals;
597
+ const [entryPoint = null] = positionals.slice(-1);
528
598
  if (!entryPoint) {
529
599
  throw new Error("Entry point is required");
530
600
  }
531
- const cacheList = GET_CACHE_LIST_FN();
601
+ const cacheInterval = GET_CACHE_INTERVAL_LIST_FN();
532
602
  return await this.run({
533
603
  symbol: values.symbol,
534
604
  entryPoint,
535
- cacheList,
605
+ cacheInterval,
536
606
  exchange: values.exchange,
537
607
  frame: values.frame,
538
608
  strategy: values.strategy,
539
609
  verbose: values.verbose,
610
+ noCache: values.noCache,
540
611
  });
541
612
  });
542
613
  }
@@ -585,6 +656,7 @@ class LiveMainService {
585
656
  },
586
657
  },
587
658
  });
659
+ notifyVerbose();
588
660
  }
589
661
  Live.background(symbol, {
590
662
  strategyName,
@@ -601,7 +673,7 @@ class LiveMainService {
601
673
  if (!values.live) {
602
674
  return;
603
675
  }
604
- const [entryPoint = null] = positionals;
676
+ const [entryPoint = null] = positionals.slice(-1);
605
677
  if (!entryPoint) {
606
678
  throw new Error("Entry point is required");
607
679
  }
@@ -655,6 +727,7 @@ class PaperMainService {
655
727
  },
656
728
  },
657
729
  });
730
+ notifyVerbose();
658
731
  }
659
732
  Live.background(symbol, {
660
733
  strategyName,
@@ -671,7 +744,7 @@ class PaperMainService {
671
744
  if (!values.paper) {
672
745
  return;
673
746
  }
674
- const [entryPoint = null] = positionals;
747
+ const [entryPoint = null] = positionals.slice(-1);
675
748
  if (!entryPoint) {
676
749
  throw new Error("Entry point is required");
677
750
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backtest-kit/cli",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
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",
@@ -59,6 +59,7 @@
59
59
  },
60
60
  "devDependencies": {
61
61
  "@backtest-kit/ui": "3.1.0",
62
+ "markdown-it": "14.1.1",
62
63
  "@rollup/plugin-typescript": "11.1.6",
63
64
  "@types/image-size": "0.7.0",
64
65
  "@types/jsdom": "21.1.7",
@@ -77,7 +78,9 @@
77
78
  "worker-testbed": "1.0.12"
78
79
  },
79
80
  "peerDependencies": {
80
- "typescript": "^5.0.0"
81
+ "typescript": "^5.0.0",
82
+ "@backtest-kit/ui": "^3.1.0",
83
+ "markdown-it": "^14.1.1"
81
84
  },
82
85
  "dependencies": {
83
86
  "ccxt": "4.5.39",
package/types.d.ts CHANGED
@@ -68,8 +68,9 @@ declare class BacktestMainService {
68
68
  strategy: string;
69
69
  exchange: string;
70
70
  frame: string;
71
- cacheList: string[];
71
+ cacheInterval: string[];
72
72
  verbose: boolean;
73
+ noCache: boolean;
73
74
  }) => Promise<void>) & functools_kit.ISingleshotClearable;
74
75
  connect: (() => Promise<void>) & functools_kit.ISingleshotClearable;
75
76
  }