@backtest-kit/cli 0.0.1 → 0.0.3

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/build/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  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 { getErrorMessage, errorData, str, BehaviorSubject, singleshot, compose, execpool, queued, sleep, randomString, createAwaiter, TIMEOUT_SYMBOL, typo, retry, memoize } from 'functools-kit';
2
+ import { getErrorMessage, errorData, singleshot, str, BehaviorSubject, compose, execpool, queued, sleep, randomString, createAwaiter, TIMEOUT_SYMBOL, typo, retry, memoize, trycatch } from 'functools-kit';
3
3
  import fs, { constants } from 'fs';
4
4
  import * as stackTrace from 'stack-trace';
5
5
  import { fileURLToPath, pathToFileURL } from 'url';
@@ -112,14 +112,14 @@ class ErrorService {
112
112
  this.handleGlobalError(err);
113
113
  });
114
114
  };
115
- this.init = () => {
115
+ this.init = singleshot(() => {
116
116
  const global = globalThis;
117
117
  if (global[ERROR_HANDLER_INSTALLED]) {
118
118
  return;
119
119
  }
120
120
  this._listenForError();
121
121
  global[ERROR_HANDLER_INSTALLED] = 1;
122
- };
122
+ });
123
123
  }
124
124
  }
125
125
 
@@ -314,8 +314,8 @@ const ADD_EXCHANGE_FN = (self) => {
314
314
  class ExchangeSchemaService {
315
315
  constructor() {
316
316
  this.loggerService = inject(TYPES.loggerService);
317
- this.init = singleshot(async () => {
318
- this.loggerService.log("exchangeSchemaService init");
317
+ this.addSchema = singleshot(async () => {
318
+ this.loggerService.log("exchangeSchemaService addSchema");
319
319
  const { length } = await listExchangeSchema();
320
320
  !length && ADD_EXCHANGE_FN(this);
321
321
  });
@@ -399,8 +399,8 @@ const ADD_FRAME_FN = (self) => {
399
399
  class FrameSchemaService {
400
400
  constructor() {
401
401
  this.loggerService = inject(TYPES.loggerService);
402
- this.init = singleshot(async () => {
403
- this.loggerService.log("frameSchemaService init");
402
+ this.addSchema = singleshot(async () => {
403
+ this.loggerService.log("frameSchemaService addSchema");
404
404
  if (!getArgs().values.backtest) {
405
405
  return;
406
406
  }
@@ -413,8 +413,8 @@ class FrameSchemaService {
413
413
  class SymbolSchemaService {
414
414
  constructor() {
415
415
  this.loggerService = inject(TYPES.loggerService);
416
- this.init = singleshot(async () => {
417
- this.loggerService.log("symbolSchemaService init");
416
+ this.addSchema = singleshot(async () => {
417
+ this.loggerService.log("symbolSchemaService addSchema");
418
418
  if (!getArgs().values.symbol) {
419
419
  console.warn("Warning: The default symbol is set to BTCUSDT. Please make sure to update it according to your needs using --symbol cli param.");
420
420
  }
@@ -435,56 +435,69 @@ const notifyFinish = singleshot(() => {
435
435
  disposeRef = compose(() => unLive(), () => unBacktest());
436
436
  });
437
437
 
438
+ const getEntry = (metaUrl) => {
439
+ return process.argv[1] === new URL(metaUrl).pathname;
440
+ };
441
+
442
+ const DEFAULT_CACHE_LIST = ["1m", "15m", "30m", "1h", "4h"];
443
+ const GET_CACHE_LIST_FN = () => {
444
+ const { values } = getArgs();
445
+ if (!values.cache) {
446
+ console.warn(`Warning: No cache timeframes provided. Using default timeframes: ${DEFAULT_CACHE_LIST.join(", ")}`);
447
+ return DEFAULT_CACHE_LIST;
448
+ }
449
+ return String(values.cache)
450
+ .split(",")
451
+ .map((timeframe) => timeframe.trim());
452
+ };
438
453
  class BacktestMainService {
439
454
  constructor() {
440
455
  this.loggerService = inject(TYPES.loggerService);
456
+ this.resolveService = inject(TYPES.resolveService);
441
457
  this.exchangeSchemaService = inject(TYPES.exchangeSchemaService);
442
458
  this.frameSchemaService = inject(TYPES.frameSchemaService);
459
+ this.symbolSchemaService = inject(TYPES.symbolSchemaService);
443
460
  this.cacheLogicService = inject(TYPES.cacheLogicService);
444
- this.resolveService = inject(TYPES.resolveService);
445
461
  this.frontendProviderService = inject(TYPES.frontendProviderService);
446
462
  this.telegramProviderService = inject(TYPES.telegramProviderService);
447
- this.init = singleshot(async () => {
448
- this.loggerService.log("backtestMainService init");
463
+ this.run = singleshot(async (payload) => {
464
+ this.loggerService.log("backtestMainService run", {
465
+ payload,
466
+ });
449
467
  {
450
- this.frontendProviderService.init();
451
- this.telegramProviderService.init();
452
- }
453
- const { values, positionals } = getArgs();
454
- if (!values.backtest) {
455
- return;
456
- }
457
- const [entryPoint = null] = positionals;
458
- if (!entryPoint) {
459
- throw new Error("Entry point is required");
468
+ this.frontendProviderService.connect();
469
+ this.telegramProviderService.connect();
460
470
  }
461
- await this.resolveService.attachEntryPoint(entryPoint);
471
+ await this.resolveService.attachEntryPoint(payload.entryPoint);
462
472
  {
463
- this.exchangeSchemaService.init();
464
- this.frameSchemaService.init();
473
+ this.exchangeSchemaService.addSchema();
474
+ this.symbolSchemaService.addSchema();
475
+ this.frameSchemaService.addSchema();
465
476
  }
466
- const symbol = values.symbol || "BTCUSDT";
477
+ const symbol = payload.symbol || "BTCUSDT";
467
478
  const [defaultStrategyName = null] = await listStrategySchema();
468
479
  const [defaultExchangeName = null] = await listExchangeSchema();
469
480
  const [defaultFrameName = null] = await listFrameSchema();
470
- const strategyName = values.strategy || defaultStrategyName?.strategyName;
481
+ const strategyName = payload.strategy || defaultStrategyName?.strategyName;
471
482
  if (!strategyName) {
472
483
  throw new Error("Strategy name is required");
473
484
  }
474
- const exchangeName = values.exchange || defaultExchangeName?.exchangeName;
485
+ const exchangeName = payload.exchange || defaultExchangeName?.exchangeName;
475
486
  if (!exchangeName) {
476
487
  throw new Error("Exchange name is required");
477
488
  }
478
- const frameName = values.frame || defaultFrameName?.frameName;
489
+ const frameName = payload.frame || defaultFrameName?.frameName;
479
490
  if (!frameName) {
480
491
  throw new Error("Frame name is required");
481
492
  }
482
- await this.cacheLogicService.execute({
483
- exchangeName,
484
- frameName,
485
- symbol,
486
- });
487
- if (values.verbose) {
493
+ {
494
+ await this.cacheLogicService.execute(payload.cacheList, {
495
+ exchangeName,
496
+ frameName,
497
+ symbol,
498
+ });
499
+ }
500
+ if (payload.verbose) {
488
501
  overrideExchangeSchema({
489
502
  exchangeName,
490
503
  callbacks: {
@@ -501,46 +514,68 @@ class BacktestMainService {
501
514
  });
502
515
  notifyFinish();
503
516
  });
517
+ this.connect = singleshot(async () => {
518
+ this.loggerService.log("backtestMainService connect");
519
+ if (!getEntry(import.meta.url)) {
520
+ return;
521
+ }
522
+ const { values, positionals } = getArgs();
523
+ if (!values.backtest) {
524
+ return;
525
+ }
526
+ const [entryPoint = null] = positionals;
527
+ if (!entryPoint) {
528
+ throw new Error("Entry point is required");
529
+ }
530
+ const cacheList = GET_CACHE_LIST_FN();
531
+ return await this.run({
532
+ symbol: values.symbol,
533
+ entryPoint,
534
+ cacheList,
535
+ exchange: values.exchange,
536
+ frame: values.frame,
537
+ strategy: values.strategy,
538
+ verbose: values.verbose,
539
+ });
540
+ });
504
541
  }
505
542
  }
506
543
 
507
544
  class LiveMainService {
508
545
  constructor() {
509
546
  this.loggerService = inject(TYPES.loggerService);
510
- this.exchangeSchemaService = inject(TYPES.exchangeSchemaService);
511
547
  this.resolveService = inject(TYPES.resolveService);
548
+ this.exchangeSchemaService = inject(TYPES.exchangeSchemaService);
549
+ this.symbolSchemaService = inject(TYPES.symbolSchemaService);
512
550
  this.frontendProviderService = inject(TYPES.frontendProviderService);
513
551
  this.telegramProviderService = inject(TYPES.telegramProviderService);
514
- this.init = singleshot(async () => {
515
- this.loggerService.log("liveMainService init");
552
+ this.liveProviderService = inject(TYPES.liveProviderService);
553
+ this.run = singleshot(async (payload) => {
554
+ this.loggerService.log("liveMainService run", {
555
+ payload,
556
+ });
516
557
  {
517
- this.frontendProviderService.init();
518
- this.telegramProviderService.init();
519
- }
520
- const { values, positionals } = getArgs();
521
- if (!values.live) {
522
- return;
558
+ this.frontendProviderService.connect();
559
+ this.telegramProviderService.connect();
560
+ this.liveProviderService.connect();
523
561
  }
524
- const [entryPoint = null] = positionals;
525
- if (!entryPoint) {
526
- throw new Error("Entry point is required");
527
- }
528
- await this.resolveService.attachEntryPoint(entryPoint);
562
+ await this.resolveService.attachEntryPoint(payload.entryPoint);
529
563
  {
530
- this.exchangeSchemaService.init();
564
+ this.exchangeSchemaService.addSchema();
565
+ this.symbolSchemaService.addSchema();
531
566
  }
532
- const symbol = values.symbol || "BTCUSDT";
567
+ const symbol = payload.symbol || "BTCUSDT";
533
568
  const [defaultStrategyName = null] = await listStrategySchema();
534
569
  const [defaultExchangeName = null] = await listExchangeSchema();
535
- const strategyName = values.strategy || defaultStrategyName?.strategyName;
570
+ const strategyName = payload.strategy || defaultStrategyName?.strategyName;
536
571
  if (!strategyName) {
537
572
  throw new Error("Strategy name is required");
538
573
  }
539
- const exchangeName = values.exchange || defaultExchangeName?.exchangeName;
574
+ const exchangeName = payload.exchange || defaultExchangeName?.exchangeName;
540
575
  if (!exchangeName) {
541
576
  throw new Error("Exchange name is required");
542
577
  }
543
- if (values.verbose) {
578
+ if (payload.verbose) {
544
579
  overrideExchangeSchema({
545
580
  exchangeName,
546
581
  callbacks: {
@@ -556,46 +591,61 @@ class LiveMainService {
556
591
  });
557
592
  notifyFinish();
558
593
  });
594
+ this.connect = singleshot(async () => {
595
+ this.loggerService.log("liveMainService connect");
596
+ if (!getEntry(import.meta.url)) {
597
+ return;
598
+ }
599
+ const { values, positionals } = getArgs();
600
+ if (!values.live) {
601
+ return;
602
+ }
603
+ const [entryPoint = null] = positionals;
604
+ if (!entryPoint) {
605
+ throw new Error("Entry point is required");
606
+ }
607
+ return await this.run({
608
+ entryPoint,
609
+ exchange: values.exchange,
610
+ strategy: values.strategy,
611
+ symbol: values.symbol,
612
+ verbose: values.verbose,
613
+ });
614
+ });
559
615
  }
560
616
  }
561
617
 
562
618
  class PaperMainService {
563
619
  constructor() {
564
620
  this.loggerService = inject(TYPES.loggerService);
565
- this.exchangeSchemaService = inject(TYPES.exchangeSchemaService);
566
621
  this.resolveService = inject(TYPES.resolveService);
622
+ this.exchangeSchemaService = inject(TYPES.exchangeSchemaService);
623
+ this.symbolSchemaService = inject(TYPES.symbolSchemaService);
567
624
  this.frontendProviderService = inject(TYPES.frontendProviderService);
568
625
  this.telegramProviderService = inject(TYPES.telegramProviderService);
569
- this.init = singleshot(async () => {
626
+ this.run = singleshot(async (payload) => {
570
627
  this.loggerService.log("paperMainService init");
571
628
  {
572
- this.frontendProviderService.init();
573
- this.telegramProviderService.init();
629
+ this.frontendProviderService.connect();
630
+ this.telegramProviderService.connect();
574
631
  }
575
- const { values, positionals } = getArgs();
576
- if (!values.paper) {
577
- return;
578
- }
579
- const [entryPoint = null] = positionals;
580
- if (!entryPoint) {
581
- throw new Error("Entry point is required");
582
- }
583
- await this.resolveService.attachEntryPoint(entryPoint);
632
+ await this.resolveService.attachEntryPoint(payload.entryPoint);
584
633
  {
585
- this.exchangeSchemaService.init();
634
+ this.exchangeSchemaService.addSchema();
635
+ this.symbolSchemaService.addSchema();
586
636
  }
587
- const symbol = values.symbol || "BTCUSDT";
637
+ const symbol = payload.symbol || "BTCUSDT";
588
638
  const [defaultStrategyName = null] = await listStrategySchema();
589
639
  const [defaultExchangeName = null] = await listExchangeSchema();
590
- const strategyName = values.strategy || defaultStrategyName?.strategyName;
640
+ const strategyName = payload.strategy || defaultStrategyName?.strategyName;
591
641
  if (!strategyName) {
592
642
  throw new Error("Strategy name is required");
593
643
  }
594
- const exchangeName = values.exchange || defaultExchangeName?.exchangeName;
644
+ const exchangeName = payload.exchange || defaultExchangeName?.exchangeName;
595
645
  if (!exchangeName) {
596
646
  throw new Error("Exchange name is required");
597
647
  }
598
- if (values.verbose) {
648
+ if (payload.verbose) {
599
649
  overrideExchangeSchema({
600
650
  exchangeName,
601
651
  callbacks: {
@@ -611,6 +661,27 @@ class PaperMainService {
611
661
  });
612
662
  notifyFinish();
613
663
  });
664
+ this.connect = singleshot(async () => {
665
+ this.loggerService.log("paperMainService connect");
666
+ if (!getEntry(import.meta.url)) {
667
+ return;
668
+ }
669
+ const { values, positionals } = getArgs();
670
+ if (!values.paper) {
671
+ return;
672
+ }
673
+ const [entryPoint = null] = positionals;
674
+ if (!entryPoint) {
675
+ throw new Error("Entry point is required");
676
+ }
677
+ return await this.run({
678
+ entryPoint,
679
+ exchange: values.exchange,
680
+ strategy: values.strategy,
681
+ symbol: values.symbol,
682
+ verbose: values.verbose,
683
+ });
684
+ });
614
685
  }
615
686
  }
616
687
 
@@ -646,12 +717,12 @@ class FrontendProviderService {
646
717
  lastSubscription();
647
718
  }
648
719
  };
649
- this.init = singleshot(async () => {
650
- this.loggerService.log("frontendProviderService init");
720
+ this.connect = singleshot(async () => {
721
+ this.loggerService.log("frontendProviderService connect");
651
722
  if (!getArgs().values.ui) {
652
723
  return;
653
724
  }
654
- entrySubject.subscribe(this.enable);
725
+ return entrySubject.subscribe(this.enable);
655
726
  });
656
727
  }
657
728
  }
@@ -682,12 +753,12 @@ class TelegramProviderService {
682
753
  lastSubscription();
683
754
  }
684
755
  };
685
- this.init = singleshot(async () => {
686
- this.loggerService.log("telegramProviderService init");
756
+ this.connect = singleshot(async () => {
757
+ this.loggerService.log("telegramProviderService connect");
687
758
  if (!getArgs().values.telegram) {
688
759
  return;
689
760
  }
690
- entrySubject.subscribe(this.enable);
761
+ return entrySubject.subscribe(this.enable);
691
762
  });
692
763
  }
693
764
  }
@@ -1109,17 +1180,6 @@ class TelegramWebService {
1109
1180
  }
1110
1181
  }
1111
1182
 
1112
- const DEFAULT_TIMEFRAME_LIST = ["1m", "15m", "30m", "1h", "4h"];
1113
- const GET_TIMEFRAME_LIST_FN = async () => {
1114
- const { values } = getArgs();
1115
- if (!values.cache) {
1116
- console.warn(`Warning: No cache timeframes provided. Using default timeframes: ${DEFAULT_TIMEFRAME_LIST.join(", ")}`);
1117
- return DEFAULT_TIMEFRAME_LIST;
1118
- }
1119
- return String(values.cache)
1120
- .split(",")
1121
- .map((timeframe) => timeframe.trim());
1122
- };
1123
1183
  const GET_TIMEFRAME_RANGE_FN = async (frameName) => {
1124
1184
  const frameList = await listFrameSchema();
1125
1185
  const frameSchema = frameList.find((frameSchema) => frameSchema.frameName === frameName);
@@ -1155,12 +1215,11 @@ const CACHE_CANDLES_FN = retry(async (interval, dto) => {
1155
1215
  class CacheLogicService {
1156
1216
  constructor() {
1157
1217
  this.loggerService = inject(TYPES.loggerService);
1158
- this.execute = async (dto) => {
1218
+ this.execute = async (intervalList, dto) => {
1159
1219
  this.loggerService.log("cacheLogicService execute", {
1160
1220
  dto,
1161
1221
  });
1162
1222
  const { startDate, endDate } = await GET_TIMEFRAME_RANGE_FN(dto.frameName);
1163
- const intervalList = await GET_TIMEFRAME_LIST_FN();
1164
1223
  try {
1165
1224
  for (const interval of intervalList) {
1166
1225
  await CACHE_CANDLES_FN(interval, {
@@ -1333,7 +1392,8 @@ class TelegramLogicService {
1333
1392
  return;
1334
1393
  }
1335
1394
  });
1336
- const unListen = compose(() => unRisk(), () => unSignal(), () => unCommit());
1395
+ const unConnect = () => this.connect.clear();
1396
+ const unListen = compose(() => unRisk(), () => unSignal(), () => unCommit(), () => unConnect());
1337
1397
  return () => {
1338
1398
  STOP_BOT_FN();
1339
1399
  unListen();
@@ -1483,10 +1543,10 @@ class ModuleConnectionService {
1483
1543
  }
1484
1544
  }
1485
1545
 
1486
- const LOAD_INSTANCE_FN = singleshot(async (self) => {
1546
+ const LOAD_INSTANCE_FN = singleshot(trycatch(async (self) => {
1487
1547
  const module = (await self.moduleConnectionService.getInstance("./live.module"));
1488
1548
  return module;
1489
- });
1549
+ }, { defaultValue: null }));
1490
1550
  class LiveProviderService {
1491
1551
  constructor() {
1492
1552
  this.loggerService = inject(TYPES.loggerService);
@@ -1496,7 +1556,7 @@ class LiveProviderService {
1496
1556
  event,
1497
1557
  });
1498
1558
  const instance = await LOAD_INSTANCE_FN(this);
1499
- if (instance.onTrailingTake) {
1559
+ if (instance?.onTrailingTake) {
1500
1560
  await instance.onTrailingTake(event);
1501
1561
  }
1502
1562
  };
@@ -1505,7 +1565,7 @@ class LiveProviderService {
1505
1565
  event,
1506
1566
  });
1507
1567
  const instance = await LOAD_INSTANCE_FN(this);
1508
- if (instance.onTrailingStop) {
1568
+ if (instance?.onTrailingStop) {
1509
1569
  await instance.onTrailingStop(event);
1510
1570
  }
1511
1571
  };
@@ -1514,7 +1574,7 @@ class LiveProviderService {
1514
1574
  event,
1515
1575
  });
1516
1576
  const instance = await LOAD_INSTANCE_FN(this);
1517
- if (instance.onBreakeven) {
1577
+ if (instance?.onBreakeven) {
1518
1578
  await instance.onBreakeven(event);
1519
1579
  }
1520
1580
  };
@@ -1523,7 +1583,7 @@ class LiveProviderService {
1523
1583
  event,
1524
1584
  });
1525
1585
  const instance = await LOAD_INSTANCE_FN(this);
1526
- if (instance.onPartialProfit) {
1586
+ if (instance?.onPartialProfit) {
1527
1587
  await instance.onPartialProfit(event);
1528
1588
  }
1529
1589
  };
@@ -1532,7 +1592,7 @@ class LiveProviderService {
1532
1592
  event,
1533
1593
  });
1534
1594
  const instance = await LOAD_INSTANCE_FN(this);
1535
- if (instance.onPartialLoss) {
1595
+ if (instance?.onPartialLoss) {
1536
1596
  await instance.onPartialLoss(event);
1537
1597
  }
1538
1598
  };
@@ -1541,7 +1601,7 @@ class LiveProviderService {
1541
1601
  event,
1542
1602
  });
1543
1603
  const instance = await LOAD_INSTANCE_FN(this);
1544
- if (instance.onScheduled) {
1604
+ if (instance?.onScheduled) {
1545
1605
  await instance.onScheduled(event);
1546
1606
  }
1547
1607
  };
@@ -1550,7 +1610,7 @@ class LiveProviderService {
1550
1610
  event,
1551
1611
  });
1552
1612
  const instance = await LOAD_INSTANCE_FN(this);
1553
- if (instance.onCancelled) {
1613
+ if (instance?.onCancelled) {
1554
1614
  await instance.onCancelled(event);
1555
1615
  }
1556
1616
  };
@@ -1559,7 +1619,7 @@ class LiveProviderService {
1559
1619
  event,
1560
1620
  });
1561
1621
  const instance = await LOAD_INSTANCE_FN(this);
1562
- if (instance.onOpened) {
1622
+ if (instance?.onOpened) {
1563
1623
  await instance.onOpened(event);
1564
1624
  }
1565
1625
  };
@@ -1568,7 +1628,7 @@ class LiveProviderService {
1568
1628
  event,
1569
1629
  });
1570
1630
  const instance = await LOAD_INSTANCE_FN(this);
1571
- if (instance.onClosed) {
1631
+ if (instance?.onClosed) {
1572
1632
  await instance.onClosed(event);
1573
1633
  }
1574
1634
  };
@@ -1577,12 +1637,20 @@ class LiveProviderService {
1577
1637
  event,
1578
1638
  });
1579
1639
  const instance = await LOAD_INSTANCE_FN(this);
1580
- if (instance.onRisk) {
1640
+ if (instance?.onRisk) {
1581
1641
  await instance.onRisk(event);
1582
1642
  }
1583
1643
  };
1584
1644
  this.enable = singleshot(() => {
1585
1645
  this.loggerService.log("liveProviderService enable");
1646
+ LOAD_INSTANCE_FN(this).then((module) => {
1647
+ if (module) {
1648
+ this.loggerService.log("Live trading initialized successfully with ./modules/live.module.mjs");
1649
+ return;
1650
+ }
1651
+ console.log("No ./modules/live.module.mjs found, live trading failed to initialize");
1652
+ process.exit(-1);
1653
+ });
1586
1654
  const unRisk = listenRisk(async (event) => {
1587
1655
  await this.handleRisk(event);
1588
1656
  });
@@ -1626,7 +1694,8 @@ class LiveProviderService {
1626
1694
  return;
1627
1695
  }
1628
1696
  });
1629
- return compose(() => unRisk(), () => unSignal(), () => unCommit());
1697
+ const unConnect = () => this.enable.clear();
1698
+ return compose(() => unRisk(), () => unSignal(), () => unCommit(), () => unConnect());
1630
1699
  });
1631
1700
  this.disable = () => {
1632
1701
  this.loggerService.log("liveProviderService disable");
@@ -1635,12 +1704,12 @@ class LiveProviderService {
1635
1704
  lastSubscription();
1636
1705
  }
1637
1706
  };
1638
- this.init = singleshot(async () => {
1639
- this.loggerService.log("liveProviderService init");
1707
+ this.connect = singleshot(async () => {
1708
+ this.loggerService.log("liveProviderService connect");
1640
1709
  if (!getArgs().values.live) {
1641
1710
  return;
1642
1711
  }
1643
- entrySubject.subscribe(this.enable);
1712
+ return entrySubject.subscribe(this.enable);
1644
1713
  });
1645
1714
  }
1646
1715
  }
@@ -1754,12 +1823,19 @@ const BEFORE_EXIT_FN$4 = singleshot(async () => {
1754
1823
  frameName,
1755
1824
  });
1756
1825
  });
1826
+ const listenGracefulShutdown$4 = singleshot(() => {
1827
+ process.on("SIGINT", BEFORE_EXIT_FN$4);
1828
+ });
1757
1829
  const main$4 = async () => {
1830
+ if (!getEntry(import.meta.url)) {
1831
+ return;
1832
+ }
1758
1833
  const { values } = getArgs();
1759
1834
  if (!values.backtest) {
1760
1835
  return;
1761
1836
  }
1762
- process.on("SIGINT", BEFORE_EXIT_FN$4);
1837
+ await cli.backtestMainService.connect();
1838
+ listenGracefulShutdown$4();
1763
1839
  };
1764
1840
  main$4();
1765
1841
 
@@ -1779,12 +1855,19 @@ const BEFORE_EXIT_FN$3 = singleshot(async () => {
1779
1855
  strategyName,
1780
1856
  });
1781
1857
  });
1858
+ const listenGracefulShutdown$3 = singleshot(() => {
1859
+ process.on("SIGINT", BEFORE_EXIT_FN$3);
1860
+ });
1782
1861
  const main$3 = async () => {
1862
+ if (!getEntry(import.meta.url)) {
1863
+ return;
1864
+ }
1783
1865
  const { values } = getArgs();
1784
1866
  if (!values.paper) {
1785
1867
  return;
1786
1868
  }
1787
- process.on("SIGINT", BEFORE_EXIT_FN$3);
1869
+ cli.paperMainService.connect();
1870
+ listenGracefulShutdown$3();
1788
1871
  };
1789
1872
  main$3();
1790
1873
 
@@ -1805,12 +1888,19 @@ const BEFORE_EXIT_FN$2 = singleshot(async () => {
1805
1888
  });
1806
1889
  listenDoneLive(cli.liveProviderService.disable);
1807
1890
  });
1891
+ const listenGracefulShutdown$2 = singleshot(() => {
1892
+ process.on("SIGINT", BEFORE_EXIT_FN$2);
1893
+ });
1808
1894
  const main$2 = async () => {
1895
+ if (!getEntry(import.meta.url)) {
1896
+ return;
1897
+ }
1809
1898
  const { values } = getArgs();
1810
1899
  if (!values.live) {
1811
1900
  return;
1812
1901
  }
1813
- process.on("SIGINT", BEFORE_EXIT_FN$2);
1902
+ await cli.liveMainService.connect();
1903
+ listenGracefulShutdown$2();
1814
1904
  };
1815
1905
  main$2();
1816
1906
 
@@ -1819,12 +1909,18 @@ const BEFORE_EXIT_FN$1 = singleshot(async () => {
1819
1909
  notifyShutdown();
1820
1910
  cli.frontendProviderService.disable();
1821
1911
  });
1912
+ const listenGracefulShutdown$1 = singleshot(() => {
1913
+ process.on("SIGINT", BEFORE_EXIT_FN$1);
1914
+ });
1822
1915
  const main$1 = async () => {
1916
+ if (!getEntry(import.meta.url)) {
1917
+ return;
1918
+ }
1823
1919
  const { values } = getArgs();
1824
1920
  if (!values.ui) {
1825
1921
  return;
1826
1922
  }
1827
- process.on("SIGINT", BEFORE_EXIT_FN$1);
1923
+ listenGracefulShutdown$1();
1828
1924
  };
1829
1925
  main$1();
1830
1926
 
@@ -1833,12 +1929,18 @@ const BEFORE_EXIT_FN = singleshot(async () => {
1833
1929
  notifyShutdown();
1834
1930
  cli.telegramProviderService.disable();
1835
1931
  });
1932
+ const listenGracefulShutdown = singleshot(() => {
1933
+ process.on("SIGINT", BEFORE_EXIT_FN);
1934
+ });
1836
1935
  const main = async () => {
1936
+ if (!getEntry(import.meta.url)) {
1937
+ return;
1938
+ }
1837
1939
  const { values } = getArgs();
1838
1940
  if (!values.telegram) {
1839
1941
  return;
1840
1942
  }
1841
- process.on("SIGINT", BEFORE_EXIT_FN);
1943
+ listenGracefulShutdown();
1842
1944
  };
1843
1945
  main();
1844
1946
 
@@ -1846,4 +1948,30 @@ function setLogger(logger) {
1846
1948
  cli.loggerService.setLogger(logger);
1847
1949
  }
1848
1950
 
1849
- export { cli, setLogger };
1951
+ let _is_started = false;
1952
+ async function run(mode, args) {
1953
+ {
1954
+ if (_is_started) {
1955
+ throw new Error("Should be called only once");
1956
+ }
1957
+ _is_started = true;
1958
+ }
1959
+ if (mode === "backtest") {
1960
+ await cli.backtestMainService.run(args);
1961
+ listenGracefulShutdown$4();
1962
+ return;
1963
+ }
1964
+ if (mode === "paper") {
1965
+ await cli.paperMainService.run(args);
1966
+ listenGracefulShutdown$3();
1967
+ return;
1968
+ }
1969
+ if (mode === "live") {
1970
+ await cli.liveMainService.run(args);
1971
+ listenGracefulShutdown$2();
1972
+ return;
1973
+ }
1974
+ throw new Error(`Invalid mode: ${mode}`);
1975
+ }
1976
+
1977
+ export { cli, run, setLogger };