@backtest-kit/cli 5.4.0 → 5.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.cjs CHANGED
@@ -5,12 +5,11 @@ 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');
8
- var url = require('url');
9
- var module$1 = require('module');
10
8
  var path = require('path');
11
9
  var fs$1 = require('fs/promises');
12
10
  var dotenv = require('dotenv');
13
11
  var diKit = require('di-kit');
12
+ var url = require('url');
14
13
  var ccxt = require('ccxt');
15
14
  var util = require('util');
16
15
  var BacktestKitUi = require('@backtest-kit/ui');
@@ -25,6 +24,7 @@ var jsdom = require('jsdom');
25
24
  var Mustache = require('mustache');
26
25
  var standalone = require('@babel/standalone');
27
26
  var pluginUMD = require('@babel/plugin-transform-modules-umd');
27
+ var module$1 = require('module');
28
28
  var BacktestKitGraph = require('@backtest-kit/graph');
29
29
  var BacktestKitOllama = require('@backtest-kit/ollama');
30
30
  var BacktestKitPinets = require('@backtest-kit/pinets');
@@ -208,6 +208,7 @@ const baseServices$1 = {
208
208
  errorService: Symbol('errorService'),
209
209
  loggerService: Symbol('loggerService'),
210
210
  resolveService: Symbol('resolveService'),
211
+ loaderService: Symbol('loaderService'),
211
212
  babelService: Symbol('babelService'),
212
213
  };
213
214
  const connectionServices$1 = {
@@ -251,52 +252,14 @@ const TYPES = {
251
252
 
252
253
  const entrySubject = new functoolsKit.BehaviorSubject();
253
254
 
254
- 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)));
255
- const __dirname$2 = path.dirname(__filename$2);
256
- 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)));
257
- const REQUIRE_ENTRY_FACTORY = (filePath) => {
258
- try {
259
- require$2(filePath);
260
- return true;
261
- }
262
- catch {
263
- return false;
264
- }
265
- };
266
- const IMPORT_ENTRY_FACTORY = async (filePath) => {
267
- {
268
- return false;
269
- }
270
- };
271
- const BABEL_ENTRY_FACTORY = async (filePath, self) => {
272
- const code = await fs$1.readFile(filePath, "utf-8");
273
- try {
274
- await self.babelService.transpileAndRun(code);
275
- return true;
276
- }
277
- catch (error) {
278
- console.log(functoolsKit.getErrorMessage(error));
279
- return false;
280
- }
281
- };
282
- const LOAD_ENTRY_FN = async (filePath, self) => {
283
- if (REQUIRE_ENTRY_FACTORY(filePath)) {
284
- return;
285
- }
286
- if (await IMPORT_ENTRY_FACTORY()) {
287
- return;
288
- }
289
- if (await BABEL_ENTRY_FACTORY(filePath, self)) {
290
- return;
291
- }
292
- throw new Error(`Failed to load entry point: ${filePath}`);
293
- };
255
+ 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)));
256
+ const __dirname$1 = path.dirname(__filename$1);
294
257
  let _is_launched = false;
