@backtest-kit/cli 3.3.4 → 3.3.6

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.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- var backtestKit = require('backtest-kit');
4
+ var BacktestKit = require('backtest-kit');
5
5
  var functoolsKit = require('functools-kit');
6
6
  var fs = require('fs');
7
7
  var stackTrace = require('stack-trace');
@@ -23,6 +23,8 @@ var MarkdownIt = require('markdown-it');
23
23
  var sanitizeHtml = require('sanitize-html');
24
24
  var jsdom = require('jsdom');
25
25
  var Mustache = require('mustache');
26
+ var standalone = require('@babel/standalone');
27
+ var pluginUMD = require('@babel/plugin-transform-modules-umd');
26
28
 
27
29
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
28
30
  function _interopNamespaceDefault(e) {
@@ -42,6 +44,7 @@ function _interopNamespaceDefault(e) {
42
44
  return Object.freeze(n);
43
45
  }
44
46
 
47
+ var BacktestKit__namespace = /*#__PURE__*/_interopNamespaceDefault(BacktestKit);
45
48
  var stackTrace__namespace = /*#__PURE__*/_interopNamespaceDefault(stackTrace);
46
49
 
47
50
  /**
@@ -75,22 +78,22 @@ var stackTrace__namespace = /*#__PURE__*/_interopNamespaceDefault(stackTrace);
75
78
  }
76
79
 
77
80
  {
78
- backtestKit.Storage.enable();
79
- backtestKit.Notification.enable();
81
+ BacktestKit.Storage.enable();
82
+ BacktestKit.Notification.enable();
80
83
  }
81
84
  {
82
- backtestKit.Markdown.disable();
83
- backtestKit.Report.enable();
85
+ BacktestKit.Markdown.disable();
86
+ BacktestKit.Report.enable();
84
87
  }
85
88
  {
86
- backtestKit.StorageLive.usePersist();
87
- backtestKit.StorageBacktest.useMemory();
89
+ BacktestKit.StorageLive.usePersist();
90
+ BacktestKit.StorageBacktest.useMemory();
88
91
  }
89
92
  {
90
- backtestKit.NotificationLive.usePersist();
91
- backtestKit.NotificationBacktest.useMemory();
93
+ BacktestKit.NotificationLive.usePersist();
94
+ BacktestKit.NotificationBacktest.useMemory();
92
95
  }
