@backtest-kit/cli 3.3.2 → 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/README.md +55 -0
- package/build/index.cjs +144 -58
- package/build/index.mjs +101 -15
- package/package.json +9 -4
- package/types.d.ts +18 -1
package/README.md
CHANGED
|
@@ -340,6 +340,10 @@ export default class {
|
|
|
340
340
|
onBreakeven(event) {
|
|
341
341
|
console.log('Breakeven triggered', event.symbol);
|
|
342
342
|
}
|
|
343
|
+
|
|
344
|
+
onAverageBuy(event) {
|
|
345
|
+
console.log('Cost averaging (DCA)', event.symbol);
|
|
346
|
+
}
|
|
343
347
|
}
|
|
344
348
|
```
|
|
345
349
|
|
|
@@ -356,6 +360,57 @@ export default class MyModule implements ILiveModule {
|
|
|
356
360
|
}
|
|
357
361
|
```
|
|
358
362
|
|
|
363
|
+
## 📦 Supported Entry Point Formats
|
|
364
|
+
|
|
365
|
+
`@backtest-kit/cli` automatically detects the format of your strategy file and loads it with the appropriate runtime — no flags or configuration required.
|
|
366
|
+
|
|
367
|
+
| Format | Extension | Runtime | Use Case |
|
|
368
|
+
|--------|-----------|---------|----------|
|
|
369
|
+
| **TypeScript** | `.ts` | [`tsx`](https://tsx.is/) via `tsImport()` | TypeScript strategies with cross-imports (ESM ↔ CJS) |
|
|
370
|
+
| **ES Module** | `.mjs` | Native `import()` | Modern JavaScript with top-level `await` and ESM syntax |
|
|
371
|
+
| **CommonJS** | `.cjs` | Native `require()` | Legacy or dual-package strategies |
|
|
372
|
+
|
|
373
|
+
### TypeScript (`.ts`)
|
|
374
|
+
|
|
375
|
+
Run TypeScript strategy files directly — no `tsc` compilation step needed. Powered by `tsx`, which handles cross-format imports transparently:
|
|
376
|
+
|
|
377
|
+
```json
|
|
378
|
+
{
|
|
379
|
+
"scripts": {
|
|
380
|
+
"backtest": "npx @backtest-kit/cli --backtest ./src/index.ts"
|
|
381
|
+
},
|
|
382
|
+
"dependencies": {
|
|
383
|
+
"@backtest-kit/cli": "latest",
|
|
384
|
+
"backtest-kit": "latest",
|
|
385
|
+
"tsx": "latest"
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### ES Module (`.mjs`)
|
|
391
|
+
|
|
392
|
+
Standard ESM format. Supports top-level `await`, named exports, and `import` syntax:
|
|
393
|
+
|
|
394
|
+
```json
|
|
395
|
+
{
|
|
396
|
+
"scripts": {
|
|
397
|
+
"backtest": "npx @backtest-kit/cli --backtest ./src/index.mjs"
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### CommonJS (`.cjs`)
|
|
403
|
+
|
|
404
|
+
For projects that compile to or use CommonJS. Loaded via `require()`:
|
|
405
|
+
|
|
406
|
+
```json
|
|
407
|
+
{
|
|
408
|
+
"scripts": {
|
|
409
|
+
"backtest": "npx @backtest-kit/cli --backtest ./dist/index.cjs"
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
359
414
|
## 🌍 Environment Variables
|
|
360
415
|
|
|
361
416
|
Create a `.env` file in your project root:
|
package/build/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
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
|
-
|
|
79
|
-
|
|
81
|
+
BacktestKit.Storage.enable();
|
|
82
|
+
BacktestKit.Notification.enable();
|
|
80
83
|
}
|
|
81
84
|
{
|
|
82
|
-
|
|
83
|
-
|
|
85
|
+
BacktestKit.Markdown.disable();
|
|
86
|
+
BacktestKit.Report.enable();
|
|
84
87
|
}
|
|
85
88
|
{
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
BacktestKit.StorageLive.usePersist();
|
|
90
|
+
BacktestKit.StorageBacktest.useMemory();
|
|
88
91
|
}
|
|
89
92
|
{
|
|
90
|
-
|
|
91
|
-
|
|
93
|
+
BacktestKit.NotificationLive.usePersist();
|
|
94
|
+
BacktestKit.NotificationBacktest.useMemory();
|
|
92
95
|
}
|
|
93
|
-
|
|
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$
|
|
238
|
-
const __dirname$1 = path.dirname(__filename$
|
|
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,17 +251,37 @@ const REQUIRE_ENTRY_FACTORY = (filePath) => {
|
|
|
247
251
|
}
|
|
248
252
|
};
|
|
249
253
|
const IMPORT_ENTRY_FACTORY = async (filePath) => {
|
|
250
|
-
|
|
254
|
+
{
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
};
|
|
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
|
+
}
|
|
251
267
|
};
|
|
252
|
-
const LOAD_ENTRY_FN = async (filePath) => {
|
|
253
|
-
if (
|
|
254
|
-
|
|
268
|
+
const LOAD_ENTRY_FN = async (filePath, self) => {
|
|
269
|
+
if (REQUIRE_ENTRY_FACTORY(filePath)) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (await IMPORT_ENTRY_FACTORY()) {
|
|
273
|
+
return;
|
|
255
274
|
}
|
|
275
|
+
if (await BABEL_ENTRY_FACTORY(filePath, self)) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
throw new Error(`Failed to load entry point: ${filePath}`);
|
|
256
279
|
};
|
|
257
280
|
let _is_launched = false;
|
|
258
281
|
class ResolveService {
|
|
259
282
|
constructor() {
|
|
260
283
|
this.loggerService = inject(TYPES.loggerService);
|
|
284
|
+
this.babelService = inject(TYPES.babelService);
|
|
261
285
|
this.DEFAULT_TEMPLATE_DIR = path.resolve(__dirname$1, '..', 'template');
|
|
262
286
|
this.OVERRIDE_TEMPLATE_DIR = path.resolve(process.cwd(), 'template');
|
|
263
287
|
this.OVERRIDE_MODULES_DIR = path.resolve(process.cwd(), 'modules');
|
|
@@ -274,7 +298,7 @@ class ResolveService {
|
|
|
274
298
|
process.chdir(moduleRoot);
|
|
275
299
|
dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
|
|
276
300
|
dotenv.config({ path: path.join(moduleRoot, '.env'), override: true, quiet: true });
|
|
277
|
-
await LOAD_ENTRY_FN(absolutePath);
|
|
301
|
+
await LOAD_ENTRY_FN(absolutePath, this);
|
|
278
302
|
await entrySubject.next(absolutePath);
|
|
279
303
|
}
|
|
280
304
|
_is_launched = true;
|
|
@@ -304,7 +328,7 @@ var ExchangeName$1 = ExchangeName;
|
|
|
304
328
|
const ADD_EXCHANGE_FN = (self) => {
|
|
305
329
|
self.loggerService.log("Adding CCXT Binance as a default exchange schema");
|
|
306
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.");
|
|
307
|
-
|
|
331
|
+
BacktestKit.addExchangeSchema({
|
|
308
332
|
exchangeName: ExchangeName$1.DefaultExchange,
|
|
309
333
|
getCandles: async (symbol, interval, since, limit) => {
|
|
310
334
|
const exchange = await getExchange();
|
|
@@ -323,7 +347,7 @@ const ADD_EXCHANGE_FN = (self) => {
|
|
|
323
347
|
const market = exchange.market(symbol);
|
|
324
348
|
const tickSize = market.limits?.price?.min || market.precision?.price;
|
|
325
349
|
if (tickSize !== undefined) {
|
|
326
|
-
return
|
|
350
|
+
return BacktestKit.roundTicks(price, tickSize);
|
|
327
351
|
}
|
|
328
352
|
return exchange.priceToPrecision(symbol, price);
|
|
329
353
|
},
|
|
@@ -332,7 +356,7 @@ const ADD_EXCHANGE_FN = (self) => {
|
|
|
332
356
|
const market = exchange.market(symbol);
|
|
333
357
|
const stepSize = market.limits?.amount?.min || market.precision?.amount;
|
|
334
358
|
if (stepSize !== undefined) {
|
|
335
|
-
return
|
|
359
|
+
return BacktestKit.roundTicks(quantity, stepSize);
|
|
336
360
|
}
|
|
337
361
|
return exchange.amountToPrecision(symbol, quantity);
|
|
338
362
|
},
|
|
@@ -361,7 +385,7 @@ class ExchangeSchemaService {
|
|
|
361
385
|
this.loggerService = inject(TYPES.loggerService);
|
|
362
386
|
this.addSchema = functoolsKit.singleshot(async () => {
|
|
363
387
|
this.loggerService.log("exchangeSchemaService addSchema");
|
|
364
|
-
const { length } = await
|
|
388
|
+
const { length } = await BacktestKit.listExchangeSchema();
|
|
365
389
|
!length && ADD_EXCHANGE_FN(this);
|
|
366
390
|
});
|
|
367
391
|
}
|
|
@@ -438,7 +462,7 @@ const getArgs = functoolsKit.singleshot(() => {
|
|
|
438
462
|
const ADD_FRAME_FN = (self) => {
|
|
439
463
|
self.loggerService.log("Adding February 2024 as a default frame schema");
|
|
440
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.");
|
|
441
|
-
|
|
465
|
+
BacktestKit.addFrameSchema({
|
|
442
466
|
frameName: FrameName$1.DefaultFrame,
|
|
443
467
|
interval: "1m",
|
|
444
468
|
startDate: new Date("2024-02-01T00:00:00Z"),
|
|
@@ -453,7 +477,7 @@ class FrameSchemaService {
|
|
|
453
477
|
if (!getArgs().values.backtest) {
|
|
454
478
|
return;
|
|
455
479
|
}
|
|
456
|
-
const { length } = await
|
|
480
|
+
const { length } = await BacktestKit.listFrameSchema();
|
|
457
481
|
!length && ADD_FRAME_FN(this);
|
|
458
482
|
});
|
|
459
483
|
}
|
|
@@ -473,11 +497,11 @@ class SymbolSchemaService {
|
|
|
473
497
|
|
|
474
498
|
const notifyFinish = functoolsKit.singleshot(() => {
|
|
475
499
|
let disposeRef;
|
|
476
|
-
const unLive =
|
|
500
|
+
const unLive = BacktestKit.listenDoneLive(() => {
|
|
477
501
|
console.log("Live trading finished");
|
|
478
502
|
disposeRef && disposeRef();
|
|
479
503
|
});
|
|
480
|
-
const unBacktest =
|
|
504
|
+
const unBacktest = BacktestKit.listenDoneBacktest(() => {
|
|
481
505
|
console.log("Backtest trading finished");
|
|
482
506
|
disposeRef && disposeRef();
|
|
483
507
|
});
|
|
@@ -491,7 +515,7 @@ const getEntry = (metaUrl) => {
|
|
|
491
515
|
|
|
492
516
|
const notifyVerbose = functoolsKit.singleshot(() => {
|
|
493
517
|
console.log("Using verbose logging...");
|
|
494
|
-
|
|
518
|
+
BacktestKit.listenSignal((event) => {
|
|
495
519
|
if (event.action === "scheduled") {
|
|
496
520
|
console.log(`[POSITION SCHEDULED] ${event.symbol}`);
|
|
497
521
|
console.log(` Strategy: ${event.strategyName}`);
|
|
@@ -571,9 +595,9 @@ class BacktestMainService {
|
|
|
571
595
|
this.frameSchemaService.addSchema();
|
|
572
596
|
}
|
|
573
597
|
const symbol = payload.symbol || "BTCUSDT";
|
|
574
|
-
const [defaultStrategyName = null] = await
|
|
575
|
-
const [defaultExchangeName = null] = await
|
|
576
|
-
const [defaultFrameName = null] = await
|
|
598
|
+
const [defaultStrategyName = null] = await BacktestKit.listStrategySchema();
|
|
599
|
+
const [defaultExchangeName = null] = await BacktestKit.listExchangeSchema();
|
|
600
|
+
const [defaultFrameName = null] = await BacktestKit.listFrameSchema();
|
|
577
601
|
const strategyName = payload.strategy || defaultStrategyName?.strategyName;
|
|
578
602
|
if (!strategyName) {
|
|
579
603
|
throw new Error("Strategy name is required");
|
|
@@ -594,7 +618,7 @@ class BacktestMainService {
|
|
|
594
618
|
});
|
|
595
619
|
}
|
|
596
620
|
if (payload.verbose) {
|
|
597
|
-
|
|
621
|
+
BacktestKit.overrideExchangeSchema({
|
|
598
622
|
exchangeName,
|
|
599
623
|
callbacks: {
|
|
600
624
|
onCandleData(symbol, interval, since) {
|
|
@@ -604,7 +628,7 @@ class BacktestMainService {
|
|
|
604
628
|
});
|
|
605
629
|
notifyVerbose();
|
|
606
630
|
}
|
|
607
|
-
|
|
631
|
+
BacktestKit.Backtest.background(symbol, {
|
|
608
632
|
strategyName,
|
|
609
633
|
frameName,
|
|
610
634
|
exchangeName,
|
|
@@ -663,8 +687,8 @@ class LiveMainService {
|
|
|
663
687
|
this.symbolSchemaService.addSchema();
|
|
664
688
|
}
|
|
665
689
|
const symbol = payload.symbol || "BTCUSDT";
|
|
666
|
-
const [defaultStrategyName = null] = await
|
|
667
|
-
const [defaultExchangeName = null] = await
|
|
690
|
+
const [defaultStrategyName = null] = await BacktestKit.listStrategySchema();
|
|
691
|
+
const [defaultExchangeName = null] = await BacktestKit.listExchangeSchema();
|
|
668
692
|
const strategyName = payload.strategy || defaultStrategyName?.strategyName;
|
|
669
693
|
if (!strategyName) {
|
|
670
694
|
throw new Error("Strategy name is required");
|
|
@@ -674,7 +698,7 @@ class LiveMainService {
|
|
|
674
698
|
throw new Error("Exchange name is required");
|
|
675
699
|
}
|
|
676
700
|
if (payload.verbose) {
|
|
677
|
-
|
|
701
|
+
BacktestKit.overrideExchangeSchema({
|
|
678
702
|
exchangeName,
|
|
679
703
|
callbacks: {
|
|
680
704
|
onCandleData(symbol, interval, since) {
|
|
@@ -684,7 +708,7 @@ class LiveMainService {
|
|
|
684
708
|
});
|
|
685
709
|
notifyVerbose();
|
|
686
710
|
}
|
|
687
|
-
|
|
711
|
+
BacktestKit.Live.background(symbol, {
|
|
688
712
|
strategyName,
|
|
689
713
|
exchangeName,
|
|
690
714
|
});
|
|
@@ -734,8 +758,8 @@ class PaperMainService {
|
|
|
734
758
|
this.symbolSchemaService.addSchema();
|
|
735
759
|
}
|
|
736
760
|
const symbol = payload.symbol || "BTCUSDT";
|
|
737
|
-
const [defaultStrategyName = null] = await
|
|
738
|
-
const [defaultExchangeName = null] = await
|
|
761
|
+
const [defaultStrategyName = null] = await BacktestKit.listStrategySchema();
|
|
762
|
+
const [defaultExchangeName = null] = await BacktestKit.listExchangeSchema();
|
|
739
763
|
const strategyName = payload.strategy || defaultStrategyName?.strategyName;
|
|
740
764
|
if (!strategyName) {
|
|
741
765
|
throw new Error("Strategy name is required");
|
|
@@ -745,7 +769,7 @@ class PaperMainService {
|
|
|
745
769
|
throw new Error("Exchange name is required");
|
|
746
770
|
}
|
|
747
771
|
if (payload.verbose) {
|
|
748
|
-
|
|
772
|
+
BacktestKit.overrideExchangeSchema({
|
|
749
773
|
exchangeName,
|
|
750
774
|
callbacks: {
|
|
751
775
|
onCandleData(symbol, interval, since) {
|
|
@@ -755,7 +779,7 @@ class PaperMainService {
|
|
|
755
779
|
});
|
|
756
780
|
notifyVerbose();
|
|
757
781
|
}
|
|
758
|
-
|
|
782
|
+
BacktestKit.Live.background(symbol, {
|
|
759
783
|
strategyName,
|
|
760
784
|
exchangeName,
|
|
761
785
|
});
|
|
@@ -865,7 +889,7 @@ class TelegramProviderService {
|
|
|
865
889
|
|
|
866
890
|
const CANDLES_LIMIT = 160;
|
|
867
891
|
const GET_CONFIG_FN = async (symbol, interval) => {
|
|
868
|
-
const candles = await
|
|
892
|
+
const candles = await BacktestKit.getCandles(symbol, interval, CANDLES_LIMIT);
|
|
869
893
|
const labels = candles.map(({ timestamp }) => new Date(timestamp).toLocaleTimeString("en-US", {
|
|
870
894
|
hour: "2-digit",
|
|
871
895
|
minute: "2-digit",
|
|
@@ -1281,7 +1305,7 @@ class TelegramWebService {
|
|
|
1281
1305
|
}
|
|
1282
1306
|
|
|
1283
1307
|
const GET_TIMEFRAME_RANGE_FN = async (frameName) => {
|
|
1284
|
-
const frameList = await
|
|
1308
|
+
const frameList = await BacktestKit.listFrameSchema();
|
|
1285
1309
|
const frameSchema = frameList.find((frameSchema) => frameSchema.frameName === frameName);
|
|
1286
1310
|
if (!frameSchema) {
|
|
1287
1311
|
throw new Error(`Frame with name ${frameName} not found`);
|
|
@@ -1292,7 +1316,7 @@ const GET_TIMEFRAME_RANGE_FN = async (frameName) => {
|
|
|
1292
1316
|
const CACHE_CANDLES_FN = functoolsKit.retry(async (interval, dto) => {
|
|
1293
1317
|
try {
|
|
1294
1318
|
console.log(`Checking candles cache for ${dto.symbol} ${interval} from ${dto.from} to ${dto.to}`);
|
|
1295
|
-
await
|
|
1319
|
+
await BacktestKit.checkCandles({
|
|
1296
1320
|
exchangeName: dto.exchangeName,
|
|
1297
1321
|
from: dto.from,
|
|
1298
1322
|
to: dto.to,
|
|
@@ -1302,7 +1326,7 @@ const CACHE_CANDLES_FN = functoolsKit.retry(async (interval, dto) => {
|
|
|
1302
1326
|
}
|
|
1303
1327
|
catch (error) {
|
|
1304
1328
|
console.log(`Caching candles for ${dto.symbol} ${interval} from ${dto.from} to ${dto.to}`);
|
|
1305
|
-
await
|
|
1329
|
+
await BacktestKit.warmCandles({
|
|
1306
1330
|
symbol: dto.symbol,
|
|
1307
1331
|
exchangeName: dto.exchangeName,
|
|
1308
1332
|
from: dto.from,
|
|
@@ -1459,10 +1483,10 @@ class TelegramLogicService {
|
|
|
1459
1483
|
};
|
|
1460
1484
|
this.connect = functoolsKit.singleshot(() => {
|
|
1461
1485
|
this.loggerService.log("telegramLogicService connect");
|
|
1462
|
-
const unRisk =
|
|
1486
|
+
const unRisk = BacktestKit.listenRisk(async (event) => {
|
|
1463
1487
|
await this.notifyRisk(event);
|
|
1464
1488
|
});
|
|
1465
|
-
const unSignal =
|
|
1489
|
+
const unSignal = BacktestKit.listenSignal(async (event) => {
|
|
1466
1490
|
if (event.action === "scheduled") {
|
|
1467
1491
|
await this.notifyScheduled(event);
|
|
1468
1492
|
return;
|
|
@@ -1480,7 +1504,7 @@ class TelegramLogicService {
|
|
|
1480
1504
|
return;
|
|
1481
1505
|
}
|
|
1482
1506
|
});
|
|
1483
|
-
const unCommit =
|
|
1507
|
+
const unCommit = BacktestKit.listenStrategyCommit(async (event) => {
|
|
1484
1508
|
if (event.action === "trailing-take") {
|
|
1485
1509
|
await this.notifyTrailingTake(event);
|
|
1486
1510
|
return;
|
|
@@ -1623,9 +1647,16 @@ const REQUIRE_MODULE_FACTORY = (fileName) => {
|
|
|
1623
1647
|
return null;
|
|
1624
1648
|
};
|
|
1625
1649
|
const IMPORT_MODULE_FACTORY = async (fileName) => {
|
|
1650
|
+
{
|
|
1651
|
+
return null;
|
|
1652
|
+
}
|
|
1653
|
+
};
|
|
1654
|
+
const BABEL_MODULE_FACTORY = async (fileName, self) => {
|
|
1626
1655
|
for (const variant of getExtVariants(fileName)) {
|
|
1627
1656
|
try {
|
|
1628
|
-
|
|
1657
|
+
const code = await fs$1.readFile(variant, "utf-8");
|
|
1658
|
+
const exports = self.babelService.transpileAndRun(code);
|
|
1659
|
+
return exports.default ?? exports;
|
|
1629
1660
|
}
|
|
1630
1661
|
catch {
|
|
1631
1662
|
continue;
|
|
@@ -1645,7 +1676,10 @@ const LOAD_MODULE_MODULE_FN = async (fileName, self) => {
|
|
|
1645
1676
|
if ((Ctor = REQUIRE_MODULE_FACTORY(resolvedFile))) {
|
|
1646
1677
|
return typeof Ctor === "function" ? new Ctor() : Ctor;
|
|
1647
1678
|
}
|
|
1648
|
-
if ((Ctor = await IMPORT_MODULE_FACTORY(
|
|
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))) {
|
|
1649
1683
|
return typeof Ctor === "function" ? new Ctor() : Ctor;
|
|
1650
1684
|
}
|
|
1651
1685
|
throw new Error(`Module module import failed for file: ${resolvedFile}`);
|
|
@@ -1654,6 +1688,7 @@ class ModuleConnectionService {
|
|
|
1654
1688
|
constructor() {
|
|
1655
1689
|
this.loggerService = inject(TYPES.loggerService);
|
|
1656
1690
|
this.resolveService = inject(TYPES.resolveService);
|
|
1691
|
+
this.babelService = inject(TYPES.babelService);
|
|
1657
1692
|
this.getInstance = functoolsKit.memoize(([fileName]) => `${fileName}`, async (fileName) => {
|
|
1658
1693
|
this.loggerService.log("moduleConnectionService getInstance", {
|
|
1659
1694
|
fileName,
|
|
@@ -1780,10 +1815,10 @@ class LiveProviderService {
|
|
|
1780
1815
|
console.log("No ./modules/live.module.mjs found, live trading failed to initialize");
|
|
1781
1816
|
process.exit(-1);
|
|
1782
1817
|
});
|
|
1783
|
-
const unRisk =
|
|
1818
|
+
const unRisk = BacktestKit.listenRisk(async (event) => {
|
|
1784
1819
|
await this.handleRisk(event);
|
|
1785
1820
|
});
|
|
1786
|
-
const unSignal =
|
|
1821
|
+
const unSignal = BacktestKit.listenSignal(async (event) => {
|
|
1787
1822
|
if (event.action === "scheduled") {
|
|
1788
1823
|
await this.handleScheduled(event);
|
|
1789
1824
|
return;
|
|
@@ -1801,7 +1836,7 @@ class LiveProviderService {
|
|
|
1801
1836
|
return;
|
|
1802
1837
|
}
|
|
1803
1838
|
});
|
|
1804
|
-
const unCommit =
|
|
1839
|
+
const unCommit = BacktestKit.listenStrategyCommit(async (event) => {
|
|
1805
1840
|
if (event.action === "trailing-take") {
|
|
1806
1841
|
await this.handleTrailingTake(event);
|
|
1807
1842
|
return;
|
|
@@ -1847,6 +1882,55 @@ class LiveProviderService {
|
|
|
1847
1882
|
}
|
|
1848
1883
|
}
|
|
1849
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
|
+
|
|
1850
1934
|
{
|
|
1851
1935
|
provide(TYPES.quickchartApiService, () => new QuickchartApiService());
|
|
1852
1936
|
provide(TYPES.telegramApiService, () => new TelegramApiService());
|
|
@@ -1855,6 +1939,7 @@ class LiveProviderService {
|
|
|
1855
1939
|
provide(TYPES.errorService, () => new ErrorService());
|
|
1856
1940
|
provide(TYPES.loggerService, () => new LoggerService());
|
|
1857
1941
|
provide(TYPES.resolveService, () => new ResolveService());
|
|
1942
|
+
provide(TYPES.babelService, () => new BabelService());
|
|
1858
1943
|
}
|
|
1859
1944
|
{
|
|
1860
1945
|
provide(TYPES.moduleConnectionService, () => new ModuleConnectionService());
|
|
@@ -1893,6 +1978,7 @@ const baseServices = {
|
|
|
1893
1978
|
errorService: inject(TYPES.errorService),
|
|
1894
1979
|
loggerService: inject(TYPES.loggerService),
|
|
1895
1980
|
resolveService: inject(TYPES.resolveService),
|
|
1981
|
+
babelService: inject(TYPES.babelService),
|
|
1896
1982
|
};
|
|
1897
1983
|
const connectionServices = {
|
|
1898
1984
|
moduleConnectionService: inject(TYPES.moduleConnectionService),
|
|
@@ -1941,7 +2027,7 @@ const notifyShutdown = functoolsKit.singleshot(async () => {
|
|
|
1941
2027
|
|
|
1942
2028
|
const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
|
|
1943
2029
|
process.off("SIGINT", BEFORE_EXIT_FN$4);
|
|
1944
|
-
const [running = null] = await
|
|
2030
|
+
const [running = null] = await BacktestKit.Backtest.list();
|
|
1945
2031
|
if (!running) {
|
|
1946
2032
|
return;
|
|
1947
2033
|
}
|
|
@@ -1950,7 +2036,7 @@ const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
|
|
|
1950
2036
|
if (status === "fulfilled") {
|
|
1951
2037
|
return;
|
|
1952
2038
|
}
|
|
1953
|
-
|
|
2039
|
+
BacktestKit.Backtest.stop(symbol, {
|
|
1954
2040
|
exchangeName,
|
|
1955
2041
|
strategyName,
|
|
1956
2042
|
frameName,
|
|
@@ -1974,7 +2060,7 @@ main$4();
|
|
|
1974
2060
|
|
|
1975
2061
|
const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
|
|
1976
2062
|
process.off("SIGINT", BEFORE_EXIT_FN$3);
|
|
1977
|
-
const [running = null] = await
|
|
2063
|
+
const [running = null] = await BacktestKit.Live.list();
|
|
1978
2064
|
if (!running) {
|
|
1979
2065
|
return;
|
|
1980
2066
|
}
|
|
@@ -1983,7 +2069,7 @@ const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
|
|
|
1983
2069
|
if (status === "fulfilled") {
|
|
1984
2070
|
return;
|
|
1985
2071
|
}
|
|
1986
|
-
|
|
2072
|
+
BacktestKit.Live.stop(symbol, {
|
|
1987
2073
|
exchangeName,
|
|
1988
2074
|
strategyName,
|
|
1989
2075
|
});
|
|
@@ -2006,7 +2092,7 @@ main$3();
|
|
|
2006
2092
|
|
|
2007
2093
|
const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
|
|
2008
2094
|
process.off("SIGINT", BEFORE_EXIT_FN$2);
|
|
2009
|
-
const [running = null] = await
|
|
2095
|
+
const [running = null] = await BacktestKit.Live.list();
|
|
2010
2096
|
if (!running) {
|
|
2011
2097
|
return;
|
|
2012
2098
|
}
|
|
@@ -2015,11 +2101,11 @@ const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
|
|
|
2015
2101
|
if (status === "fulfilled") {
|
|
2016
2102
|
return;
|
|
2017
2103
|
}
|
|
2018
|
-
|
|
2104
|
+
BacktestKit.Live.stop(symbol, {
|
|
2019
2105
|
exchangeName,
|
|
2020
2106
|
strategyName,
|
|
2021
2107
|
});
|
|
2022
|
-
|
|
2108
|
+
BacktestKit.listenDoneLive(cli.liveProviderService.disable);
|
|
2023
2109
|
});
|
|
2024
2110
|
const listenGracefulShutdown$2 = functoolsKit.singleshot(() => {
|
|
2025
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,30 +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
|
-
|
|
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
|
-
|
|
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
|
|
228
|
-
await
|
|
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
|
+
}
|
|
229
245
|
};
|
|
230
|
-
const LOAD_ENTRY_FN = async (filePath) => {
|
|
231
|
-
if (
|
|
232
|
-
|
|
246
|
+
const LOAD_ENTRY_FN = async (filePath, self) => {
|
|
247
|
+
if (REQUIRE_ENTRY_FACTORY()) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (await IMPORT_ENTRY_FACTORY(filePath)) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (await BABEL_ENTRY_FACTORY(filePath, self)) {
|
|
254
|
+
return;
|
|
233
255
|
}
|
|
256
|
+
throw new Error(`Failed to load entry point: ${filePath}`);
|
|
234
257
|
};
|
|
235
258
|
let _is_launched = false;
|
|
236
259
|
class ResolveService {
|
|
237
260
|
constructor() {
|
|
238
261
|
this.loggerService = inject(TYPES.loggerService);
|
|
262
|
+
this.babelService = inject(TYPES.babelService);
|
|
239
263
|
this.DEFAULT_TEMPLATE_DIR = path.resolve(__dirname, '..', 'template');
|
|
240
264
|
this.OVERRIDE_TEMPLATE_DIR = path.resolve(process.cwd(), 'template');
|
|
241
265
|
this.OVERRIDE_MODULES_DIR = path.resolve(process.cwd(), 'modules');
|
|
@@ -252,7 +276,7 @@ class ResolveService {
|
|
|
252
276
|
process.chdir(moduleRoot);
|
|
253
277
|
dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
|
|
254
278
|
dotenv.config({ path: path.join(moduleRoot, '.env'), override: true, quiet: true });
|
|
255
|
-
await LOAD_ENTRY_FN(absolutePath);
|
|
279
|
+
await LOAD_ENTRY_FN(absolutePath, this);
|
|
256
280
|
await entrySubject.next(absolutePath);
|
|
257
281
|
}
|
|
258
282
|
_is_launched = true;
|
|
@@ -1583,16 +1607,21 @@ class TelegramTemplateService {
|
|
|
1583
1607
|
}
|
|
1584
1608
|
}
|
|
1585
1609
|
|
|
1586
|
-
|
|
1610
|
+
createRequire(import.meta.url);
|
|
1587
1611
|
const getExtVariants = (fileName) => {
|
|
1588
1612
|
const ext = path.extname(fileName);
|
|
1589
1613
|
const base = ext ? fileName.slice(0, -ext.length) : fileName;
|
|
1590
1614
|
return [fileName, `${base}.cjs`, `${base}.mjs`];
|
|
1591
1615
|
};
|
|
1592
1616
|
const REQUIRE_MODULE_FACTORY = (fileName) => {
|
|
1617
|
+
{
|
|
1618
|
+
return null;
|
|
1619
|
+
}
|
|
1620
|
+
};
|
|
1621
|
+
const IMPORT_MODULE_FACTORY = async (fileName) => {
|
|
1593
1622
|
for (const variant of getExtVariants(fileName)) {
|
|
1594
1623
|
try {
|
|
1595
|
-
return
|
|
1624
|
+
return await import(pathToFileURL(variant).href);
|
|
1596
1625
|
}
|
|
1597
1626
|
catch {
|
|
1598
1627
|
continue;
|
|
@@ -1600,10 +1629,12 @@ const REQUIRE_MODULE_FACTORY = (fileName) => {
|
|
|
1600
1629
|
}
|
|
1601
1630
|
return null;
|
|
1602
1631
|
};
|
|
1603
|
-
const
|
|
1632
|
+
const BABEL_MODULE_FACTORY = async (fileName, self) => {
|
|
1604
1633
|
for (const variant of getExtVariants(fileName)) {
|
|
1605
1634
|
try {
|
|
1606
|
-
|
|
1635
|
+
const code = await fs$1.readFile(variant, "utf-8");
|
|
1636
|
+
const exports = self.babelService.transpileAndRun(code);
|
|
1637
|
+
return exports.default ?? exports;
|
|
1607
1638
|
}
|
|
1608
1639
|
catch {
|
|
1609
1640
|
continue;
|
|
@@ -1620,18 +1651,22 @@ const LOAD_MODULE_MODULE_FN = async (fileName, self) => {
|
|
|
1620
1651
|
.then(() => true)
|
|
1621
1652
|
.catch(() => false);
|
|
1622
1653
|
const resolvedFile = hasOverride ? overridePath : targetPath;
|
|
1623
|
-
if ((Ctor = REQUIRE_MODULE_FACTORY(
|
|
1654
|
+
if ((Ctor = REQUIRE_MODULE_FACTORY())) {
|
|
1624
1655
|
return typeof Ctor === "function" ? new Ctor() : Ctor;
|
|
1625
1656
|
}
|
|
1626
1657
|
if ((Ctor = await IMPORT_MODULE_FACTORY(resolvedFile))) {
|
|
1627
1658
|
return typeof Ctor === "function" ? new Ctor() : Ctor;
|
|
1628
1659
|
}
|
|
1660
|
+
if ((Ctor = await BABEL_MODULE_FACTORY(resolvedFile, self))) {
|
|
1661
|
+
return typeof Ctor === "function" ? new Ctor() : Ctor;
|
|
1662
|
+
}
|
|
1629
1663
|
throw new Error(`Module module import failed for file: ${resolvedFile}`);
|
|
1630
1664
|
};
|
|
1631
1665
|
class ModuleConnectionService {
|
|
1632
1666
|
constructor() {
|
|
1633
1667
|
this.loggerService = inject(TYPES.loggerService);
|
|
1634
1668
|
this.resolveService = inject(TYPES.resolveService);
|
|
1669
|
+
this.babelService = inject(TYPES.babelService);
|
|
1635
1670
|
this.getInstance = memoize(([fileName]) => `${fileName}`, async (fileName) => {
|
|
1636
1671
|
this.loggerService.log("moduleConnectionService getInstance", {
|
|
1637
1672
|
fileName,
|
|
@@ -1825,6 +1860,55 @@ class LiveProviderService {
|
|
|
1825
1860
|
}
|
|
1826
1861
|
}
|
|
1827
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
|
+
|
|
1828
1912
|
{
|
|
1829
1913
|
provide(TYPES.quickchartApiService, () => new QuickchartApiService());
|
|
1830
1914
|
provide(TYPES.telegramApiService, () => new TelegramApiService());
|
|
@@ -1833,6 +1917,7 @@ class LiveProviderService {
|
|
|
1833
1917
|
provide(TYPES.errorService, () => new ErrorService());
|
|
1834
1918
|
provide(TYPES.loggerService, () => new LoggerService());
|
|
1835
1919
|
provide(TYPES.resolveService, () => new ResolveService());
|
|
1920
|
+
provide(TYPES.babelService, () => new BabelService());
|
|
1836
1921
|
}
|
|
1837
1922
|
{
|
|
1838
1923
|
provide(TYPES.moduleConnectionService, () => new ModuleConnectionService());
|
|
@@ -1871,6 +1956,7 @@ const baseServices = {
|
|
|
1871
1956
|
errorService: inject(TYPES.errorService),
|
|
1872
1957
|
loggerService: inject(TYPES.loggerService),
|
|
1873
1958
|
resolveService: inject(TYPES.resolveService),
|
|
1959
|
+
babelService: inject(TYPES.babelService),
|
|
1874
1960
|
};
|
|
1875
1961
|
const connectionServices = {
|
|
1876
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.
|
|
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
|
-
"
|
|
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",
|
|
@@ -78,10 +81,12 @@
|
|
|
78
81
|
"worker-testbed": "1.0.12"
|
|
79
82
|
},
|
|
80
83
|
"peerDependencies": {
|
|
81
|
-
"
|
|
84
|
+
"@babel/plugin-transform-modules-umd": "^7.27.1",
|
|
85
|
+
"@babel/standalone": "^7.29.1",
|
|
82
86
|
"@backtest-kit/ui": "^3.3.2",
|
|
83
87
|
"backtest-kit": "^3.3.2",
|
|
84
|
-
"markdown-it": "^14.1.1"
|
|
88
|
+
"markdown-it": "^14.1.1",
|
|
89
|
+
"typescript": "^5.0.0"
|
|
85
90
|
},
|
|
86
91
|
"dependencies": {
|
|
87
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
|
-
|
|
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
|
};
|