295
258
  class ResolveService {
296
259
  constructor() {
297
260
  this.loggerService = inject(TYPES.loggerService);
298
- this.babelService = inject(TYPES.babelService);
299
- this.DEFAULT_TEMPLATE_DIR = path.resolve(__dirname$2, '..', 'template');
261
+ this.loaderService = inject(TYPES.loaderService);
262
+ this.DEFAULT_TEMPLATE_DIR = path.resolve(__dirname$1, '..', 'template');
300
263
  this.OVERRIDE_TEMPLATE_DIR = path.resolve(process.cwd(), 'template');
301
264
  this.OVERRIDE_MODULES_DIR = path.resolve(process.cwd(), 'modules');
302
265
  this.attachEntryPoint = async (entryPoint) => {
@@ -313,7 +276,7 @@ class ResolveService {
313
276
  cwd !== moduleRoot && BacktestKit.Log.useJsonl();
314
277
  dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
315
278
  dotenv.config({ path: path.join(moduleRoot, '.env'), override: true, quiet: true });
316
- await LOAD_ENTRY_FN(absolutePath, this);
279
+ this.loaderService.import(absolutePath);
317
280
  await entrySubject.next(absolutePath);
318
281
  }
319
282
  _is_launched = true;
@@ -1750,12 +1713,10 @@ class TelegramTemplateService {
1750
1713
  }
1751
1714
  }
1752
1715
 
1753
- 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)));
1754
1716
  const getExtVariants = (fileName) => {
1755
1717
  const ext = path.extname(fileName);
1756
1718
  const base = ext ? fileName.slice(0, -ext.length) : fileName;
1757
1719
  return [
1758
- fileName,
1759
1720
  `${base}.cjs`,
1760
1721
  `${base}.mjs`,
1761
1722
  `${base}.ts`,
@@ -1763,32 +1724,16 @@ const getExtVariants = (fileName) => {
1763
1724
  `${base}.js`,
1764
1725
  ];
1765
1726
  };
1766
- const REQUIRE_MODULE_FACTORY = (fileName) => {
1727
+ const LOADER_FACTORY = async (fileName, self) => {
1767
1728
  for (const variant of getExtVariants(fileName)) {
1768
1729
  try {
1769
- require$1(variant);
1770
- return true;
1771
- }
1772
- catch {
1773
- continue;
1774
- }
1775
- }
1776
- return false;
1777
- };
1778
- const IMPORT_MODULE_FACTORY = async (fileName) => {
1779
- {
1780
- return false;
1781
- }
1782
- };
1783
- const BABEL_MODULE_FACTORY = async (fileName, self) => {
1784
- for (const variant of getExtVariants(fileName)) {
1785
- try {
1786
- const code = await fs$1.readFile(variant, "utf-8");
1787
- self.babelService.transpileAndRun(code);
1730
+ await fs$1.access(variant, fs.constants.F_OK | fs.constants.R_OK);
1731
+ self.loaderService.import(variant);
1788
1732
  return true;
1789
1733
  }
1790
1734
  catch (error) {
1791
- console.log(functoolsKit.getErrorMessage(error));
1735
+ const { values } = getArgs();
1736
+ values.verbose && console.log(functoolsKit.getErrorMessage(error));
1792
1737
  continue;
1793
1738
  }
1794
1739
  }
@@ -1802,13 +1747,7 @@ const LOAD_MODULE_MODULE_FN = async (fileName, self) => {
1802
1747
  .then(() => true)
1803
1748
  .catch(() => false);
1804
1749
  const resolvedFile = hasOverride ? overridePath : targetPath;
1805
- if (REQUIRE_MODULE_FACTORY(resolvedFile)) {
1806
- return true;
1807
- }
1808
- if (await IMPORT_MODULE_FACTORY()) {
1809
- return true;
1810
- }
1811
- if (await BABEL_MODULE_FACTORY(resolvedFile, self)) {
1750
+ if (LOADER_FACTORY(resolvedFile, self)) {
1812
1751
  return true;
1813
1752
  }
1814
1753
  console.warn(`Module module import failed for file: ${resolvedFile}`);
@@ -1818,7 +1757,7 @@ class ModuleConnectionService {
1818
1757
  constructor() {
1819
1758
  this.loggerService = inject(TYPES.loggerService);
1820
1759
  this.resolveService = inject(TYPES.resolveService);
1821
- this.babelService = inject(TYPES.babelService);
1760
+ this.loaderService = inject(TYPES.loaderService);
1822
1761
  this.loadModule = async (fileName) => {
1823
1762
  this.loggerService.log("moduleConnectionService getInstance", {
1824
1763
  fileName,
@@ -1829,36 +1768,6 @@ class ModuleConnectionService {
1829
1768
  }
1830
1769
 
1831
1770
  standalone.registerPlugin("plugin-transform-modules-umd", pluginUMD);
1832
- const getBaseRequire = functoolsKit.singleshot(() => {
1833
- const baseRequire = module$1.createRequire(path.join(process.cwd(), "index.cjs"));
1834
- return new Proxy(baseRequire, {
1835
- apply(_target, _this, args) {
1836
- const id = args[0];
1837
- if (id === "backtest-kit")
1838
- return globalThis.BacktestKit;
1839
- if (id === "@backtest-kit/cli")
1840
- return globalThis.BacktestKitCli;
1841
- if (id === "@backtest-kit/ui")
1842
- return globalThis.BacktestKitUi;
1843
- if (id === "@backtest-kit/graph")
1844
- return globalThis.BacktestKitGraph;
1845
- if (id === "@backtest-kit/ollama")
1846
- return globalThis.BacktestKitOllama;
1847
- if (id === "@backtest-kit/pinets")
1848
- return globalThis.BacktestKitPinets;
1849
- if (id === "@backtest-kit/signals")
1850
- return globalThis.BacktestKitSignals;
1851
- return baseRequire(id);
1852
- },
1853
- });
1854
- });
1855
- 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)));
1856
- const __dirname$1 = path.dirname(__filename$1);
1857
- const BacktestKitCli = new Proxy({}, {
1858
- get(_target, prop) {
1859
- throw new Error(`@backtest-kit/cli is not available in this context (accessed: ${String(prop)})`);
1860
- },
1861
- });
1862
1771
  class BabelService {
1863
1772
  constructor() {
1864
1773
  this.loggerService = inject(TYPES.loggerService);
@@ -1894,22 +1803,119 @@ class BabelService {
1894
1803
  }
1895
1804
  return result.code;
1896
1805
  };
1897
- this.transpileAndRun = (code) => {
1898
- this.loggerService.log("babelService transpileAndRun", {
1899
- codeLen: code.length,
1900
- });
1901
- const require = getBaseRequire();
1902
- const module = { exports: {} };
1903
- const exports = module.exports;
1904
- eval(this.transpile(code));
1905
- return {
1906
- require,
1907
- __filename: __filename$1,
1908
- __dirname: __dirname$1,
1909
- exports,
1910
- module,
1911
- };
1912
- };
1806
+ }
1807
+ }
1808
+
1809
+ const TRANSPILE_FN = (code, self) => {
1810
+ const require = self.getBaseRequire();
1811
+ const __filename = self.__filename;
1812
+ const __dirname = self.__dirname;
1813
+ const module = { exports: {} };
1814
+ const exports = module.exports;
1815
+ eval(self.params.babel.transpile(code));
1816
+ return {
1817
+ require,
1818
+ __filename,
1819
+ __dirname,
1820
+ exports,
1821
+ module,
1822
+ };
1823
+ };
1824
+ const REQUIRE_ENTRY_FACTORY = (filePath, self) => {
1825
+ const baseRequire = self.getBaseRequire();
1826
+ try {
1827
+ return baseRequire(filePath);
1828
+ }
1829
+ catch {
1830
+ return null;
1831
+ }
1832
+ };
1833
+ const BABEL_ENTRY_FACTORY = (filePath, self) => {
1834
+ try {
1835
+ const resolvedPath = path.resolve(self.__dirname, filePath);
1836
+ const code = fs.readFileSync(resolvedPath, "utf-8");
1837
+ const child = self.fork(path.dirname(resolvedPath));
1838
+ const { module } = TRANSPILE_FN(code, child);
1839
+ return "default" in module.exports
1840
+ ? module.exports.default
1841
+ : module.exports;
1842
+ }
1843
+ catch {
1844
+ return null;
1845
+ }
1846
+ };
1847
+ const ENTRY_FACTORY = (filePath, self) => {
1848
+ let result = null;
1849
+ if (result = REQUIRE_ENTRY_FACTORY(filePath, self)) {
1850
+ return result;
1851
+ }
1852
+ if (result = BABEL_ENTRY_FACTORY(filePath, self)) {
1853
+ return result;
1854
+ }
1855
+ throw new Error(`Failed to load module at ${filePath} (basepath: ${self.params.path})`);
1856
+ };
1857
+ const CREATE_BASE_REQUIRE_FN = (self) => {
1858
+ const baseRequire = module$1.createRequire(self.__filename);
1859
+ return new Proxy(baseRequire, {
1860
+ apply(_target, _this, args) {
1861
+ const id = args[0];
1862
+ if (id === "backtest-kit")
1863
+ return globalThis.BacktestKit;
1864
+ if (id === "@backtest-kit/cli")
1865
+ return globalThis.BacktestKitCli;
1866
+ if (id === "@backtest-kit/ui")
1867
+ return globalThis.BacktestKitUi;
1868
+ if (id === "@backtest-kit/graph")
1869
+ return globalThis.BacktestKitGraph;
1870
+ if (id === "@backtest-kit/ollama")
1871
+ return globalThis.BacktestKitOllama;
1872
+ if (id === "@backtest-kit/pinets")
1873
+ return globalThis.BacktestKitPinets;
1874
+ if (id === "@backtest-kit/signals")
1875
+ return globalThis.BacktestKitSignals;
1876
+ if (id.startsWith("./") || id.startsWith("../")) {
1877
+ const resolved = path.resolve(self.__dirname, id);
1878
+ const child = self.fork(path.dirname(resolved));
1879
+ return child.import(resolved);
1880
+ }
1881
+ return baseRequire(id);
1882
+ },
1883
+ });
1884
+ };
1885
+ const BacktestKitCli = new Proxy({}, {
1886
+ get(_target, prop) {
1887
+ throw new Error(`@backtest-kit/cli is not available in this context (accessed: ${String(prop)})`);
1888
+ },
1889
+ });
1890
+ class ClientLoader {
1891
+ constructor(params) {
1892
+ this.params = params;
1893
+ this.getBaseRequire = functoolsKit.singleshot(() => {
1894
+ this.params.logger.log("ClientLoader getBaseRequire", {
1895
+ basePath: this.params.path,
1896
+ });
1897
+ return CREATE_BASE_REQUIRE_FN(this);
1898
+ });
1899
+ this.__filename = path.join(params.path, "index.cjs");
1900
+ this.__dirname = path.dirname(this.__filename);
1901
+ }
1902
+ fork(basePath) {
1903
+ this.params.logger.log("ClientLoader fork", {
1904
+ basePath: this.params.path,
1905
+ path: basePath,
1906
+ });
1907
+ return new ClientLoader({
1908
+ path: basePath,
1909
+ babel: this.params.babel,
1910
+ logger: this.params.logger,
1911
+ });
1912
+ }
1913
+ import(filePath) {
1914
+ this.params.logger.log("ClientLoader import", {
1915
+ filePath,
1916
+ basePath: this.params.path,
1917
+ });
1918
+ return ENTRY_FACTORY(filePath, this);
1913
1919
  }
1914
1920
  }
1915
1921
  globalThis.BacktestKit = BacktestKit__namespace;
@@ -1920,6 +1926,25 @@ globalThis.BacktestKitOllama = BacktestKitOllama__namespace;
1920
1926
  globalThis.BacktestKitPinets = BacktestKitPinets__namespace;
1921
1927
  globalThis.BacktestKitSignals = BacktestKitSignals__namespace;
1922
1928
 
1929
+ class LoaderService {
1930
+ constructor() {
1931
+ this.babelService = inject(TYPES.babelService);
1932
+ this.loggerService = inject(TYPES.loggerService);
1933
+ this.getInstance = functoolsKit.memoize(([basePath]) => `${basePath}`, (basePath) => new ClientLoader({
1934
+ babel: this.babelService,
1935
+ logger: this.loggerService,
1936
+ path: basePath,
1937
+ }));
1938
+ this.import = async (filePath, basePath = process.cwd()) => {
1939
+ this.loggerService.log("loaderService import", {
1940
+ filePath,
1941
+ });
1942
+ const instance = this.getInstance(basePath);
1943
+ return instance.import(filePath);
1944
+ };
1945
+ }
1946
+ }
1947
+
1923
1948
  {
1924
1949
  provide(TYPES.quickchartApiService, () => new QuickchartApiService());
1925
1950
  provide(TYPES.telegramApiService, () => new TelegramApiService());
@@ -1928,6 +1953,7 @@ globalThis.BacktestKitSignals = BacktestKitSignals__namespace;
1928
1953
  provide(TYPES.errorService, () => new ErrorService());
1929
1954
  provide(TYPES.loggerService, () => new LoggerService());
1930
1955
  provide(TYPES.resolveService, () => new ResolveService());
1956
+ provide(TYPES.loaderService, () => new LoaderService());
1931
1957
  provide(TYPES.babelService, () => new BabelService());
1932
1958
  }
1933
1959
  {
@@ -1966,6 +1992,7 @@ const baseServices = {
1966
1992
  errorService: inject(TYPES.errorService),
1967
1993
  loggerService: inject(TYPES.loggerService),
1968
1994
  resolveService: inject(TYPES.resolveService),
1995
+ loaderService: inject(TYPES.loaderService),
1969
1996
  babelService: inject(TYPES.babelService),
1970
1997
  };
1971
1998
  const connectionServices = {
package/build/index.mjs CHANGED
@@ -4,12 +4,11 @@ import { Storage, Notification, Markdown, Report, StorageLive, StorageBacktest,
4
4
  import { getErrorMessage, errorData, singleshot, str, BehaviorSubject, compose, execpool, queued, sleep, randomString, createAwaiter, TIMEOUT_SYMBOL, typo, retry, trycatch, memoize } from 'functools-kit';
5
5
  import fs, { constants } from 'fs';
6
6
  import * as stackTrace from 'stack-trace';
7
- import { fileURLToPath, pathToFileURL } from 'url';
8
- import { createRequire } from 'module';
9
7
  import path from 'path';
10
8
  import fs$1, { access } from 'fs/promises';
11
9
  import dotenv from 'dotenv';
12
10
  import { createActivator } from 'di-kit';
11
+ import { fileURLToPath } from 'url';
13
12
  import ccxt from 'ccxt';
14
13
  import { parseArgs } from 'util';
15
14
  import * as BacktestKitUi from '@backtest-kit/ui';
@@ -25,6 +24,7 @@ import { JSDOM } from 'jsdom';
25
24
  import Mustache from 'mustache';
26
25
  import { registerPlugin, transform } from '@babel/standalone';
27
26
  import pluginUMD from '@babel/plugin-transform-modules-umd';
27
+ import { createRequire } from 'module';
28
28
  import * as BacktestKitGraph from '@backtest-kit/graph';
29
29
  import * as BacktestKitOllama from '@backtest-kit/ollama';
30
30
  import * as BacktestKitPinets from '@backtest-kit/pinets';
@@ -182,6 +182,7 @@ const baseServices$1 = {
182
182
  errorService: Symbol('errorService'),
183
183
  loggerService: Symbol('loggerService'),
184
184
  resolveService: Symbol('resolveService'),
185
+ loaderService: Symbol('loaderService'),
185
186
  babelService: Symbol('babelService'),
186
187
  };
187
188
  const connectionServices$1 = {
@@ -225,52 +226,14 @@ const TYPES = {
225
226
 
226
227
  const entrySubject = new BehaviorSubject();
227
228
 
228
- const __filename$1 = fileURLToPath(import.meta.url);
229
- const __dirname$1 = path.dirname(__filename$1);
230
- createRequire(import.meta.url);
231
- const REQUIRE_ENTRY_FACTORY = (filePath) => {
232
- {
233
- return false;
234
- }
235
- };
236
- const IMPORT_ENTRY_FACTORY = async (filePath) => {
237
- try {
238
- await import(pathToFileURL(filePath).href);
239
- return true;
240
- }
241
- catch {
242
- return false;
243
- }
244
- };
245
- const BABEL_ENTRY_FACTORY = async (filePath, self) => {
246
- const code = await fs$1.readFile(filePath, "utf-8");
247
- try {
248
- await self.babelService.transpileAndRun(code);
249
- return true;
250
- }
251
- catch (error) {
252
- console.log(getErrorMessage(error));
253
- return false;
254
- }
255
- };
256
- const LOAD_ENTRY_FN = async (filePath, self) => {
257
- if (REQUIRE_ENTRY_FACTORY()) {
258
- return;
259
- }
260
- if (await IMPORT_ENTRY_FACTORY(filePath)) {
261
- return;
262
- }
263
- if (await BABEL_ENTRY_FACTORY(filePath, self)) {
264
- return;
265
- }
266
- throw new Error(`Failed to load entry point: ${filePath}`);
267
- };
229
+ const __filename = fileURLToPath(import.meta.url);
230
+ const __dirname = path.dirname(__filename);
268
231
  let _is_launched = false;
269
232
  class ResolveService {
270
233
  constructor() {
271
234
  this.loggerService = inject(TYPES.loggerService);
272
- this.babelService = inject(TYPES.babelService);
273
- this.DEFAULT_TEMPLATE_DIR = path.resolve(__dirname$1, '..', 'template');
235
+ this.loaderService = inject(TYPES.loaderService);
236
+ this.DEFAULT_TEMPLATE_DIR = path.resolve(__dirname, '..', 'template');
274
237
  this.OVERRIDE_TEMPLATE_DIR = path.resolve(process.cwd(), 'template');
275
238
  this.OVERRIDE_MODULES_DIR = path.resolve(process.cwd(), 'modules');
276
239
  this.attachEntryPoint = async (entryPoint) => {
@@ -287,7 +250,7 @@ class ResolveService {
287
250
  cwd !== moduleRoot && Log.useJsonl();
288
251
  dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
289
252
  dotenv.config({ path: path.join(moduleRoot, '.env'), override: true, quiet: true });
290
- await LOAD_ENTRY_FN(absolutePath, this);
253
+ this.loaderService.import(absolutePath);
291
254
  await entrySubject.next(absolutePath);
292
255
  }
293
256
  _is_launched = true;
@@ -1724,12 +1687,10 @@ class TelegramTemplateService {
1724
1687
  }
1725
1688
  }
1726
1689
 
1727
- createRequire(import.meta.url);
1728
1690
  const getExtVariants = (fileName) => {
1729
1691
  const ext = path.extname(fileName);
1730
1692
  const base = ext ? fileName.slice(0, -ext.length) : fileName;
1731
1693
  return [
1732
- fileName,
1733
1694
  `${base}.cjs`,
1734
1695
  `${base}.mjs`,
1735
1696
  `${base}.ts`,
@@ -1737,32 +1698,16 @@ const getExtVariants = (fileName) => {
1737
1698
  `${base}.js`,
1738
1699
  ];
1739
1700
  };
1740
- const REQUIRE_MODULE_FACTORY = (fileName) => {
1741
- {
1742
- return false;
1743
- }
1744
- };
1745
- const IMPORT_MODULE_FACTORY = async (fileName) => {
1701
+ const LOADER_FACTORY = async (fileName, self) => {
1746
1702
  for (const variant of getExtVariants(fileName)) {
1747
1703
  try {
1748
- await import(pathToFileURL(variant).href);
1749
- return true;
1750
- }
1751
- catch {
1752
- continue;
1753
- }
1754
- }
1755
- return false;
1756
- };
1757
- const BABEL_MODULE_FACTORY = async (fileName, self) => {
1758
- for (const variant of getExtVariants(fileName)) {
1759
- try {
1760
- const code = await fs$1.readFile(variant, "utf-8");
1761
- self.babelService.transpileAndRun(code);
1704
+ await fs$1.access(variant, constants.F_OK | constants.R_OK);
1705
+ self.loaderService.import(variant);
1762
1706
  return true;
1763
1707
  }
1764
1708
  catch (error) {
1765
- console.log(getErrorMessage(error));
1709
+ const { values } = getArgs();
1710
+ values.verbose && console.log(getErrorMessage(error));
1766
1711
  continue;
1767
1712
  }
1768
1713
  }
@@ -1776,13 +1721,7 @@ const LOAD_MODULE_MODULE_FN = async (fileName, self) => {
1776
1721
  .then(() => true)
1777
1722
  .catch(() => false);
1778
1723
  const resolvedFile = hasOverride ? overridePath : targetPath;
1779
- if (REQUIRE_MODULE_FACTORY()) {
1780
- return true;
1781
- }
1782
- if (await IMPORT_MODULE_FACTORY(resolvedFile)) {
1783
- return true;
1784
- }
1785
- if (await BABEL_MODULE_FACTORY(resolvedFile, self)) {
1724
+ if (LOADER_FACTORY(resolvedFile, self)) {
1786
1725
  return true;
1787
1726
  }
1788
1727
  console.warn(`Module module import failed for file: ${resolvedFile}`);
@@ -1792,7 +1731,7 @@ class ModuleConnectionService {
1792
1731
  constructor() {
1793
1732
  this.loggerService = inject(TYPES.loggerService);
1794
1733
  this.resolveService = inject(TYPES.resolveService);
1795
- this.babelService = inject(TYPES.babelService);
1734
+ this.loaderService = inject(TYPES.loaderService);
1796
1735
  this.loadModule = async (fileName) => {
1797
1736
  this.loggerService.log("moduleConnectionService getInstance", {
1798
1737
  fileName,
@@ -1803,36 +1742,6 @@ class ModuleConnectionService {
1803
1742
  }
1804
1743
 
1805
1744
  registerPlugin("plugin-transform-modules-umd", pluginUMD);
1806
- const getBaseRequire = singleshot(() => {
1807
- const baseRequire = createRequire(path.join(process.cwd(), "index.cjs"));
1808
- return new Proxy(baseRequire, {
1809
- apply(_target, _this, args) {
1810
- const id = args[0];
1811
- if (id === "backtest-kit")
1812
- return globalThis.BacktestKit;
1813
- if (id === "@backtest-kit/cli")
1814
- return globalThis.BacktestKitCli;
1815
- if (id === "@backtest-kit/ui")
1816
- return globalThis.BacktestKitUi;
1817
- if (id === "@backtest-kit/graph")
1818
- return globalThis.BacktestKitGraph;
1819
- if (id === "@backtest-kit/ollama")
1820
- return globalThis.BacktestKitOllama;
1821
- if (id === "@backtest-kit/pinets")
1822
- return globalThis.BacktestKitPinets;
1823
- if (id === "@backtest-kit/signals")
1824
- return globalThis.BacktestKitSignals;
1825
- return baseRequire(id);
1826
- },
1827
- });
1828
- });
1829
- const __filename = fileURLToPath(import.meta.url);
1830
- const __dirname = path.dirname(__filename);
1831
- const BacktestKitCli = new Proxy({}, {
1832
- get(_target, prop) {
1833
- throw new Error(`@backtest-kit/cli is not available in this context (accessed: ${String(prop)})`);
1834
- },
1835
- });
1836
1745
  class BabelService {
1837
1746
  constructor() {
1838
1747
  this.loggerService = inject(TYPES.loggerService);
@@ -1868,22 +1777,115 @@ class BabelService {
1868
1777
  }
1869
1778
  return result.code;
1870
1779
  };
1871
- this.transpileAndRun = (code) => {
1872
- this.loggerService.log("babelService transpileAndRun", {
1873
- codeLen: code.length,
1874
- });
1875
- const require = getBaseRequire();
1876
- const module = { exports: {} };
1877
- const exports = module.exports;
1878
- eval(this.transpile(code));
1879
- return {
1880
- require,
1881
- __filename,
1882
- __dirname,
1883
- exports,
1884
- module,
1885
- };
1886
- };
1780
+ }
1781
+ }
1782
+
1783
+ const TRANSPILE_FN = (code, self) => {
1784
+ const require = self.getBaseRequire();
1785
+ const __filename = self.__filename;
1786
+ const __dirname = self.__dirname;
1787
+ const module = { exports: {} };
1788
+ const exports = module.exports;
1789
+ eval(self.params.babel.transpile(code));
1790
+ return {
1791
+ require,
1792
+ __filename,
1793
+ __dirname,
1794
+ exports,
1795
+ module,
1796
+ };
1797
+ };
1798
+ const REQUIRE_ENTRY_FACTORY = (filePath, self) => {
1799
+ {
1800
+ return null;
1801
+ }
1802
+ };
1803
+ const BABEL_ENTRY_FACTORY = (filePath, self) => {
1804
+ try {
1805
+ const resolvedPath = path.resolve(self.__dirname, filePath);
1806
+ const code = fs.readFileSync(resolvedPath, "utf-8");
1807
+ const child = self.fork(path.dirname(resolvedPath));
1808
+ const { module } = TRANSPILE_FN(code, child);
1809
+ return "default" in module.exports
1810
+ ? module.exports.default
1811
+ : module.exports;
1812
+ }
1813
+ catch {
1814
+ return null;
1815
+ }
1816
+ };
1817
+ const ENTRY_FACTORY = (filePath, self) => {
1818
+ let result = null;
1819
+ if (result = REQUIRE_ENTRY_FACTORY()) {
1820
+ return result;
1821
+ }
1822
+ if (result = BABEL_ENTRY_FACTORY(filePath, self)) {
1823
+ return result;
1824
+ }
1825
+ throw new Error(`Failed to load module at ${filePath} (basepath: ${self.params.path})`);
1826
+ };
1827
+ const CREATE_BASE_REQUIRE_FN = (self) => {
1828
+ const baseRequire = createRequire(self.__filename);
1829
+ return new Proxy(baseRequire, {
1830
+ apply(_target, _this, args) {
1831
+ const id = args[0];
1832
+ if (id === "backtest-kit")
1833
+ return globalThis.BacktestKit;
1834
+ if (id === "@backtest-kit/cli")
1835
+ return globalThis.BacktestKitCli;
1836
+ if (id === "@backtest-kit/ui")
1837
+ return globalThis.BacktestKitUi;
1838
+ if (id === "@backtest-kit/graph")
1839
+ return globalThis.BacktestKitGraph;
1840
+ if (id === "@backtest-kit/ollama")
1841
+ return globalThis.BacktestKitOllama;
1842
+ if (id === "@backtest-kit/pinets")
1843
+ return globalThis.BacktestKitPinets;
1844
+ if (id === "@backtest-kit/signals")
1845
+ return globalThis.BacktestKitSignals;
1846
+ if (id.startsWith("./") || id.startsWith("../")) {
1847
+ const resolved = path.resolve(self.__dirname, id);
1848
+ const child = self.fork(path.dirname(resolved));
1849
+ return child.import(resolved);
1850
+ }
1851
+ return baseRequire(id);
1852
+ },
1853
+ });
1854
+ };
1855
+ const BacktestKitCli = new Proxy({}, {
1856
+ get(_target, prop) {
1857
+ throw new Error(`@backtest-kit/cli is not available in this context (accessed: ${String(prop)})`);
1858
+ },
1859
+ });
1860
+ class ClientLoader {
1861
+ constructor(params) {
1862
+ this.params = params;
1863
+ this.getBaseRequire = singleshot(() => {
1864
+ this.params.logger.log("ClientLoader getBaseRequire", {
1865
+ basePath: this.params.path,
1866
+ });
1867
+ return CREATE_BASE_REQUIRE_FN(this);
1868
+ });
1869
+ this.__filename = path.join(params.path, "index.cjs");
1870
+ this.__dirname = path.dirname(this.__filename);
1871
+ }
1872
+ fork(basePath) {
1873
+ this.params.logger.log("ClientLoader fork", {
1874
+ basePath: this.params.path,
1875
+ path: basePath,
1876
+ });
1877
+ return new ClientLoader({
1878
+ path: basePath,
1879
+ babel: this.params.babel,
1880
+ logger: this.params.logger,
1881
+ });
1882
+ }
1883
+ import(filePath) {
1884
+ this.params.logger.log("ClientLoader import", {
1885
+ filePath,
1886
+ basePath: this.params.path,
1887
+ });
1888
+ return ENTRY_FACTORY(filePath, this);
1887
1889
  }
1888
1890
  }
1889
1891
  globalThis.BacktestKit = BacktestKit;
@@ -1894,6 +1896,25 @@ globalThis.BacktestKitOllama = BacktestKitOllama;
1894
1896
  globalThis.BacktestKitPinets = BacktestKitPinets;
1895
1897
  globalThis.BacktestKitSignals = BacktestKitSignals;
1896
1898
 
1899
+ class LoaderService {
1900
+ constructor() {
1901
+ this.babelService = inject(TYPES.babelService);
1902
+ this.loggerService = inject(TYPES.loggerService);
1903
+ this.getInstance = memoize(([basePath]) => `${basePath}`, (basePath) => new ClientLoader({
1904
+ babel: this.babelService,
1905
+ logger: this.loggerService,
1906
+ path: basePath,
1907
+ }));
1908
+ this.import = async (filePath, basePath = process.cwd()) => {
1909
+ this.loggerService.log("loaderService import", {
1910
+ filePath,
1911
+ });
1912
+ const instance = this.getInstance(basePath);
1913
+ return instance.import(filePath);
1914
+ };
1915
+ }
1916
+ }
1917
+
1897
1918
  {
1898
1919
  provide(TYPES.quickchartApiService, () => new QuickchartApiService());
1899
1920
  provide(TYPES.telegramApiService, () => new TelegramApiService());
@@ -1902,6 +1923,7 @@ globalThis.BacktestKitSignals = BacktestKitSignals;
1902
1923
  provide(TYPES.errorService, () => new ErrorService());
1903
1924
  provide(TYPES.loggerService, () => new LoggerService());
1904
1925
  provide(TYPES.resolveService, () => new ResolveService());
1926
+ provide(TYPES.loaderService, () => new LoaderService());
1905
1927
  provide(TYPES.babelService, () => new BabelService());
1906
1928
  }
1907
1929
  {
@@ -1940,6 +1962,7 @@ const baseServices = {
1940
1962
  errorService: inject(TYPES.errorService),
1941
1963
  loggerService: inject(TYPES.loggerService),
1942
1964
  resolveService: inject(TYPES.resolveService),
1965
+ loaderService: inject(TYPES.loaderService),
1943
1966
  babelService: inject(TYPES.babelService),
1944
1967
  };
1945
1968
  const connectionServices = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backtest-kit/cli",
3
- "version": "5.4.0",
3
+ "version": "5.5.0",
4
4
  "description": "Zero-boilerplate CLI runner for backtest-kit strategies. Run backtests, paper trading, and live bots with candle cache warming, web dashboard, and Telegram notifications — no setup code required.",
5
5
  "author": {
6
6
  "name": "Petr Tripolsky",
@@ -60,11 +60,11 @@
60
60
  "devDependencies": {
61
61
  "@babel/plugin-transform-modules-umd": "7.27.1",
62
62
  "@babel/standalone": "7.29.1",
63
- "@backtest-kit/ui": "5.4.0",
64
- "@backtest-kit/graph": "5.4.0",
65
- "@backtest-kit/ollama": "5.4.0",
66
- "@backtest-kit/pinets": "5.4.0",
67
- "@backtest-kit/signals": "5.4.0",
63
+ "@backtest-kit/ui": "5.5.0",
64
+ "@backtest-kit/graph": "5.5.0",
65
+ "@backtest-kit/ollama": "5.5.0",
66
+ "@backtest-kit/pinets": "5.5.0",
67
+ "@backtest-kit/signals": "5.5.0",
68
68
  "@rollup/plugin-replace": "6.0.3",
69
69
  "@rollup/plugin-typescript": "11.1.6",
70
70
  "@types/image-size": "0.7.0",
@@ -72,7 +72,7 @@
72
72
  "@types/mustache": "4.2.6",
73
73
  "@types/node": "22.9.0",
74
74
  "@types/stack-trace": "0.0.33",
75
- "backtest-kit": "5.4.0",
75
+ "backtest-kit": "5.5.0",
76
76
  "glob": "11.0.1",
77
77
  "markdown-it": "14.1.1",
78
78
  "rimraf": "6.0.1",
@@ -87,12 +87,12 @@
87
87
  "peerDependencies": {
88
88
  "@babel/plugin-transform-modules-umd": "^7.27.1",
89
89
  "@babel/standalone": "^7.29.1",
90
- "@backtest-kit/ui": "^5.4.0",
91
- "@backtest-kit/graph": "^5.4.0",
92
- "@backtest-kit/ollama": "^5.4.0",
93
- "@backtest-kit/pinets": "^5.4.0",
94
- "@backtest-kit/signals": "^5.4.0",
95
- "backtest-kit": "^5.4.0",
90
+ "@backtest-kit/ui": "^5.5.0",
91
+ "@backtest-kit/graph": "^5.5.0",
92
+ "@backtest-kit/ollama": "^5.5.0",
93
+ "@backtest-kit/pinets": "^5.5.0",
94
+ "@backtest-kit/signals": "^5.5.0",
95
+ "backtest-kit": "^5.5.0",
96
96
  "markdown-it": "^14.1.1",
97
97
  "typescript": "^5.0.0"
98
98
  },
package/types.d.ts CHANGED
@@ -1,11 +1,5 @@
1
1
  import * as functools_kit from 'functools-kit';
2
- import * as BacktestKit from 'backtest-kit';
3
2
  import { CandleInterval, TrailingTakeCommit, TrailingStopCommit, BreakevenCommit, PartialProfitCommit, PartialLossCommit, IStrategyTickResultScheduled, IStrategyTickResultCancelled, IStrategyTickResultOpened, IStrategyTickResultClosed, RiskContract, AverageBuyCommit, SignalOpenContract, SignalCloseContract, CancelScheduledCommit, ClosePendingCommit } from 'backtest-kit';
4
- import * as BacktestKitUi from '@backtest-kit/ui';
5
- import * as BacktestKitGraph from '@backtest-kit/graph';
6
- import * as BacktestKitOllama from '@backtest-kit/ollama';
7
- import * as BacktestKitPinets from '@backtest-kit/pinets';
8
- import * as BacktestKitSignals from '@backtest-kit/signals';
9
3
  import { Input } from 'telegraf';
10
4
 
11
5
  interface ILogger {
@@ -93,35 +87,16 @@ declare class FrameSchemaService {
93
87
  addSchema: (() => Promise<void>) & functools_kit.ISingleshotClearable;
94
88
  }
95
89
 
96
- declare const BacktestKitCli: {};
97
- declare global {
98
- interface Window {
99
- BacktestKit: typeof BacktestKit;
100
- BacktestKitCli: typeof BacktestKitCli;
101
- BacktestKitUi: typeof BacktestKitUi;
102
- BacktestKitGraph: typeof BacktestKitGraph;
103
- BacktestKitOllama: typeof BacktestKitOllama;
104
- BacktestKitPinets: typeof BacktestKitPinets;
105
- BacktestKitSignals: typeof BacktestKitSignals;
106
- }
107
- }
108
- declare class BabelService {
109
- readonly loggerService: LoggerService;
110
- transpile: (code: string) => any;
111
- transpileAndRun: (code: string) => {
112
- require: NodeRequire;
113
- __filename: string;
114
- __dirname: string;
115
- exports: Record<string, unknown>;
116
- module: {
117
- exports: Record<string, unknown>;
118
- };
119
- };
90
+ declare class LoaderService {
91
+ private readonly babelService;
92
+ private readonly loggerService;
93
+ private getInstance;
94
+ import: (filePath: string, basePath?: string) => Promise<any>;
120
95
  }
121
96
 
122
97
  declare class ResolveService {
123
98
  readonly loggerService: LoggerService;
124
- readonly babelService: BabelService;
99
+ readonly loaderService: LoaderService;
125
100
  readonly DEFAULT_TEMPLATE_DIR: string;
126
101
  readonly OVERRIDE_TEMPLATE_DIR: string;
127
102
  readonly OVERRIDE_MODULES_DIR: string;
@@ -229,10 +204,19 @@ declare class TelegramTemplateService {
229
204
  declare class ModuleConnectionService {
230
205
  readonly loggerService: LoggerService;
231
206
  readonly resolveService: ResolveService;
232
- readonly babelService: BabelService;
207
+ readonly loaderService: LoaderService;
233
208
  loadModule: (fileName: string) => Promise<boolean>;
234
209
  }
235
210
 
211
+ interface IBabel {
212
+ transpile(code: string): string;
213
+ }
214
+
215
+ declare class BabelService implements IBabel {
216
+ readonly loggerService: LoggerService;
217
+ transpile: (code: string) => any;
218
+ }
219
+
236
220
  declare const cli: {
237
221
  telegramTemplateService: TelegramTemplateService;
238
222
  telegramWebService: TelegramWebService;
@@ -250,11 +234,16 @@ declare const cli: {
250
234
  errorService: ErrorService;
251
235
  loggerService: LoggerService;
252
236
  resolveService: ResolveService;
237
+ loaderService: LoaderService;
253
238
  babelService: BabelService;
254
239
  telegramApiService: TelegramApiService;
255
240
  quickchartApiService: QuickchartApiService;
256
241
  };
257
242
 
243
+ interface ILoader {
244
+ import(filePath: string): any;
245
+ }
246
+
258
247
  declare enum ExchangeName {
259
248
  DefaultExchange = "default_exchange"
260
249
  }
@@ -272,4 +261,4 @@ type Mode = "backtest" | "live" | "paper";
272
261
  type Args = Partial<PayloadBacktest> | Partial<PayloadPaper> | Partial<PayloadLive>;
273
262
  declare function run(mode: Mode, args: Args): Promise<void>;
274
263
 
275
- export { ExchangeName, FrameName, type ILogger, cli, run, setLogger };
264
+ export { ExchangeName, FrameName, type IBabel, type ILoader, type ILogger, cli, run, setLogger };