93
- backtestKit.setConfig({
96
+ BacktestKit.setConfig({
94
97
  CC_MAX_NOTIFICATIONS: 5000,
95
98
  CC_MAX_SIGNALS: 750,
96
99
  });
@@ -191,6 +194,7 @@ const baseServices$1 = {
191
194
  errorService: Symbol('errorService'),
192
195
  loggerService: Symbol('loggerService'),
193
196
  resolveService: Symbol('resolveService'),
197
+ babelService: Symbol('babelService'),
194
198
  };
195
199
  const connectionServices$1 = {
196
200
  moduleConnectionService: Symbol('moduleConnectionService'),
@@ -234,12 +238,12 @@ const TYPES = {
234
238
 
235
239
  const entrySubject = new functoolsKit.BehaviorSubject();
236
240
 
237
- 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)));
238
- const __dirname$1 = path.dirname(__filename$1);
239
- 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)));
241
+ const __filename$2 = 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)));
242
+ const __dirname$2 = path.dirname(__filename$2);
243
+ const require$3 = 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)));
240
244
  const REQUIRE_ENTRY_FACTORY = (filePath) => {
241
245
  try {
242
- require$2(filePath);
246
+ require$3(filePath);
243
247
  return true;
244
248
  }
245
249
  catch {
@@ -247,26 +251,39 @@ const REQUIRE_ENTRY_FACTORY = (filePath) => {
247
251
  }
248
252
  };
249
253
  const IMPORT_ENTRY_FACTORY = async (filePath) => {
250
- await import(url.pathToFileURL(filePath).href);
254
+ {
255
+ return false;
256
+ }
251
257
  };
252
- const TSX_ENTRY_FACTORY = async (filePath) => {
253
- const { tsImport } = await import('tsx/esm/api');
254
- await tsImport(url.pathToFileURL(filePath).href, (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
258
+ const BABEL_ENTRY_FACTORY = async (filePath, self) => {
259
+ const code = await fs$1.readFile(filePath, "utf-8");
260
+ try {
261
+ await self.babelService.transpileAndRun(code);
262
+ return true;
263
+ }
264
+ catch (error) {
265
+ console.log(functoolsKit.getErrorMessage(error));
266
+ return false;
267
+ }
255
268
  };
256
- const LOAD_ENTRY_FN = async (filePath) => {
257
- if (filePath.endsWith('.ts') || filePath.endsWith('.tsx')) {
258
- await TSX_ENTRY_FACTORY(filePath);
269
+ const LOAD_ENTRY_FN = async (filePath, self) => {
270
+ if (REQUIRE_ENTRY_FACTORY(filePath)) {
259
271
  return;
260
272
  }
261
- if (!REQUIRE_ENTRY_FACTORY(filePath)) {
262
- await IMPORT_ENTRY_FACTORY(filePath);
273
+ if (await IMPORT_ENTRY_FACTORY()) {
274
+ return;
263
275
  }
276
+ if (await BABEL_ENTRY_FACTORY(filePath, self)) {
277
+ return;
278
+ }
279
+ throw new Error(`Failed to load entry point: ${filePath}`);
264
280
  };
265
281
  let _is_launched = false;
266
282
  class ResolveService {
267
283
  constructor() {
268
284
  this.loggerService = inject(TYPES.loggerService);
269
- this.DEFAULT_TEMPLATE_DIR = path.resolve(__dirname$1, '..', 'template');
285
+ this.babelService = inject(TYPES.babelService);
286
+ this.DEFAULT_TEMPLATE_DIR = path.resolve(__dirname$2, '..', 'template');
270
287
  this.OVERRIDE_TEMPLATE_DIR = path.resolve(process.cwd(), 'template');
271
288
  this.OVERRIDE_MODULES_DIR = path.resolve(process.cwd(), 'modules');
272
289
  this.attachEntryPoint = async (entryPoint) => {
@@ -282,7 +299,7 @@ class ResolveService {
282
299
  process.chdir(moduleRoot);
283
300
  dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
284
301
  dotenv.config({ path: path.join(moduleRoot, '.env'), override: true, quiet: true });
285
- await LOAD_ENTRY_FN(absolutePath);
302
+ await LOAD_ENTRY_FN(absolutePath, this);
286
303
  await entrySubject.next(absolutePath);
287
304
  }
288
305
  _is_launched = true;
@@ -312,7 +329,7 @@ var ExchangeName$1 = ExchangeName;
312
329
  const ADD_EXCHANGE_FN = (self) => {
313
330
  self.loggerService.log("Adding CCXT Binance as a default exchange schema");
314
331
  console.warn("Warning: The default exchange schema is set to CCXT Binance. Please make sure to update it according to your needs using --exchange cli param.");
315
- backtestKit.addExchangeSchema({
332
+ BacktestKit.addExchangeSchema({
316
333
  exchangeName: ExchangeName$1.DefaultExchange,
317
334
  getCandles: async (symbol, interval, since, limit) => {
318
335
  const exchange = await getExchange();
@@ -331,7 +348,7 @@ const ADD_EXCHANGE_FN = (self) => {
331
348
  const market = exchange.market(symbol);
332
349
  const tickSize = market.limits?.price?.min || market.precision?.price;
333
350
  if (tickSize !== undefined) {
334
- return backtestKit.roundTicks(price, tickSize);
351
+ return BacktestKit.roundTicks(price, tickSize);
335
352
  }
336
353
  return exchange.priceToPrecision(symbol, price);
337
354
  },
@@ -340,7 +357,7 @@ const ADD_EXCHANGE_FN = (self) => {
340
357
  const market = exchange.market(symbol);
341
358
  const stepSize = market.limits?.amount?.min || market.precision?.amount;
342
359
  if (stepSize !== undefined) {
343
- return backtestKit.roundTicks(quantity, stepSize);
360
+ return BacktestKit.roundTicks(quantity, stepSize);
344
361
  }
345
362
  return exchange.amountToPrecision(symbol, quantity);
346
363
  },
@@ -369,7 +386,7 @@ class ExchangeSchemaService {
369
386
  this.loggerService = inject(TYPES.loggerService);
370
387
  this.addSchema = functoolsKit.singleshot(async () => {
371
388
  this.loggerService.log("exchangeSchemaService addSchema");
372
- const { length } = await backtestKit.listExchangeSchema();
389
+ const { length } = await BacktestKit.listExchangeSchema();
373
390
  !length && ADD_EXCHANGE_FN(this);
374
391
  });
375
392
  }
@@ -446,7 +463,7 @@ const getArgs = functoolsKit.singleshot(() => {
446
463
  const ADD_FRAME_FN = (self) => {
447
464
  self.loggerService.log("Adding February 2024 as a default frame schema");
448
465
  console.warn("Warning: The default frame schema is set to February 2024. Please make sure to update it according to your needs using --frame cli param.");
449
- backtestKit.addFrameSchema({
466
+ BacktestKit.addFrameSchema({
450
467
  frameName: FrameName$1.DefaultFrame,
451
468
  interval: "1m",
452
469
  startDate: new Date("2024-02-01T00:00:00Z"),
@@ -461,7 +478,7 @@ class FrameSchemaService {
461
478
  if (!getArgs().values.backtest) {
462
479
  return;
463
480
  }
464
- const { length } = await backtestKit.listFrameSchema();
481
+ const { length } = await BacktestKit.listFrameSchema();
465
482
  !length && ADD_FRAME_FN(this);
466
483
  });
467
484
  }
@@ -481,11 +498,11 @@ class SymbolSchemaService {
481
498
 
482
499
  const notifyFinish = functoolsKit.singleshot(() => {
483
500
  let disposeRef;
484
- const unLive = backtestKit.listenDoneLive(() => {
501
+ const unLive = BacktestKit.listenDoneLive(() => {
485
502
  console.log("Live trading finished");
486
503
  disposeRef && disposeRef();
487
504
  });
488
- const unBacktest = backtestKit.listenDoneBacktest(() => {
505
+ const unBacktest = BacktestKit.listenDoneBacktest(() => {
489
506
  console.log("Backtest trading finished");
490
507
  disposeRef && disposeRef();
491
508
  });
@@ -499,7 +516,7 @@ const getEntry = (metaUrl) => {
499
516
 
500
517
  const notifyVerbose = functoolsKit.singleshot(() => {
501
518
  console.log("Using verbose logging...");
502
- backtestKit.listenSignal((event) => {
519
+ BacktestKit.listenSignal((event) => {
503
520
  if (event.action === "scheduled") {
504
521
  console.log(`[POSITION SCHEDULED] ${event.symbol}`);
505
522
  console.log(` Strategy: ${event.strategyName}`);
@@ -579,9 +596,9 @@ class BacktestMainService {
579
596
  this.frameSchemaService.addSchema();
580
597
  }
581
598
  const symbol = payload.symbol || "BTCUSDT";
582
- const [defaultStrategyName = null] = await backtestKit.listStrategySchema();
583
- const [defaultExchangeName = null] = await backtestKit.listExchangeSchema();
584
- const [defaultFrameName = null] = await backtestKit.listFrameSchema();
599
+ const [defaultStrategyName = null] = await BacktestKit.listStrategySchema();
600
+ const [defaultExchangeName = null] = await BacktestKit.listExchangeSchema();
601
+ const [defaultFrameName = null] = await BacktestKit.listFrameSchema();
585
602
  const strategyName = payload.strategy || defaultStrategyName?.strategyName;
586
603
  if (!strategyName) {
587
604
  throw new Error("Strategy name is required");
@@ -602,7 +619,7 @@ class BacktestMainService {
602
619
  });
603
620
  }
604
621
  if (payload.verbose) {
605
- backtestKit.overrideExchangeSchema({
622
+ BacktestKit.overrideExchangeSchema({
606
623
  exchangeName,
607
624
  callbacks: {
608
625
  onCandleData(symbol, interval, since) {
@@ -612,7 +629,7 @@ class BacktestMainService {
612
629
  });
613
630
  notifyVerbose();
614
631
  }
615
- backtestKit.Backtest.background(symbol, {
632
+ BacktestKit.Backtest.background(symbol, {
616
633
  strategyName,
617
634
  frameName,
618
635
  exchangeName,
@@ -671,8 +688,8 @@ class LiveMainService {
671
688
  this.symbolSchemaService.addSchema();
672
689
  }
673
690
  const symbol = payload.symbol || "BTCUSDT";
674
- const [defaultStrategyName = null] = await backtestKit.listStrategySchema();
675
- const [defaultExchangeName = null] = await backtestKit.listExchangeSchema();
691
+ const [defaultStrategyName = null] = await BacktestKit.listStrategySchema();
692
+ const [defaultExchangeName = null] = await BacktestKit.listExchangeSchema();
676
693
  const strategyName = payload.strategy || defaultStrategyName?.strategyName;
677
694
  if (!strategyName) {
678
695
  throw new Error("Strategy name is required");
@@ -682,7 +699,7 @@ class LiveMainService {
682
699
  throw new Error("Exchange name is required");
683
700
  }
684
701
  if (payload.verbose) {
685
- backtestKit.overrideExchangeSchema({
702
+ BacktestKit.overrideExchangeSchema({
686
703
  exchangeName,
687
704
  callbacks: {
688
705
  onCandleData(symbol, interval, since) {
@@ -692,7 +709,7 @@ class LiveMainService {
692
709
  });
693
710
  notifyVerbose();
694
711
  }
695
- backtestKit.Live.background(symbol, {
712
+ BacktestKit.Live.background(symbol, {
696
713
  strategyName,
697
714
  exchangeName,
698
715
  });
@@ -742,8 +759,8 @@ class PaperMainService {
742
759
  this.symbolSchemaService.addSchema();
743
760
  }
744
761
  const symbol = payload.symbol || "BTCUSDT";
745
- const [defaultStrategyName = null] = await backtestKit.listStrategySchema();
746
- const [defaultExchangeName = null] = await backtestKit.listExchangeSchema();
762
+ const [defaultStrategyName = null] = await BacktestKit.listStrategySchema();
763
+ const [defaultExchangeName = null] = await BacktestKit.listExchangeSchema();
747
764
  const strategyName = payload.strategy || defaultStrategyName?.strategyName;
748
765
  if (!strategyName) {
749
766
  throw new Error("Strategy name is required");
@@ -753,7 +770,7 @@ class PaperMainService {
753
770
  throw new Error("Exchange name is required");
754
771
  }
755
772
  if (payload.verbose) {
756
- backtestKit.overrideExchangeSchema({
773
+ BacktestKit.overrideExchangeSchema({
757
774
  exchangeName,
758
775
  callbacks: {
759
776
  onCandleData(symbol, interval, since) {
@@ -763,7 +780,7 @@ class PaperMainService {
763
780
  });
764
781
  notifyVerbose();
765
782
  }
766
- backtestKit.Live.background(symbol, {
783
+ BacktestKit.Live.background(symbol, {
767
784
  strategyName,
768
785
  exchangeName,
769
786
  });
@@ -873,7 +890,7 @@ class TelegramProviderService {
873
890
 
874
891
  const CANDLES_LIMIT = 160;
875
892
  const GET_CONFIG_FN = async (symbol, interval) => {
876
- const candles = await backtestKit.getCandles(symbol, interval, CANDLES_LIMIT);
893
+ const candles = await BacktestKit.getCandles(symbol, interval, CANDLES_LIMIT);
877
894
  const labels = candles.map(({ timestamp }) => new Date(timestamp).toLocaleTimeString("en-US", {
878
895
  hour: "2-digit",
879
896
  minute: "2-digit",
@@ -1289,7 +1306,7 @@ class TelegramWebService {
1289
1306
  }
1290
1307
 
1291
1308
  const GET_TIMEFRAME_RANGE_FN = async (frameName) => {
1292
- const frameList = await backtestKit.listFrameSchema();
1309
+ const frameList = await BacktestKit.listFrameSchema();
1293
1310
  const frameSchema = frameList.find((frameSchema) => frameSchema.frameName === frameName);
1294
1311
  if (!frameSchema) {
1295
1312
  throw new Error(`Frame with name ${frameName} not found`);
@@ -1300,7 +1317,7 @@ const GET_TIMEFRAME_RANGE_FN = async (frameName) => {
1300
1317
  const CACHE_CANDLES_FN = functoolsKit.retry(async (interval, dto) => {
1301
1318
  try {
1302
1319
  console.log(`Checking candles cache for ${dto.symbol} ${interval} from ${dto.from} to ${dto.to}`);
1303
- await backtestKit.checkCandles({
1320
+ await BacktestKit.checkCandles({
1304
1321
  exchangeName: dto.exchangeName,
1305
1322
  from: dto.from,
1306
1323
  to: dto.to,
@@ -1310,7 +1327,7 @@ const CACHE_CANDLES_FN = functoolsKit.retry(async (interval, dto) => {
1310
1327
  }
1311
1328
  catch (error) {
1312
1329
  console.log(`Caching candles for ${dto.symbol} ${interval} from ${dto.from} to ${dto.to}`);
1313
- await backtestKit.warmCandles({
1330
+ await BacktestKit.warmCandles({
1314
1331
  symbol: dto.symbol,
1315
1332
  exchangeName: dto.exchangeName,
1316
1333
  from: dto.from,
@@ -1467,10 +1484,10 @@ class TelegramLogicService {
1467
1484
  };
1468
1485
  this.connect = functoolsKit.singleshot(() => {
1469
1486
  this.loggerService.log("telegramLogicService connect");
1470
- const unRisk = backtestKit.listenRisk(async (event) => {
1487
+ const unRisk = BacktestKit.listenRisk(async (event) => {
1471
1488
  await this.notifyRisk(event);
1472
1489
  });
1473
- const unSignal = backtestKit.listenSignal(async (event) => {
1490
+ const unSignal = BacktestKit.listenSignal(async (event) => {
1474
1491
  if (event.action === "scheduled") {
1475
1492
  await this.notifyScheduled(event);
1476
1493
  return;
@@ -1488,7 +1505,7 @@ class TelegramLogicService {
1488
1505
  return;
1489
1506
  }
1490
1507
  });
1491
- const unCommit = backtestKit.listenStrategyCommit(async (event) => {
1508
+ const unCommit = BacktestKit.listenStrategyCommit(async (event) => {
1492
1509
  if (event.action === "trailing-take") {
1493
1510
  await this.notifyTrailingTake(event);
1494
1511
  return;
@@ -1613,7 +1630,7 @@ class TelegramTemplateService {
1613
1630
  }
1614
1631
  }
1615
1632
 
1616
- const require$1 = 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)));
1633
+ 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)));
1617
1634
  const getExtVariants = (fileName) => {
1618
1635
  const ext = path.extname(fileName);
1619
1636
  const base = ext ? fileName.slice(0, -ext.length) : fileName;
@@ -1622,7 +1639,7 @@ const getExtVariants = (fileName) => {
1622
1639
  const REQUIRE_MODULE_FACTORY = (fileName) => {
1623
1640
  for (const variant of getExtVariants(fileName)) {
1624
1641
  try {
1625
- return require$1(variant);
1642
+ return require$2(variant);
1626
1643
  }
1627
1644
  catch {
1628
1645
  continue;
@@ -1631,11 +1648,19 @@ const REQUIRE_MODULE_FACTORY = (fileName) => {
1631
1648
  return null;
1632
1649
  };
1633
1650
  const IMPORT_MODULE_FACTORY = async (fileName) => {
1651
+ {
1652
+ return null;
1653
+ }
1654
+ };
1655
+ const BABEL_MODULE_FACTORY = async (fileName, self) => {
1634
1656
  for (const variant of getExtVariants(fileName)) {
1635
1657
  try {
1636
- return await import(variant);
1658
+ const code = await fs$1.readFile(variant, "utf-8");
1659
+ const { exports } = self.babelService.transpileAndRun(code);
1660
+ return exports.default ?? exports;
1637
1661
  }
1638
- catch {
1662
+ catch (error) {
1663
+ console.log(functoolsKit.getErrorMessage(error));
1639
1664
  continue;
1640
1665
  }
1641
1666
  }
@@ -1653,7 +1678,10 @@ const LOAD_MODULE_MODULE_FN = async (fileName, self) => {
1653
1678
  if ((Ctor = REQUIRE_MODULE_FACTORY(resolvedFile))) {
1654
1679
  return typeof Ctor === "function" ? new Ctor() : Ctor;
1655
1680
  }
1656
- if ((Ctor = await IMPORT_MODULE_FACTORY(resolvedFile))) {
1681
+ if ((Ctor = await IMPORT_MODULE_FACTORY())) {
1682
+ return typeof Ctor === "function" ? new Ctor() : Ctor;
1683
+ }
1684
+ if ((Ctor = await BABEL_MODULE_FACTORY(resolvedFile, self))) {
1657
1685
  return typeof Ctor === "function" ? new Ctor() : Ctor;
1658
1686
  }
1659
1687
  throw new Error(`Module module import failed for file: ${resolvedFile}`);
@@ -1662,6 +1690,7 @@ class ModuleConnectionService {
1662
1690
  constructor() {
1663
1691
  this.loggerService = inject(TYPES.loggerService);
1664
1692
  this.resolveService = inject(TYPES.resolveService);
1693
+ this.babelService = inject(TYPES.babelService);
1665
1694
  this.getInstance = functoolsKit.memoize(([fileName]) => `${fileName}`, async (fileName) => {
1666
1695
  this.loggerService.log("moduleConnectionService getInstance", {
1667
1696
  fileName,
@@ -1788,10 +1817,10 @@ class LiveProviderService {
1788
1817
  console.log("No ./modules/live.module.mjs found, live trading failed to initialize");
1789
1818
  process.exit(-1);
1790
1819
  });
1791
- const unRisk = backtestKit.listenRisk(async (event) => {
1820
+ const unRisk = BacktestKit.listenRisk(async (event) => {
1792
1821
  await this.handleRisk(event);
1793
1822
  });
1794
- const unSignal = backtestKit.listenSignal(async (event) => {
1823
+ const unSignal = BacktestKit.listenSignal(async (event) => {
1795
1824
  if (event.action === "scheduled") {
1796
1825
  await this.handleScheduled(event);
1797
1826
  return;
@@ -1809,7 +1838,7 @@ class LiveProviderService {
1809
1838
  return;
1810
1839
  }
1811
1840
  });
1812
- const unCommit = backtestKit.listenStrategyCommit(async (event) => {
1841
+ const unCommit = BacktestKit.listenStrategyCommit(async (event) => {
1813
1842
  if (event.action === "trailing-take") {
1814
1843
  await this.handleTrailingTake(event);
1815
1844
  return;
@@ -1855,6 +1884,62 @@ class LiveProviderService {
1855
1884
  }
1856
1885
  }
1857
1886
 
1887
+ standalone.registerPlugin("plugin-transform-modules-umd", pluginUMD);
1888
+ const require$1 = 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)));
1889
+ 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)));
1890
+ const __dirname$1 = path.dirname(__filename$1);
1891
+ const BacktestKitCli = new Proxy({}, {
1892
+ get(_target, prop) {
1893
+ throw new Error(`@backtest-kit/cli is not available in this context (accessed: ${String(prop)})`);
1894
+ },
1895
+ });
1896
+ class BabelService {
1897
+ constructor() {
1898
+ this.loggerService = inject(TYPES.loggerService);
1899
+ this.transpile = (code) => {
1900
+ this.loggerService.log("babelService transpile", { codeLen: code.length });
1901
+ const result = standalone.transform(code, {
1902
+ filename: "index.ts",
1903
+ presets: ["env", "typescript"],
1904
+ plugins: [
1905
+ [
1906
+ "plugin-transform-modules-umd",
1907
+ {
1908
+ globals: {
1909
+ "backtest-kit": "BacktestKit",
1910
+ "@backtest-kit/cli": "BacktestKitCli",
1911
+ },
1912
+ moduleId: "Executor",
1913
+ },
1914
+ ],
1915
+ ],
1916
+ parserOpts: { strictMode: false },
1917
+ });
1918
+ if (!result.code) {
1919
+ throw new Error("BabelService transpile failed");
1920
+ }
1921
+ return result.code;
1922
+ };
1923
+ this.transpileAndRun = (code) => {
1924
+ this.loggerService.log("babelService transpileAndRun", {
1925
+ codeLen: code.length,
1926
+ });
1927
+ const module = { exports: {} };
1928
+ const exports = module.exports;
1929
+ eval(this.transpile(code));
1930
+ return {
1931
+ require: require$1,
1932
+ __filename: __filename$1,
1933
+ __dirname: __dirname$1,
1934
+ exports,
1935
+ module,
1936
+ };
1937
+ };
1938
+ }
1939
+ }
1940
+ globalThis.BacktestKit = BacktestKit__namespace;
1941
+ globalThis.BacktestKitCli = BacktestKitCli;
1942
+
1858
1943
  {
1859
1944
  provide(TYPES.quickchartApiService, () => new QuickchartApiService());
1860
1945
  provide(TYPES.telegramApiService, () => new TelegramApiService());
@@ -1863,6 +1948,7 @@ class LiveProviderService {
1863
1948
  provide(TYPES.errorService, () => new ErrorService());
1864
1949
  provide(TYPES.loggerService, () => new LoggerService());
1865
1950
  provide(TYPES.resolveService, () => new ResolveService());
1951
+ provide(TYPES.babelService, () => new BabelService());
1866
1952
  }
1867
1953
  {
1868
1954
  provide(TYPES.moduleConnectionService, () => new ModuleConnectionService());
@@ -1901,6 +1987,7 @@ const baseServices = {
1901
1987
  errorService: inject(TYPES.errorService),
1902
1988
  loggerService: inject(TYPES.loggerService),
1903
1989
  resolveService: inject(TYPES.resolveService),
1990
+ babelService: inject(TYPES.babelService),
1904
1991
  };
1905
1992
  const connectionServices = {
1906
1993
  moduleConnectionService: inject(TYPES.moduleConnectionService),
@@ -1949,7 +2036,7 @@ const notifyShutdown = functoolsKit.singleshot(async () => {
1949
2036
 
1950
2037
  const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
1951
2038
  process.off("SIGINT", BEFORE_EXIT_FN$4);
1952
- const [running = null] = await backtestKit.Backtest.list();
2039
+ const [running = null] = await BacktestKit.Backtest.list();
1953
2040
  if (!running) {
1954
2041
  return;
1955
2042
  }
@@ -1958,7 +2045,7 @@ const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
1958
2045
  if (status === "fulfilled") {
1959
2046
  return;
1960
2047
  }
1961
- backtestKit.Backtest.stop(symbol, {
2048
+ BacktestKit.Backtest.stop(symbol, {
1962
2049
  exchangeName,
1963
2050
  strategyName,
1964
2051
  frameName,
@@ -1982,7 +2069,7 @@ main$4();
1982
2069
 
1983
2070
  const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
1984
2071
  process.off("SIGINT", BEFORE_EXIT_FN$3);
1985
- const [running = null] = await backtestKit.Live.list();
2072
+ const [running = null] = await BacktestKit.Live.list();
1986
2073
  if (!running) {
1987
2074
  return;
1988
2075
  }
@@ -1991,7 +2078,7 @@ const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
1991
2078
  if (status === "fulfilled") {
1992
2079
  return;
1993
2080
  }
1994
- backtestKit.Live.stop(symbol, {
2081
+ BacktestKit.Live.stop(symbol, {
1995
2082
  exchangeName,
1996
2083
  strategyName,
1997
2084
  });
@@ -2014,7 +2101,7 @@ main$3();
2014
2101
 
2015
2102
  const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
2016
2103
  process.off("SIGINT", BEFORE_EXIT_FN$2);
2017
- const [running = null] = await backtestKit.Live.list();
2104
+ const [running = null] = await BacktestKit.Live.list();
2018
2105
  if (!running) {
2019
2106
  return;
2020
2107
  }
@@ -2023,11 +2110,11 @@ const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
2023
2110
  if (status === "fulfilled") {
2024
2111
  return;
2025
2112
  }
2026
- backtestKit.Live.stop(symbol, {
2113
+ BacktestKit.Live.stop(symbol, {
2027
2114
  exchangeName,
2028
2115
  strategyName,
2029
2116
  });
2030
- backtestKit.listenDoneLive(cli.liveProviderService.disable);
2117
+ BacktestKit.listenDoneLive(cli.liveProviderService.disable);
2031
2118
  });
2032
2119
  const listenGracefulShutdown$2 = functoolsKit.singleshot(() => {
2033
2120
  process.on("SIGINT", BEFORE_EXIT_FN$2);
package/build/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import * as BacktestKit from 'backtest-kit';
2
3
  import { Storage, Notification, Markdown, Report, StorageLive, StorageBacktest, NotificationLive, NotificationBacktest, setConfig, listExchangeSchema, addExchangeSchema, roundTicks, listFrameSchema, addFrameSchema, listenDoneLive, listenDoneBacktest, listenSignal, listStrategySchema, overrideExchangeSchema, Backtest, Live, getCandles, checkCandles, warmCandles, listenRisk, listenStrategyCommit } from 'backtest-kit';
3
4
  import { getErrorMessage, errorData, singleshot, str, BehaviorSubject, compose, execpool, queued, sleep, randomString, createAwaiter, TIMEOUT_SYMBOL, typo, retry, memoize, trycatch } from 'functools-kit';
4
5
  import fs, { constants } from 'fs';
@@ -21,6 +22,8 @@ import MarkdownIt from 'markdown-it';
21
22
  import sanitizeHtml from 'sanitize-html';
22
23
  import { JSDOM } from 'jsdom';
23
24
  import Mustache from 'mustache';
25
+ import { registerPlugin, transform } from '@babel/standalone';
26
+ import pluginUMD from '@babel/plugin-transform-modules-umd';
24
27
 
25
28
  /**
26
29
  * Fix for `Attempted to assign to readonly property (at redactToken)`
@@ -169,6 +172,7 @@ const baseServices$1 = {
169
172
  errorService: Symbol('errorService'),
170
173
  loggerService: Symbol('loggerService'),
171
174
  resolveService: Symbol('resolveService'),
175
+ babelService: Symbol('babelService'),
172
176
  };
173
177
  const connectionServices$1 = {
174
178
  moduleConnectionService: Symbol('moduleConnectionService'),
@@ -212,39 +216,52 @@ const TYPES = {
212
216
 
213
217
  const entrySubject = new BehaviorSubject();
214
218
 
215
- const __filename = fileURLToPath(import.meta.url);
216
- const __dirname = path.dirname(__filename);
217
- const require$1 = createRequire(import.meta.url);
219
+ const __filename$1 = fileURLToPath(import.meta.url);
220
+ const __dirname$1 = path.dirname(__filename$1);
221
+ createRequire(import.meta.url);
218
222
  const REQUIRE_ENTRY_FACTORY = (filePath) => {
223
+ {
224
+ return false;
225
+ }
226
+ };
227
+ const IMPORT_ENTRY_FACTORY = async (filePath) => {
219
228
  try {
220
- require$1(filePath);
229
+ await import(pathToFileURL(filePath).href);
221
230
  return true;
222
231
  }
223
232
  catch {
224
233
  return false;
225
234
  }
226
235
  };
227
- const IMPORT_ENTRY_FACTORY = async (filePath) => {
228
- await import(pathToFileURL(filePath).href);
229
- };
230
- const TSX_ENTRY_FACTORY = async (filePath) => {
231
- const { tsImport } = await import('tsx/esm/api');
232
- await tsImport(pathToFileURL(filePath).href, import.meta.url);
236
+ const BABEL_ENTRY_FACTORY = async (filePath, self) => {
237
+ const code = await fs$1.readFile(filePath, "utf-8");
238
+ try {
239
+ await self.babelService.transpileAndRun(code);
240
+ return true;
241
+ }
242
+ catch (error) {
243
+ console.log(getErrorMessage(error));
244
+ return false;
245
+ }
233
246
  };
234
- const LOAD_ENTRY_FN = async (filePath) => {
235
- if (filePath.endsWith('.ts') || filePath.endsWith('.tsx')) {
236
- await TSX_ENTRY_FACTORY(filePath);
247
+ const LOAD_ENTRY_FN = async (filePath, self) => {
248
+ if (REQUIRE_ENTRY_FACTORY()) {
237
249
  return;
238
250
  }
239
- if (!REQUIRE_ENTRY_FACTORY(filePath)) {
240
- await IMPORT_ENTRY_FACTORY(filePath);
251
+ if (await IMPORT_ENTRY_FACTORY(filePath)) {
252
+ return;
253
+ }
254
+ if (await BABEL_ENTRY_FACTORY(filePath, self)) {
255
+ return;
241
256
  }
257
+ throw new Error(`Failed to load entry point: ${filePath}`);
242
258
  };
243
259
  let _is_launched = false;
244
260
  class ResolveService {
245
261
  constructor() {
246
262
  this.loggerService = inject(TYPES.loggerService);
247
- this.DEFAULT_TEMPLATE_DIR = path.resolve(__dirname, '..', 'template');
263
+ this.babelService = inject(TYPES.babelService);
264
+ this.DEFAULT_TEMPLATE_DIR = path.resolve(__dirname$1, '..', 'template');
248
265
  this.OVERRIDE_TEMPLATE_DIR = path.resolve(process.cwd(), 'template');
249
266
  this.OVERRIDE_MODULES_DIR = path.resolve(process.cwd(), 'modules');
250
267
  this.attachEntryPoint = async (entryPoint) => {
@@ -260,7 +277,7 @@ class ResolveService {
260
277
  process.chdir(moduleRoot);
261
278
  dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
262
279
  dotenv.config({ path: path.join(moduleRoot, '.env'), override: true, quiet: true });
263
- await LOAD_ENTRY_FN(absolutePath);
280
+ await LOAD_ENTRY_FN(absolutePath, this);
264
281
  await entrySubject.next(absolutePath);
265
282
  }
266
283
  _is_launched = true;
@@ -1591,16 +1608,21 @@ class TelegramTemplateService {
1591
1608
  }
1592
1609
  }
1593
1610
 
1594
- const require = createRequire(import.meta.url);
1611
+ createRequire(import.meta.url);
1595
1612
  const getExtVariants = (fileName) => {
1596
1613
  const ext = path.extname(fileName);
1597
1614
  const base = ext ? fileName.slice(0, -ext.length) : fileName;
1598
1615
  return [fileName, `${base}.cjs`, `${base}.mjs`];
1599
1616
  };
1600
1617
  const REQUIRE_MODULE_FACTORY = (fileName) => {
1618
+ {
1619
+ return null;
1620
+ }
1621
+ };
1622
+ const IMPORT_MODULE_FACTORY = async (fileName) => {
1601
1623
  for (const variant of getExtVariants(fileName)) {
1602
1624
  try {
1603
- return require(variant);
1625
+ return await import(pathToFileURL(variant).href);
1604
1626
  }
1605
1627
  catch {
1606
1628
  continue;
@@ -1608,12 +1630,15 @@ const REQUIRE_MODULE_FACTORY = (fileName) => {
1608
1630
  }
1609
1631
  return null;
1610
1632
  };
1611
- const IMPORT_MODULE_FACTORY = async (fileName) => {
1633
+ const BABEL_MODULE_FACTORY = async (fileName, self) => {
1612
1634
  for (const variant of getExtVariants(fileName)) {
1613
1635
  try {
1614
- return await import(variant);
1636
+ const code = await fs$1.readFile(variant, "utf-8");
1637
+ const { exports } = self.babelService.transpileAndRun(code);
1638
+ return exports.default ?? exports;
1615
1639
  }
1616
- catch {
1640
+ catch (error) {
1641
+ console.log(getErrorMessage(error));
1617
1642
  continue;
1618
1643
  }
1619
1644
  }
@@ -1628,18 +1653,22 @@ const LOAD_MODULE_MODULE_FN = async (fileName, self) => {
1628
1653
  .then(() => true)
1629
1654
  .catch(() => false);
1630
1655
  const resolvedFile = hasOverride ? overridePath : targetPath;
1631
- if ((Ctor = REQUIRE_MODULE_FACTORY(resolvedFile))) {
1656
+ if ((Ctor = REQUIRE_MODULE_FACTORY())) {
1632
1657
  return typeof Ctor === "function" ? new Ctor() : Ctor;
1633
1658
  }
1634
1659
  if ((Ctor = await IMPORT_MODULE_FACTORY(resolvedFile))) {
1635
1660
  return typeof Ctor === "function" ? new Ctor() : Ctor;
1636
1661
  }
1662
+ if ((Ctor = await BABEL_MODULE_FACTORY(resolvedFile, self))) {
1663
+ return typeof Ctor === "function" ? new Ctor() : Ctor;
1664
+ }
1637
1665
  throw new Error(`Module module import failed for file: ${resolvedFile}`);
1638
1666
  };
1639
1667
  class ModuleConnectionService {
1640
1668
  constructor() {
1641
1669
  this.loggerService = inject(TYPES.loggerService);
1642
1670
  this.resolveService = inject(TYPES.resolveService);
1671
+ this.babelService = inject(TYPES.babelService);
1643
1672
  this.getInstance = memoize(([fileName]) => `${fileName}`, async (fileName) => {
1644
1673
  this.loggerService.log("moduleConnectionService getInstance", {
1645
1674
  fileName,
@@ -1833,6 +1862,62 @@ class LiveProviderService {
1833
1862
  }
1834
1863
  }
1835
1864
 
1865
+ registerPlugin("plugin-transform-modules-umd", pluginUMD);
1866
+ const require = createRequire(import.meta.url);
1867
+ const __filename = fileURLToPath(import.meta.url);
1868
+ const __dirname = path.dirname(__filename);
1869
+ const BacktestKitCli = new Proxy({}, {
1870
+ get(_target, prop) {
1871
+ throw new Error(`@backtest-kit/cli is not available in this context (accessed: ${String(prop)})`);
1872
+ },
1873
+ });
1874
+ class BabelService {
1875
+ constructor() {
1876
+ this.loggerService = inject(TYPES.loggerService);
1877
+ this.transpile = (code) => {
1878
+ this.loggerService.log("babelService transpile", { codeLen: code.length });
1879
+ const result = transform(code, {
1880
+ filename: "index.ts",
1881
+ presets: ["env", "typescript"],
1882
+ plugins: [
1883
+ [
1884
+ "plugin-transform-modules-umd",
1885
+ {
1886
+ globals: {
1887
+ "backtest-kit": "BacktestKit",
1888
+ "@backtest-kit/cli": "BacktestKitCli",
1889
+ },
1890
+ moduleId: "Executor",
1891
+ },
1892
+ ],
1893
+ ],
1894
+ parserOpts: { strictMode: false },
1895
+ });
1896
+ if (!result.code) {
1897
+ throw new Error("BabelService transpile failed");
1898
+ }
1899
+ return result.code;
1900
+ };
1901
+ this.transpileAndRun = (code) => {
1902
+ this.loggerService.log("babelService transpileAndRun", {
1903
+ codeLen: code.length,
1904
+ });
1905
+ const module = { exports: {} };
1906
+ const exports = module.exports;
1907
+ eval(this.transpile(code));
1908
+ return {
1909
+ require,
1910
+ __filename,
1911
+ __dirname,
1912
+ exports,
1913
+ module,
1914
+ };
1915
+ };
1916
+ }
1917
+ }
1918
+ globalThis.BacktestKit = BacktestKit;
1919
+ globalThis.BacktestKitCli = BacktestKitCli;
1920
+
1836
1921
  {
1837
1922
  provide(TYPES.quickchartApiService, () => new QuickchartApiService());
1838
1923
  provide(TYPES.telegramApiService, () => new TelegramApiService());
@@ -1841,6 +1926,7 @@ class LiveProviderService {
1841
1926
  provide(TYPES.errorService, () => new ErrorService());
1842
1927
  provide(TYPES.loggerService, () => new LoggerService());
1843
1928
  provide(TYPES.resolveService, () => new ResolveService());
1929
+ provide(TYPES.babelService, () => new BabelService());
1844
1930
  }
1845
1931
  {
1846
1932
  provide(TYPES.moduleConnectionService, () => new ModuleConnectionService());
@@ -1879,6 +1965,7 @@ const baseServices = {
1879
1965
  errorService: inject(TYPES.errorService),
1880
1966
  loggerService: inject(TYPES.loggerService),
1881
1967
  resolveService: inject(TYPES.resolveService),
1968
+ babelService: inject(TYPES.babelService),
1882
1969
  };
1883
1970
  const connectionServices = {
1884
1971
  moduleConnectionService: inject(TYPES.moduleConnectionService),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backtest-kit/cli",
3
- "version": "3.3.4",
3
+ "version": "3.3.6",
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",
@@ -58,8 +58,10 @@
58
58
  "default": "./build/index.cjs"
59
59
  },
60
60
  "devDependencies": {
61
+ "@babel/plugin-transform-modules-umd": "7.27.1",
62
+ "@babel/standalone": "7.29.1",
61
63
  "@backtest-kit/ui": "3.3.2",
62
- "markdown-it": "14.1.1",
64
+ "@rollup/plugin-replace": "6.0.3",
63
65
  "@rollup/plugin-typescript": "11.1.6",
64
66
  "@types/image-size": "0.7.0",
65
67
  "@types/jsdom": "21.1.7",
@@ -68,6 +70,7 @@
68
70
  "@types/stack-trace": "0.0.33",
69
71
  "backtest-kit": "3.3.2",
70
72
  "glob": "11.0.1",
73
+ "markdown-it": "14.1.1",
71
74
  "rimraf": "6.0.1",
72
75
  "rollup": "3.29.5",
73
76
  "rollup-plugin-dts": "6.1.1",
@@ -75,15 +78,15 @@
75
78
  "ts-morph": "27.0.2",
76
79
  "tslib": "2.7.0",
77
80
  "typedoc": "0.27.9",
78
- "tsx": "4.19.3",
79
81
  "worker-testbed": "1.0.12"
80
82
  },
81
83
  "peerDependencies": {
82
- "typescript": "^5.0.0",
84
+ "@babel/plugin-transform-modules-umd": "^7.27.1",
85
+ "@babel/standalone": "^7.29.1",
83
86
  "@backtest-kit/ui": "^3.3.2",
84
87
  "backtest-kit": "^3.3.2",
85
88
  "markdown-it": "^14.1.1",
86
- "tsx": "^4.19.3"
89
+ "typescript": "^5.0.0"
87
90
  },
88
91
  "dependencies": {
89
92
  "ccxt": "4.5.39",
package/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as functools_kit from 'functools-kit';
2
+ import * as BacktestKit from 'backtest-kit';
2
3
  import { CandleInterval, TrailingTakeCommit, TrailingStopCommit, BreakevenCommit, PartialProfitCommit, PartialLossCommit, IStrategyTickResultScheduled, IStrategyTickResultCancelled, IStrategyTickResultOpened, IStrategyTickResultClosed, RiskContract, AverageBuyCommit } from 'backtest-kit';
3
4
  import { Input } from 'telegraf';
4
5
 
@@ -85,8 +86,30 @@ declare class FrameSchemaService {
85
86
  addSchema: (() => Promise<void>) & functools_kit.ISingleshotClearable;
86
87
  }
87
88
 
89
+ declare const BacktestKitCli: {};
90
+ declare global {
91
+ interface Window {
92
+ BacktestKit: typeof BacktestKit;
93
+ BacktestKitCli: typeof BacktestKitCli;
94
+ }
95
+ }
96
+ declare class BabelService {
97
+ readonly loggerService: LoggerService;
98
+ transpile: (code: string) => any;
99
+ transpileAndRun: (code: string) => {
100
+ require: NodeRequire;
101
+ __filename: string;
102
+ __dirname: string;
103
+ exports: Record<string, unknown>;
104
+ module: {
105
+ exports: Record<string, unknown>;
106
+ };
107
+ };
108
+ }
109
+
88
110
  declare class ResolveService {
89
- private readonly loggerService;
111
+ readonly loggerService: LoggerService;
112
+ readonly babelService: BabelService;
90
113
  readonly DEFAULT_TEMPLATE_DIR: string;
91
114
  readonly OVERRIDE_TEMPLATE_DIR: string;
92
115
  readonly OVERRIDE_MODULES_DIR: string;
@@ -203,6 +226,7 @@ type TBaseModuleCtor = new () => BaseModule;
203
226
  declare class ModuleConnectionService {
204
227
  readonly loggerService: LoggerService;
205
228
  readonly resolveService: ResolveService;
229
+ readonly babelService: BabelService;
206
230
  getInstance: ((fileName: string) => Promise<BaseModule>) & functools_kit.IClearableMemoize<string> & functools_kit.IControlMemoize<string, Promise<Partial<ILiveModule>>>;
207
231
  }
208
232
 
@@ -243,6 +267,7 @@ declare const cli: {
243
267
  errorService: ErrorService;
244
268
  loggerService: LoggerService;
245
269
  resolveService: ResolveService;
270
+ babelService: BabelService;
246
271
  telegramApiService: TelegramApiService;
247
272
  quickchartApiService: QuickchartApiService;
248
273
  };