@backtest-kit/cli 3.3.4 → 3.3.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/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,8 +238,8 @@ 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);
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$1 = path.dirname(__filename$2);
239
243
  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)));
240
244
  const REQUIRE_ENTRY_FACTORY = (filePath) => {
241
245
  try {
@@ -247,25 +251,37 @@ 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 {
265
+ return false;
266
+ }
255
267
  };
256
- const LOAD_ENTRY_FN = async (filePath) => {
257
- if (filePath.endsWith('.ts') || filePath.endsWith('.tsx')) {
258
- await TSX_ENTRY_FACTORY(filePath);
268
+ const LOAD_ENTRY_FN = async (filePath, self) => {
269
+ if (REQUIRE_ENTRY_FACTORY(filePath)) {
270
+ return;
271
+ }
272
+ if (await IMPORT_ENTRY_FACTORY()) {
259
273
  return;
260
274
  }
261
- if (!REQUIRE_ENTRY_FACTORY(filePath)) {
262
- await IMPORT_ENTRY_FACTORY(filePath);
275
+ if (await BABEL_ENTRY_FACTORY(filePath, self)) {
276
+ return;
263
277
  }
278
+ throw new Error(`Failed to load entry point: ${filePath}`);
264
279
  };
265
280
  let _is_launched = false;
266
281
  class ResolveService {
267
282
  constructor() {
268
283
  this.loggerService = inject(TYPES.loggerService);
284
+ this.babelService = inject(TYPES.babelService);
269
285
  this.DEFAULT_TEMPLATE_DIR = path.resolve(__dirname$1, '..', 'template');
270
286
  this.OVERRIDE_TEMPLATE_DIR = path.resolve(process.cwd(), 'template');
271
287
  this.OVERRIDE_MODULES_DIR = path.resolve(process.cwd(), 'modules');
@@ -282,7 +298,7 @@ class ResolveService {
282
298
  process.chdir(moduleRoot);
283
299
  dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
284
300
  dotenv.config({ path: path.join(moduleRoot, '.env'), override: true, quiet: true });
285
- await LOAD_ENTRY_FN(absolutePath);
301
+ await LOAD_ENTRY_FN(absolutePath, this);
286
302
  await entrySubject.next(absolutePath);
287
303
  }
288
304
  _is_launched = true;
@@ -312,7 +328,7 @@ var ExchangeName$1 = ExchangeName;
312
328
  const ADD_EXCHANGE_FN = (self) => {
313
329
  self.loggerService.log("Adding CCXT Binance as a default exchange schema");
314
330
  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({
331
+ BacktestKit.addExchangeSchema({
316
332
  exchangeName: ExchangeName$1.DefaultExchange,
317
333
  getCandles: async (symbol, interval, since, limit) => {
318
334
  const exchange = await getExchange();
@@ -331,7 +347,7 @@ const ADD_EXCHANGE_FN = (self) => {
331
347
  const market = exchange.market(symbol);
332
348
  const tickSize = market.limits?.price?.min || market.precision?.price;
333
349
  if (tickSize !== undefined) {
334
- return backtestKit.roundTicks(price, tickSize);
350
+ return BacktestKit.roundTicks(price, tickSize);
335
351
  }
336
352
  return exchange.priceToPrecision(symbol, price);
337
353
  },
@@ -340,7 +356,7 @@ const ADD_EXCHANGE_FN = (self) => {
340
356
  const market = exchange.market(symbol);
341
357
  const stepSize = market.limits?.amount?.min || market.precision?.amount;
342
358
  if (stepSize !== undefined) {
343
- return backtestKit.roundTicks(quantity, stepSize);
359
+ return BacktestKit.roundTicks(quantity, stepSize);
344
360
  }
345
361
  return exchange.amountToPrecision(symbol, quantity);
346
362
  },
@@ -369,7 +385,7 @@ class ExchangeSchemaService {
369
385
  this.loggerService = inject(TYPES.loggerService);
370
386
  this.addSchema = functoolsKit.singleshot(async () => {
371
387
  this.loggerService.log("exchangeSchemaService addSchema");
372
- const { length } = await backtestKit.listExchangeSchema();
388
+ const { length } = await BacktestKit.listExchangeSchema();
373
389
  !length && ADD_EXCHANGE_FN(this);
374
390
  });
375
391
  }
@@ -446,7 +462,7 @@ const getArgs = functoolsKit.singleshot(() => {
446
462
  const ADD_FRAME_FN = (self) => {
447
463
  self.loggerService.log("Adding February 2024 as a default frame schema");
448
464
  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({
465
+ BacktestKit.addFrameSchema({
450
466
  frameName: FrameName$1.DefaultFrame,
451
467
  interval: "1m",
452
468
  startDate: new Date("2024-02-01T00:00:00Z"),
@@ -461,7 +477,7 @@ class FrameSchemaService {
461
477
  if (!getArgs().values.backtest) {
462
478
  return;
463
479
  }
464
- const { length } = await backtestKit.listFrameSchema();
480
+ const { length } = await BacktestKit.listFrameSchema();
465
481
  !length && ADD_FRAME_FN(this);
466
482
  });
467
483
  }
@@ -481,11 +497,11 @@ class SymbolSchemaService {
481
497
 
482
498
  const notifyFinish = functoolsKit.singleshot(() => {
483
499
  let disposeRef;
484
- const unLive = backtestKit.listenDoneLive(() => {
500
+ const unLive = BacktestKit.listenDoneLive(() => {
485
501
  console.log("Live trading finished");
486
502
  disposeRef && disposeRef();
487
503
  });
488
- const unBacktest = backtestKit.listenDoneBacktest(() => {
504
+ const unBacktest = BacktestKit.listenDoneBacktest(() => {
489
505
  console.log("Backtest trading finished");
490
506
  disposeRef && disposeRef();
491
507
  });
@@ -499,7 +515,7 @@ const getEntry = (metaUrl) => {
499
515
 
500
516
  const notifyVerbose = functoolsKit.singleshot(() => {
501
517
  console.log("Using verbose logging...");
502
- backtestKit.listenSignal((event) => {
518
+ BacktestKit.listenSignal((event) => {
503
519
  if (event.action === "scheduled") {
504
520
  console.log(`[POSITION SCHEDULED] ${event.symbol}`);
505
521
  console.log(` Strategy: ${event.strategyName}`);
@@ -579,9 +595,9 @@ class BacktestMainService {
579
595
  this.frameSchemaService.addSchema();
580
596
  }
581
597
  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();
598
+ const [defaultStrategyName = null] = await BacktestKit.listStrategySchema();
599
+ const [defaultExchangeName = null] = await BacktestKit.listExchangeSchema();
600
+ const [defaultFrameName = null] = await BacktestKit.listFrameSchema();
585
601
  const strategyName = payload.strategy || defaultStrategyName?.strategyName;
586
602
  if (!strategyName) {
587
603
  throw new Error("Strategy name is required");
@@ -602,7 +618,7 @@ class BacktestMainService {
602
618
  });
603
619
  }
604
620
  if (payload.verbose) {
605
- backtestKit.overrideExchangeSchema({
621
+ BacktestKit.overrideExchangeSchema({
606
622
  exchangeName,
607
623
  callbacks: {
608
624
  onCandleData(symbol, interval, since) {
@@ -612,7 +628,7 @@ class BacktestMainService {
612
628
  });
613
629
  notifyVerbose();
614
630
  }
615
- backtestKit.Backtest.background(symbol, {
631
+ BacktestKit.Backtest.background(symbol, {
616
632
  strategyName,
617
633
  frameName,
618
634
  exchangeName,
@@ -671,8 +687,8 @@ class LiveMainService {
671
687
  this.symbolSchemaService.addSchema();
672
688
  }
673
689
  const symbol = payload.symbol || "BTCUSDT";
674
- const [defaultStrategyName = null] = await backtestKit.listStrategySchema();
675
- const [defaultExchangeName = null] = await backtestKit.listExchangeSchema();
690
+ const [defaultStrategyName = null] = await BacktestKit.listStrategySchema();
691
+ const [defaultExchangeName = null] = await BacktestKit.listExchangeSchema();
676
692
  const strategyName = payload.strategy || defaultStrategyName?.strategyName;
677
693
  if (!strategyName) {
678
694
  throw new Error("Strategy name is required");
@@ -682,7 +698,7 @@ class LiveMainService {
682
698
  throw new Error("Exchange name is required");
683
699
  }
684
700
  if (payload.verbose) {
685
- backtestKit.overrideExchangeSchema({
701
+ BacktestKit.overrideExchangeSchema({
686
702
  exchangeName,
687
703
  callbacks: {
688
704
  onCandleData(symbol, interval, since) {
@@ -692,7 +708,7 @@ class LiveMainService {
692
708
  });
693
709
  notifyVerbose();
694
710
  }
695
- backtestKit.Live.background(symbol, {
711
+ BacktestKit.Live.background(symbol, {
696
712
  strategyName,
697
713
  exchangeName,
698
714
  });
@@ -742,8 +758,8 @@ class PaperMainService {
742
758
  this.symbolSchemaService.addSchema();
743
759
  }
744
760
  const symbol = payload.symbol || "BTCUSDT";
745
- const [defaultStrategyName = null] = await backtestKit.listStrategySchema();
746
- const [defaultExchangeName = null] = await backtestKit.listExchangeSchema();
761
+ const [defaultStrategyName = null] = await BacktestKit.listStrategySchema();
762
+ const [defaultExchangeName = null] = await BacktestKit.listExchangeSchema();
747
763
  const strategyName = payload.strategy || defaultStrategyName?.strategyName;
748
764
  if (!strategyName) {
749
765
  throw new Error("Strategy name is required");
@@ -753,7 +769,7 @@ class PaperMainService {
753
769
  throw new Error("Exchange name is required");
754
770
  }
755
771
  if (payload.verbose) {
756
- backtestKit.overrideExchangeSchema({
772
+ BacktestKit.overrideExchangeSchema({
757
773
  exchangeName,
758
774
  callbacks: {
759
775
  onCandleData(symbol, interval, since) {
@@ -763,7 +779,7 @@ class PaperMainService {
763
779
  });
764
780
  notifyVerbose();
765
781
  }
766
- backtestKit.Live.background(symbol, {
782
+ BacktestKit.Live.background(symbol, {
767
783
  strategyName,
768
784
  exchangeName,
769
785
  });
@@ -873,7 +889,7 @@ class TelegramProviderService {
873
889
 
874
890
  const CANDLES_LIMIT = 160;
875
891
  const GET_CONFIG_FN = async (symbol, interval) => {
876
- const candles = await backtestKit.getCandles(symbol, interval, CANDLES_LIMIT);
892
+ const candles = await BacktestKit.getCandles(symbol, interval, CANDLES_LIMIT);
877
893
  const labels = candles.map(({ timestamp }) => new Date(timestamp).toLocaleTimeString("en-US", {
878
894
  hour: "2-digit",
879
895
  minute: "2-digit",
@@ -1289,7 +1305,7 @@ class TelegramWebService {
1289
1305
  }
1290
1306
 
1291
1307
  const GET_TIMEFRAME_RANGE_FN = async (frameName) => {
1292
- const frameList = await backtestKit.listFrameSchema();
1308
+ const frameList = await BacktestKit.listFrameSchema();
1293
1309
  const frameSchema = frameList.find((frameSchema) => frameSchema.frameName === frameName);
1294
1310
  if (!frameSchema) {
1295
1311
  throw new Error(`Frame with name ${frameName} not found`);
@@ -1300,7 +1316,7 @@ const GET_TIMEFRAME_RANGE_FN = async (frameName) => {
1300
1316
  const CACHE_CANDLES_FN = functoolsKit.retry(async (interval, dto) => {
1301
1317
  try {
1302
1318
  console.log(`Checking candles cache for ${dto.symbol} ${interval} from ${dto.from} to ${dto.to}`);
1303
- await backtestKit.checkCandles({
1319
+ await BacktestKit.checkCandles({
1304
1320
  exchangeName: dto.exchangeName,
1305
1321
  from: dto.from,
1306
1322
  to: dto.to,
@@ -1310,7 +1326,7 @@ const CACHE_CANDLES_FN = functoolsKit.retry(async (interval, dto) => {
1310
1326
  }
1311
1327
  catch (error) {
1312
1328
  console.log(`Caching candles for ${dto.symbol} ${interval} from ${dto.from} to ${dto.to}`);
1313
- await backtestKit.warmCandles({
1329
+ await BacktestKit.warmCandles({
1314
1330
  symbol: dto.symbol,
1315
1331
  exchangeName: dto.exchangeName,
1316
1332
  from: dto.from,
@@ -1467,10 +1483,10 @@ class TelegramLogicService {
1467
1483
  };
1468
1484
  this.connect = functoolsKit.singleshot(() => {
1469
1485
  this.loggerService.log("telegramLogicService connect");
1470
- const unRisk = backtestKit.listenRisk(async (event) => {
1486
+ const unRisk = BacktestKit.listenRisk(async (event) => {
1471
1487
  await this.notifyRisk(event);
1472
1488
  });
1473
- const unSignal = backtestKit.listenSignal(async (event) => {
1489
+ const unSignal = BacktestKit.listenSignal(async (event) => {
1474
1490
  if (event.action === "scheduled") {
1475
1491
  await this.notifyScheduled(event);
1476
1492
  return;
@@ -1488,7 +1504,7 @@ class TelegramLogicService {
1488
1504
  return;
1489
1505
  }
1490
1506
  });
1491
- const unCommit = backtestKit.listenStrategyCommit(async (event) => {
1507
+ const unCommit = BacktestKit.listenStrategyCommit(async (event) => {
1492
1508
  if (event.action === "trailing-take") {
1493
1509
  await this.notifyTrailingTake(event);
1494
1510
  return;
@@ -1631,9 +1647,16 @@ const REQUIRE_MODULE_FACTORY = (fileName) => {
1631
1647
  return null;
1632
1648
  };
1633
1649
  const IMPORT_MODULE_FACTORY = async (fileName) => {
1650
+ {
1651
+ return null;
1652
+ }
1653
+ };
1654
+ const BABEL_MODULE_FACTORY = async (fileName, self) => {
1634
1655
  for (const variant of getExtVariants(fileName)) {
1635
1656
  try {
1636
- return await import(variant);
1657
+ const code = await fs$1.readFile(variant, "utf-8");
1658
+ const exports = self.babelService.transpileAndRun(code);
1659
+ return exports.default ?? exports;
1637
1660
  }
1638
1661
  catch {
1639
1662
  continue;
@@ -1653,7 +1676,10 @@ const LOAD_MODULE_MODULE_FN = async (fileName, self) => {
1653
1676
  if ((Ctor = REQUIRE_MODULE_FACTORY(resolvedFile))) {
1654
1677
  return typeof Ctor === "function" ? new Ctor() : Ctor;
1655
1678
  }
1656
- if ((Ctor = await IMPORT_MODULE_FACTORY(resolvedFile))) {
1679
+ if ((Ctor = await IMPORT_MODULE_FACTORY())) {
1680
+ return typeof Ctor === "function" ? new Ctor() : Ctor;
1681
+ }
1682
+ if ((Ctor = await BABEL_MODULE_FACTORY(resolvedFile, self))) {
1657
1683
  return typeof Ctor === "function" ? new Ctor() : Ctor;
1658
1684
  }
1659
1685
  throw new Error(`Module module import failed for file: ${resolvedFile}`);
@@ -1662,6 +1688,7 @@ class ModuleConnectionService {
1662
1688
  constructor() {
1663
1689
  this.loggerService = inject(TYPES.loggerService);
1664
1690
  this.resolveService = inject(TYPES.resolveService);
1691
+ this.babelService = inject(TYPES.babelService);
1665
1692
  this.getInstance = functoolsKit.memoize(([fileName]) => `${fileName}`, async (fileName) => {
1666
1693
  this.loggerService.log("moduleConnectionService getInstance", {
1667
1694
  fileName,
@@ -1788,10 +1815,10 @@ class LiveProviderService {
1788
1815
  console.log("No ./modules/live.module.mjs found, live trading failed to initialize");
1789
1816
  process.exit(-1);
1790
1817
  });
1791
- const unRisk = backtestKit.listenRisk(async (event) => {
1818
+ const unRisk = BacktestKit.listenRisk(async (event) => {
1792
1819
  await this.handleRisk(event);
1793
1820
  });
1794
- const unSignal = backtestKit.listenSignal(async (event) => {
1821
+ const unSignal = BacktestKit.listenSignal(async (event) => {
1795
1822
  if (event.action === "scheduled") {
1796
1823
  await this.handleScheduled(event);
1797
1824
  return;
@@ -1809,7 +1836,7 @@ class LiveProviderService {
1809
1836
  return;
1810
1837
  }
1811
1838
  });
1812
- const unCommit = backtestKit.listenStrategyCommit(async (event) => {
1839
+ const unCommit = BacktestKit.listenStrategyCommit(async (event) => {
1813
1840
  if (event.action === "trailing-take") {
1814
1841
  await this.handleTrailingTake(event);
1815
1842
  return;
@@ -1855,6 +1882,55 @@ class LiveProviderService {
1855
1882
  }
1856
1883
  }
1857
1884
 
1885
+ standalone.registerPlugin("plugin-transform-modules-umd", pluginUMD);
1886
+ 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)));
1887
+ 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)));
1888
+ path.dirname(__filename$1);
1889
+ const BacktestKitCli = new Proxy({}, {
1890
+ get(_target, prop) {
1891
+ throw new Error(`@backtest-kit/cli is not available in this context (accessed: ${String(prop)})`);
1892
+ },
1893
+ });
1894
+ class BabelService {
1895
+ constructor() {
1896
+ this.loggerService = inject(TYPES.loggerService);
1897
+ this.transpile = (code) => {
1898
+ this.loggerService.log("babelService transpile", { codeLen: code.length });
1899
+ const result = standalone.transform(code, {
1900
+ filename: "index.ts",
1901
+ presets: ["env", "typescript"],
1902
+ plugins: [
1903
+ [
1904
+ "plugin-transform-modules-umd",
1905
+ {
1906
+ globals: {
1907
+ "backtest-kit": "BacktestKit",
1908
+ "@backtest-kit/cli": "BacktestKitCli",
1909
+ },
1910
+ moduleId: "Executor",
1911
+ },
1912
+ ],
1913
+ ],
1914
+ parserOpts: { strictMode: false },
1915
+ });
1916
+ if (!result.code) {
1917
+ throw new Error("BabelService transpile failed");
1918
+ }
1919
+ return result.code;
1920
+ };
1921
+ this.transpileAndRun = (code) => {
1922
+ this.loggerService.log("babelService transpileAndRun", {
1923
+ codeLen: code.length,
1924
+ });
1925
+ const module = { exports: {} };
1926
+ eval(this.transpile(code));
1927
+ return module.exports;
1928
+ };
1929
+ }
1930
+ }
1931
+ globalThis.BacktestKit = BacktestKit__namespace;
1932
+ globalThis.BacktestKitCli = BacktestKitCli;
1933
+
1858
1934
  {
1859
1935
  provide(TYPES.quickchartApiService, () => new QuickchartApiService());
1860
1936
  provide(TYPES.telegramApiService, () => new TelegramApiService());
@@ -1863,6 +1939,7 @@ class LiveProviderService {
1863
1939
  provide(TYPES.errorService, () => new ErrorService());
1864
1940
  provide(TYPES.loggerService, () => new LoggerService());
1865
1941
  provide(TYPES.resolveService, () => new ResolveService());
1942
+ provide(TYPES.babelService, () => new BabelService());
1866
1943
  }
1867
1944
  {
1868
1945
  provide(TYPES.moduleConnectionService, () => new ModuleConnectionService());
@@ -1901,6 +1978,7 @@ const baseServices = {
1901
1978
  errorService: inject(TYPES.errorService),
1902
1979
  loggerService: inject(TYPES.loggerService),
1903
1980
  resolveService: inject(TYPES.resolveService),
1981
+ babelService: inject(TYPES.babelService),
1904
1982
  };
1905
1983
  const connectionServices = {
1906
1984
  moduleConnectionService: inject(TYPES.moduleConnectionService),
@@ -1949,7 +2027,7 @@ const notifyShutdown = functoolsKit.singleshot(async () => {
1949
2027
 
1950
2028
  const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
1951
2029
  process.off("SIGINT", BEFORE_EXIT_FN$4);
1952
- const [running = null] = await backtestKit.Backtest.list();
2030
+ const [running = null] = await BacktestKit.Backtest.list();
1953
2031
  if (!running) {
1954
2032
  return;
1955
2033
  }
@@ -1958,7 +2036,7 @@ const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
1958
2036
  if (status === "fulfilled") {
1959
2037
  return;
1960
2038
  }
1961
- backtestKit.Backtest.stop(symbol, {
2039
+ BacktestKit.Backtest.stop(symbol, {
1962
2040
  exchangeName,
1963
2041
  strategyName,
1964
2042
  frameName,
@@ -1982,7 +2060,7 @@ main$4();
1982
2060
 
1983
2061
  const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
1984
2062
  process.off("SIGINT", BEFORE_EXIT_FN$3);
1985
- const [running = null] = await backtestKit.Live.list();
2063
+ const [running = null] = await BacktestKit.Live.list();
1986
2064
  if (!running) {
1987
2065
  return;
1988
2066
  }
@@ -1991,7 +2069,7 @@ const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
1991
2069
  if (status === "fulfilled") {
1992
2070
  return;
1993
2071
  }
1994
- backtestKit.Live.stop(symbol, {
2072
+ BacktestKit.Live.stop(symbol, {
1995
2073
  exchangeName,
1996
2074
  strategyName,
1997
2075
  });
@@ -2014,7 +2092,7 @@ main$3();
2014
2092
 
2015
2093
  const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
2016
2094
  process.off("SIGINT", BEFORE_EXIT_FN$2);
2017
- const [running = null] = await backtestKit.Live.list();
2095
+ const [running = null] = await BacktestKit.Live.list();
2018
2096
  if (!running) {
2019
2097
  return;
2020
2098
  }
@@ -2023,11 +2101,11 @@ const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
2023
2101
  if (status === "fulfilled") {
2024
2102
  return;
2025
2103
  }
2026
- backtestKit.Live.stop(symbol, {
2104
+ BacktestKit.Live.stop(symbol, {
2027
2105
  exchangeName,
2028
2106
  strategyName,
2029
2107
  });
2030
- backtestKit.listenDoneLive(cli.liveProviderService.disable);
2108
+ BacktestKit.listenDoneLive(cli.liveProviderService.disable);
2031
2109
  });
2032
2110
  const listenGracefulShutdown$2 = functoolsKit.singleshot(() => {
2033
2111
  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,38 +216,50 @@ 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 = 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 {
243
+ return false;
244
+ }
233
245
  };
234
- const LOAD_ENTRY_FN = async (filePath) => {
235
- if (filePath.endsWith('.ts') || filePath.endsWith('.tsx')) {
236
- await TSX_ENTRY_FACTORY(filePath);
246
+ const LOAD_ENTRY_FN = async (filePath, self) => {
247
+ if (REQUIRE_ENTRY_FACTORY()) {
237
248
  return;
238
249
  }
239
- if (!REQUIRE_ENTRY_FACTORY(filePath)) {
240
- await IMPORT_ENTRY_FACTORY(filePath);
250
+ if (await IMPORT_ENTRY_FACTORY(filePath)) {
251
+ return;
241
252
  }
253
+ if (await BABEL_ENTRY_FACTORY(filePath, self)) {
254
+ return;
255
+ }
256
+ throw new Error(`Failed to load entry point: ${filePath}`);
242
257
  };
243
258
  let _is_launched = false;
244
259
  class ResolveService {
245
260
  constructor() {
246
261
  this.loggerService = inject(TYPES.loggerService);
262
+ this.babelService = inject(TYPES.babelService);
247
263
  this.DEFAULT_TEMPLATE_DIR = path.resolve(__dirname, '..', 'template');
248
264
  this.OVERRIDE_TEMPLATE_DIR = path.resolve(process.cwd(), 'template');
249
265
  this.OVERRIDE_MODULES_DIR = path.resolve(process.cwd(), 'modules');
@@ -260,7 +276,7 @@ class ResolveService {
260
276
  process.chdir(moduleRoot);
261
277
  dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
262
278
  dotenv.config({ path: path.join(moduleRoot, '.env'), override: true, quiet: true });
263
- await LOAD_ENTRY_FN(absolutePath);
279
+ await LOAD_ENTRY_FN(absolutePath, this);
264
280
  await entrySubject.next(absolutePath);
265
281
  }
266
282
  _is_launched = true;
@@ -1591,16 +1607,21 @@ class TelegramTemplateService {
1591
1607
  }
1592
1608
  }
1593
1609
 
1594
- const require = createRequire(import.meta.url);
1610
+ createRequire(import.meta.url);
1595
1611
  const getExtVariants = (fileName) => {
1596
1612
  const ext = path.extname(fileName);
1597
1613
  const base = ext ? fileName.slice(0, -ext.length) : fileName;
1598
1614
  return [fileName, `${base}.cjs`, `${base}.mjs`];
1599
1615
  };
1600
1616
  const REQUIRE_MODULE_FACTORY = (fileName) => {
1617
+ {
1618
+ return null;
1619
+ }
1620
+ };
1621
+ const IMPORT_MODULE_FACTORY = async (fileName) => {
1601
1622
  for (const variant of getExtVariants(fileName)) {
1602
1623
  try {
1603
- return require(variant);
1624
+ return await import(pathToFileURL(variant).href);
1604
1625
  }
1605
1626
  catch {
1606
1627
  continue;
@@ -1608,10 +1629,12 @@ const REQUIRE_MODULE_FACTORY = (fileName) => {
1608
1629
  }
1609
1630
  return null;
1610
1631
  };
1611
- const IMPORT_MODULE_FACTORY = async (fileName) => {
1632
+ const BABEL_MODULE_FACTORY = async (fileName, self) => {
1612
1633
  for (const variant of getExtVariants(fileName)) {
1613
1634
  try {
1614
- return await import(variant);
1635
+ const code = await fs$1.readFile(variant, "utf-8");
1636
+ const exports = self.babelService.transpileAndRun(code);
1637
+ return exports.default ?? exports;
1615
1638
  }
1616
1639
  catch {
1617
1640
  continue;
@@ -1628,18 +1651,22 @@ const LOAD_MODULE_MODULE_FN = async (fileName, self) => {
1628
1651
  .then(() => true)
1629
1652
  .catch(() => false);
1630
1653
  const resolvedFile = hasOverride ? overridePath : targetPath;
1631
- if ((Ctor = REQUIRE_MODULE_FACTORY(resolvedFile))) {
1654
+ if ((Ctor = REQUIRE_MODULE_FACTORY())) {
1632
1655
  return typeof Ctor === "function" ? new Ctor() : Ctor;
1633
1656
  }
1634
1657
  if ((Ctor = await IMPORT_MODULE_FACTORY(resolvedFile))) {
1635
1658
  return typeof Ctor === "function" ? new Ctor() : Ctor;
1636
1659
  }
1660
+ if ((Ctor = await BABEL_MODULE_FACTORY(resolvedFile, self))) {
1661
+ return typeof Ctor === "function" ? new Ctor() : Ctor;
1662
+ }
1637
1663
  throw new Error(`Module module import failed for file: ${resolvedFile}`);
1638
1664
  };
1639
1665
  class ModuleConnectionService {
1640
1666
  constructor() {
1641
1667
  this.loggerService = inject(TYPES.loggerService);
1642
1668
  this.resolveService = inject(TYPES.resolveService);
1669
+ this.babelService = inject(TYPES.babelService);
1643
1670
  this.getInstance = memoize(([fileName]) => `${fileName}`, async (fileName) => {
1644
1671
  this.loggerService.log("moduleConnectionService getInstance", {
1645
1672
  fileName,
@@ -1833,6 +1860,55 @@ class LiveProviderService {
1833
1860
  }
1834
1861
  }
1835
1862
 
1863
+ registerPlugin("plugin-transform-modules-umd", pluginUMD);
1864
+ createRequire(import.meta.url);
1865
+ const __filename = fileURLToPath(import.meta.url);
1866
+ path.dirname(__filename);
1867
+ const BacktestKitCli = new Proxy({}, {
1868
+ get(_target, prop) {
1869
+ throw new Error(`@backtest-kit/cli is not available in this context (accessed: ${String(prop)})`);
1870
+ },
1871
+ });
1872
+ class BabelService {
1873
+ constructor() {
1874
+ this.loggerService = inject(TYPES.loggerService);
1875
+ this.transpile = (code) => {
1876
+ this.loggerService.log("babelService transpile", { codeLen: code.length });
1877
+ const result = transform(code, {
1878
+ filename: "index.ts",
1879
+ presets: ["env", "typescript"],
1880
+ plugins: [
1881
+ [
1882
+ "plugin-transform-modules-umd",
1883
+ {
1884
+ globals: {
1885
+ "backtest-kit": "BacktestKit",
1886
+ "@backtest-kit/cli": "BacktestKitCli",
1887
+ },
1888
+ moduleId: "Executor",
1889
+ },
1890
+ ],
1891
+ ],
1892
+ parserOpts: { strictMode: false },
1893
+ });
1894
+ if (!result.code) {
1895
+ throw new Error("BabelService transpile failed");
1896
+ }
1897
+ return result.code;
1898
+ };
1899
+ this.transpileAndRun = (code) => {
1900
+ this.loggerService.log("babelService transpileAndRun", {
1901
+ codeLen: code.length,
1902
+ });
1903
+ const module = { exports: {} };
1904
+ eval(this.transpile(code));
1905
+ return module.exports;
1906
+ };
1907
+ }
1908
+ }
1909
+ globalThis.BacktestKit = BacktestKit;
1910
+ globalThis.BacktestKitCli = BacktestKitCli;
1911
+
1836
1912
  {
1837
1913
  provide(TYPES.quickchartApiService, () => new QuickchartApiService());
1838
1914
  provide(TYPES.telegramApiService, () => new TelegramApiService());
@@ -1841,6 +1917,7 @@ class LiveProviderService {
1841
1917
  provide(TYPES.errorService, () => new ErrorService());
1842
1918
  provide(TYPES.loggerService, () => new LoggerService());
1843
1919
  provide(TYPES.resolveService, () => new ResolveService());
1920
+ provide(TYPES.babelService, () => new BabelService());
1844
1921
  }
1845
1922
  {
1846
1923
  provide(TYPES.moduleConnectionService, () => new ModuleConnectionService());
@@ -1879,6 +1956,7 @@ const baseServices = {
1879
1956
  errorService: inject(TYPES.errorService),
1880
1957
  loggerService: inject(TYPES.loggerService),
1881
1958
  resolveService: inject(TYPES.resolveService),
1959
+ babelService: inject(TYPES.babelService),
1882
1960
  };
1883
1961
  const connectionServices = {
1884
1962
  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.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",
@@ -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,22 @@ 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) => Record<string, unknown>;
100
+ }
101
+
88
102
  declare class ResolveService {
89
- private readonly loggerService;
103
+ readonly loggerService: LoggerService;
104
+ readonly babelService: BabelService;
90
105
  readonly DEFAULT_TEMPLATE_DIR: string;
91
106
  readonly OVERRIDE_TEMPLATE_DIR: string;
92
107
  readonly OVERRIDE_MODULES_DIR: string;
@@ -203,6 +218,7 @@ type TBaseModuleCtor = new () => BaseModule;
203
218
  declare class ModuleConnectionService {
204
219
  readonly loggerService: LoggerService;
205
220
  readonly resolveService: ResolveService;
221
+ readonly babelService: BabelService;
206
222
  getInstance: ((fileName: string) => Promise<BaseModule>) & functools_kit.IClearableMemoize<string> & functools_kit.IControlMemoize<string, Promise<Partial<ILiveModule>>>;
207
223
  }
208
224
 
@@ -243,6 +259,7 @@ declare const cli: {
243
259
  errorService: ErrorService;
244
260
  loggerService: LoggerService;
245
261
  resolveService: ResolveService;
262
+ babelService: BabelService;
246
263
  telegramApiService: TelegramApiService;
247
264
  quickchartApiService: QuickchartApiService;
248
265
  };