@backtest-kit/cli 5.9.0 → 5.10.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/README.md +163 -1
- package/build/index.cjs +152 -15
- package/build/index.mjs +154 -16
- package/package.json +15 -14
- package/types.d.ts +2 -1
package/README.md
CHANGED
|
@@ -436,6 +436,169 @@ For projects that compile to or use CommonJS. Loaded via `require()`:
|
|
|
436
436
|
}
|
|
437
437
|
```
|
|
438
438
|
|
|
439
|
+
## 🌲 Running Local PineScript Indicators
|
|
440
|
+
|
|
441
|
+
`@backtest-kit/cli` can execute any local `.pine` file against a real exchange and print the results as a Markdown table — no TradingView account required.
|
|
442
|
+
|
|
443
|
+
### CLI Flags
|
|
444
|
+
|
|
445
|
+
| Flag | Type | Description |
|
|
446
|
+
|------|------|-------------|
|
|
447
|
+
| `--pine` | boolean | Enable PineScript execution mode |
|
|
448
|
+
| `--symbol` | string | Trading pair (default: `"BTCUSDT"`) |
|
|
449
|
+
| `--timeframe` | string | Candle interval (default: `"15m"`) |
|
|
450
|
+
| `--limit` | string | Number of candles to fetch (default: `250`) |
|
|
451
|
+
| `--when` | string | End date for candle window — ISO 8601 or Unix ms (default: now) |
|
|
452
|
+
| `--exchange` | string | Exchange name (default: first registered, falls back to CCXT Binance) |
|
|
453
|
+
| `--json` | string | Write plots as a JSON array to a file (e.g. `--json=./output.json`) |
|
|
454
|
+
| `--jsonl` | string | Write plots as JSONL (one row per line) to a file (e.g. `--jsonl=./output.jsonl`) |
|
|
455
|
+
| `--markdown` | string | Write Markdown table to a file (e.g. `--markdown=./output.md`) |
|
|
456
|
+
|
|
457
|
+
**Important:** `limit` must cover indicator warmup bars — rows before warmup completes will show `N/A`
|
|
458
|
+
|
|
459
|
+
**Positional argument:** path to the `.pine` file.
|
|
460
|
+
|
|
461
|
+
### Exchange via `pine.module`
|
|
462
|
+
|
|
463
|
+
By default the CLI registers CCXT Binance automatically. To use a different exchange — or to configure API keys, custom rate limits, or a non-spot market — create a `modules/pine.module.ts` file. The CLI loads it automatically before running the script.
|
|
464
|
+
|
|
465
|
+
The CLI looks for `modules/pine.module` in two locations (first match wins):
|
|
466
|
+
|
|
467
|
+
1. **Next to the `.pine` file** — `<pine-file-dir>/modules/pine.module.ts`
|
|
468
|
+
2. **Project root** — `<cwd>/modules/pine.module.ts`
|
|
469
|
+
|
|
470
|
+
```
|
|
471
|
+
my-project/
|
|
472
|
+
├── math/
|
|
473
|
+
│ ├── master_trend_15m.pine ← indicator
|
|
474
|
+
│ └── modules/
|
|
475
|
+
│ └── pine.module.ts ← loaded first (next to .pine file)
|
|
476
|
+
├── modules/
|
|
477
|
+
│ └── pine.module.ts ← fallback (project root)
|
|
478
|
+
└── package.json
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Inside `pine.module.ts` call `addExchangeSchema` from `backtest-kit` and give the exchange a name:
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
// modules/pine.module.ts
|
|
485
|
+
import { addExchangeSchema } from "backtest-kit";
|
|
486
|
+
import ccxt from "ccxt";
|
|
487
|
+
|
|
488
|
+
addExchangeSchema({
|
|
489
|
+
exchangeName: "my-exchange",
|
|
490
|
+
getCandles: async (symbol, interval, since, limit) => {
|
|
491
|
+
const exchange = new ccxt.bybit({ enableRateLimit: true });
|
|
492
|
+
const ohlcv = await exchange.fetchOHLCV(symbol, interval, since.getTime(), limit);
|
|
493
|
+
return ohlcv.map(([timestamp, open, high, low, close, volume]) => ({
|
|
494
|
+
timestamp, open, high, low, close, volume,
|
|
495
|
+
}));
|
|
496
|
+
},
|
|
497
|
+
formatPrice: (symbol, price) => price.toFixed(2),
|
|
498
|
+
formatQuantity: (symbol, quantity) => quantity.toFixed(8),
|
|
499
|
+
});
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### Environment variables (`.env`)
|
|
503
|
+
|
|
504
|
+
Before loading `pine.module`, the CLI loads `.env` files in the same order as for strategy modules — project root first, then the `.pine` file directory (overrides root):
|
|
505
|
+
|
|
506
|
+
```
|
|
507
|
+
my-project/
|
|
508
|
+
├── math/
|
|
509
|
+
│ ├── .env ← loaded second (overrides root)
|
|
510
|
+
│ └── master_trend_15m.pine
|
|
511
|
+
├── .env ← loaded first
|
|
512
|
+
└── package.json
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
Use this to store API keys without hardcoding them:
|
|
516
|
+
|
|
517
|
+
```env
|
|
518
|
+
# .env
|
|
519
|
+
BYBIT_API_KEY=xxx
|
|
520
|
+
BYBIT_API_SECRET=yyy
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
// modules/pine.module.ts
|
|
525
|
+
addExchangeSchema({
|
|
526
|
+
exchangeName: "my-exchange",
|
|
527
|
+
getCandles: async (symbol, interval, since, limit) => {
|
|
528
|
+
const exchange = new ccxt.bybit({
|
|
529
|
+
apiKey: process.env.BYBIT_API_KEY,
|
|
530
|
+
secret: process.env.BYBIT_API_SECRET,
|
|
531
|
+
enableRateLimit: true,
|
|
532
|
+
});
|
|
533
|
+
// ...
|
|
534
|
+
},
|
|
535
|
+
});
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
Then run:
|
|
539
|
+
|
|
540
|
+
```bash
|
|
541
|
+
npx @backtest-kit/cli --pine ./math/master_trend_15m.pine \
|
|
542
|
+
--exchange my-exchange \
|
|
543
|
+
--symbol BTCUSDT \
|
|
544
|
+
--timeframe 15m \
|
|
545
|
+
--limit 180 \
|
|
546
|
+
--when "2025-09-24T12:00:00.000Z"
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
Or add it to `package.json`:
|
|
550
|
+
|
|
551
|
+
```json
|
|
552
|
+
{
|
|
553
|
+
"scripts": {
|
|
554
|
+
"pine": "npx @backtest-kit/cli --pine ./math/master_trend_15m.pine --symbol BTCUSDT --timeframe 15m --limit 180"
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
```bash
|
|
560
|
+
npm run pine
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### PineScript Requirements
|
|
564
|
+
|
|
565
|
+
The CLI reads all `plot()` calls that use `display=display.data_window` as output columns. Every other `plot()` is ignored. Name each output plot explicitly:
|
|
566
|
+
|
|
567
|
+
```pine
|
|
568
|
+
//@version=5
|
|
569
|
+
indicator("MyIndicator", overlay=true)
|
|
570
|
+
|
|
571
|
+
// ... computation ...
|
|
572
|
+
|
|
573
|
+
plot(close, "Close", display=display.data_window)
|
|
574
|
+
plot(position, "Position", display=display.data_window)
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
The column names in the output Markdown table are taken directly from those plot names — no manual schema definition needed.
|
|
578
|
+
|
|
579
|
+
### Output
|
|
580
|
+
|
|
581
|
+
The CLI prints a Markdown table to stdout:
|
|
582
|
+
|
|
583
|
+
```
|
|
584
|
+
# PineScript Technical Analysis Dump
|
|
585
|
+
|
|
586
|
+
**Signal ID**: CLI execution 2025-09-24T12:00:00.000Z
|
|
587
|
+
|
|
588
|
+
| Close | Position | timestamp |
|
|
589
|
+
| --- | --- | --- |
|
|
590
|
+
| 112871.28 | -1.0000 | 2025-09-22T15:00:00.000Z |
|
|
591
|
+
| 112666.69 | -1.0000 | 2025-09-22T15:15:00.000Z |
|
|
592
|
+
| 112736.00 | 0.0000 | 2025-09-22T18:30:00.000Z |
|
|
593
|
+
| 112653.90 | 1.0000 | 2025-09-22T22:15:00.000Z |
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
Redirect to a file to save the report:
|
|
597
|
+
|
|
598
|
+
```bash
|
|
599
|
+
npm run pine > report.md
|
|
600
|
+
```
|
|
601
|
+
|
|
439
602
|
## 🌍 Environment Variables
|
|
440
603
|
|
|
441
604
|
Create a `.env` file in your project root:
|
|
@@ -474,7 +637,6 @@ When your strategy module does not register an exchange, frame, or strategy name
|
|
|
474
637
|
|
|
475
638
|
> **Note:** The default exchange schema **does not support order book fetching in backtest mode**. If your strategy calls `getOrderBook()` during backtest, you must register a custom exchange schema with your own snapshot storage.
|
|
476
639
|
|
|
477
|
-
|
|
478
640
|
## 🔧 Programmatic API
|
|
479
641
|
|
|
480
642
|
In addition to the CLI, `@backtest-kit/cli` can be used as a library — call `run()` directly from your own script without spawning a child process or parsing CLI flags.
|
package/build/index.cjs
CHANGED
|
@@ -93,6 +93,8 @@ var BacktestKitSignals__namespace = /*#__PURE__*/_interopNamespaceDefault(Backte
|
|
|
93
93
|
{
|
|
94
94
|
BacktestKit.Markdown.enable();
|
|
95
95
|
BacktestKit.Report.enable();
|
|
96
|
+
BacktestKit.Dump.enable();
|
|
97
|
+
BacktestKit.Memory.enable();
|
|
96
98
|
}
|
|
97
99
|
{
|
|
98
100
|
BacktestKit.Dump.useMarkdown();
|
|
@@ -296,12 +298,31 @@ class ResolveService {
|
|
|
296
298
|
this.loggerService.log("resolveService getIsLaunched");
|
|
297
299
|
return _is_launched;
|
|
298
300
|
};
|
|
299
|
-
this.
|
|
300
|
-
this.loggerService.log("resolveService
|
|
301
|
+
this.attachPine = async (pinePath) => {
|
|
302
|
+
this.loggerService.log("resolveService attachPine");
|
|
301
303
|
if (_is_launched) {
|
|
302
304
|
throw new Error("Entry point is already attached. Multiple entry points are not allowed.");
|
|
303
305
|
}
|
|
304
|
-
const absolutePath = path.resolve(
|
|
306
|
+
const absolutePath = path.resolve(pinePath);
|
|
307
|
+
await fs$1.access(absolutePath, fs.constants.F_OK | fs.constants.R_OK);
|
|
308
|
+
const pineDir = path.dirname(absolutePath);
|
|
309
|
+
{
|
|
310
|
+
const cwd = process.cwd();
|
|
311
|
+
process.chdir(pineDir);
|
|
312
|
+
dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
|
|
313
|
+
dotenv.config({ path: path.join(pineDir, '.env'), override: true, quiet: true });
|
|
314
|
+
}
|
|
315
|
+
_is_launched = true;
|
|
316
|
+
return await fs$1.readFile(absolutePath, "utf-8");
|
|
317
|
+
};
|
|
318
|
+
this.attachJavascript = async (jsPath) => {
|
|
319
|
+
this.loggerService.log("resolveService attachJavascript", {
|
|
320
|
+
jsPath
|
|
321
|
+
});
|
|
322
|
+
if (_is_launched) {
|
|
323
|
+
throw new Error("Entry point is already attached. Multiple entry points are not allowed.");
|
|
324
|
+
}
|
|
325
|
+
const absolutePath = path.resolve(jsPath);
|
|
305
326
|
await fs$1.access(absolutePath, fs.constants.F_OK | fs.constants.R_OK);
|
|
306
327
|
const moduleRoot = path.dirname(absolutePath);
|
|
307
328
|
{
|
|
@@ -428,6 +449,7 @@ const getArgs = functoolsKit.singleshot(() => {
|
|
|
428
449
|
const { values, positionals } = util.parseArgs({
|
|
429
450
|
args: process.argv,
|
|
430
451
|
options: {
|
|
452
|
+
// backtest entry
|
|
431
453
|
symbol: {
|
|
432
454
|
type: "string",
|
|
433
455
|
default: "",
|
|
@@ -480,6 +502,35 @@ const getArgs = functoolsKit.singleshot(() => {
|
|
|
480
502
|
type: "string",
|
|
481
503
|
default: "1m, 15m, 30m, 4h",
|
|
482
504
|
},
|
|
505
|
+
// pinescript entry
|
|
506
|
+
pine: {
|
|
507
|
+
type: "boolean",
|
|
508
|
+
default: false,
|
|
509
|
+
},
|
|
510
|
+
timeframe: {
|
|
511
|
+
type: "string",
|
|
512
|
+
default: "",
|
|
513
|
+
},
|
|
514
|
+
limit: {
|
|
515
|
+
type: "string",
|
|
516
|
+
default: "",
|
|
517
|
+
},
|
|
518
|
+
when: {
|
|
519
|
+
type: "string",
|
|
520
|
+
default: "",
|
|
521
|
+
},
|
|
522
|
+
json: {
|
|
523
|
+
type: "string",
|
|
524
|
+
default: "",
|
|
525
|
+
},
|
|
526
|
+
jsonl: {
|
|
527
|
+
type: "string",
|
|
528
|
+
default: "",
|
|
529
|
+
},
|
|
530
|
+
markdown: {
|
|
531
|
+
type: "string",
|
|
532
|
+
default: "",
|
|
533
|
+
},
|
|
483
534
|
},
|
|
484
535
|
strict: false,
|
|
485
536
|
allowPositionals: true,
|
|
@@ -621,7 +672,7 @@ class BacktestMainService {
|
|
|
621
672
|
this.frontendProviderService.connect();
|
|
622
673
|
this.telegramProviderService.connect();
|
|
623
674
|
}
|
|
624
|
-
await this.resolveService.
|
|
675
|
+
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
625
676
|
{
|
|
626
677
|
this.exchangeSchemaService.addSchema();
|
|
627
678
|
this.symbolSchemaService.addSchema();
|
|
@@ -714,7 +765,7 @@ class LiveMainService {
|
|
|
714
765
|
this.frontendProviderService.connect();
|
|
715
766
|
this.telegramProviderService.connect();
|
|
716
767
|
}
|
|
717
|
-
await this.resolveService.
|
|
768
|
+
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
718
769
|
{
|
|
719
770
|
this.exchangeSchemaService.addSchema();
|
|
720
771
|
this.symbolSchemaService.addSchema();
|
|
@@ -787,7 +838,7 @@ class PaperMainService {
|
|
|
787
838
|
this.frontendProviderService.connect();
|
|
788
839
|
this.telegramProviderService.connect();
|
|
789
840
|
}
|
|
790
|
-
await this.resolveService.
|
|
841
|
+
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
791
842
|
{
|
|
792
843
|
this.exchangeSchemaService.addSchema();
|
|
793
844
|
this.symbolSchemaService.addSchema();
|
|
@@ -2147,7 +2198,7 @@ const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
|
|
|
2147
2198
|
const listenGracefulShutdown$4 = functoolsKit.singleshot(() => {
|
|
2148
2199
|
process.on("SIGINT", BEFORE_EXIT_FN$4);
|
|
2149
2200
|
});
|
|
2150
|
-
const main$
|
|
2201
|
+
const main$5 = async () => {
|
|
2151
2202
|
if (!getEntry((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))) {
|
|
2152
2203
|
return;
|
|
2153
2204
|
}
|
|
@@ -2158,7 +2209,7 @@ const main$4 = async () => {
|
|
|
2158
2209
|
await cli.backtestMainService.connect();
|
|
2159
2210
|
listenGracefulShutdown$4();
|
|
2160
2211
|
};
|
|
2161
|
-
main$
|
|
2212
|
+
main$5();
|
|
2162
2213
|
|
|
2163
2214
|
const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
|
|
2164
2215
|
process.off("SIGINT", BEFORE_EXIT_FN$3);
|
|
@@ -2179,7 +2230,7 @@ const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
|
|
|
2179
2230
|
const listenGracefulShutdown$3 = functoolsKit.singleshot(() => {
|
|
2180
2231
|
process.on("SIGINT", BEFORE_EXIT_FN$3);
|
|
2181
2232
|
});
|
|
2182
|
-
const main$
|
|
2233
|
+
const main$4 = async () => {
|
|
2183
2234
|
if (!getEntry((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))) {
|
|
2184
2235
|
return;
|
|
2185
2236
|
}
|
|
@@ -2190,7 +2241,7 @@ const main$3 = async () => {
|
|
|
2190
2241
|
cli.paperMainService.connect();
|
|
2191
2242
|
listenGracefulShutdown$3();
|
|
2192
2243
|
};
|
|
2193
|
-
main$
|
|
2244
|
+
main$4();
|
|
2194
2245
|
|
|
2195
2246
|
const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
|
|
2196
2247
|
process.off("SIGINT", BEFORE_EXIT_FN$2);
|
|
@@ -2211,7 +2262,7 @@ const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
|
|
|
2211
2262
|
const listenGracefulShutdown$2 = functoolsKit.singleshot(() => {
|
|
2212
2263
|
process.on("SIGINT", BEFORE_EXIT_FN$2);
|
|
2213
2264
|
});
|
|
2214
|
-
const main$
|
|
2265
|
+
const main$3 = async () => {
|
|
2215
2266
|
if (!getEntry((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))) {
|
|
2216
2267
|
return;
|
|
2217
2268
|
}
|
|
@@ -2222,7 +2273,7 @@ const main$2 = async () => {
|
|
|
2222
2273
|
await cli.liveMainService.connect();
|
|
2223
2274
|
listenGracefulShutdown$2();
|
|
2224
2275
|
};
|
|
2225
|
-
main$
|
|
2276
|
+
main$3();
|
|
2226
2277
|
|
|
2227
2278
|
const BEFORE_EXIT_FN$1 = functoolsKit.singleshot(async () => {
|
|
2228
2279
|
process.off("SIGINT", BEFORE_EXIT_FN$1);
|
|
@@ -2232,7 +2283,7 @@ const BEFORE_EXIT_FN$1 = functoolsKit.singleshot(async () => {
|
|
|
2232
2283
|
const listenGracefulShutdown$1 = functoolsKit.singleshot(() => {
|
|
2233
2284
|
process.on("SIGINT", BEFORE_EXIT_FN$1);
|
|
2234
2285
|
});
|
|
2235
|
-
const main$
|
|
2286
|
+
const main$2 = async () => {
|
|
2236
2287
|
if (!getEntry((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))) {
|
|
2237
2288
|
return;
|
|
2238
2289
|
}
|
|
@@ -2242,7 +2293,7 @@ const main$1 = async () => {
|
|
|
2242
2293
|
}
|
|
2243
2294
|
listenGracefulShutdown$1();
|
|
2244
2295
|
};
|
|
2245
|
-
main$
|
|
2296
|
+
main$2();
|
|
2246
2297
|
|
|
2247
2298
|
const BEFORE_EXIT_FN = functoolsKit.singleshot(async () => {
|
|
2248
2299
|
process.off("SIGINT", BEFORE_EXIT_FN);
|
|
@@ -2252,7 +2303,7 @@ const BEFORE_EXIT_FN = functoolsKit.singleshot(async () => {
|
|
|
2252
2303
|
const listenGracefulShutdown = functoolsKit.singleshot(() => {
|
|
2253
2304
|
process.on("SIGINT", BEFORE_EXIT_FN);
|
|
2254
2305
|
});
|
|
2255
|
-
const main = async () => {
|
|
2306
|
+
const main$1 = async () => {
|
|
2256
2307
|
if (!getEntry((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))) {
|
|
2257
2308
|
return;
|
|
2258
2309
|
}
|
|
@@ -2262,6 +2313,92 @@ const main = async () => {
|
|
|
2262
2313
|
}
|
|
2263
2314
|
listenGracefulShutdown();
|
|
2264
2315
|
};
|
|
2316
|
+
main$1();
|
|
2317
|
+
|
|
2318
|
+
const EXTRACT_ROWS_FN = (plots, schema) => {
|
|
2319
|
+
const keys = Object.keys(schema);
|
|
2320
|
+
const dataLength = keys
|
|
2321
|
+
.map((k) => plots[k]?.data?.length ?? 0)
|
|
2322
|
+
.reduce((acm, cur) => Math.max(acm, cur), 0);
|
|
2323
|
+
const rows = [];
|
|
2324
|
+
for (let i = 0; i < dataLength; i++) {
|
|
2325
|
+
const row = {};
|
|
2326
|
+
for (const key of keys) {
|
|
2327
|
+
const point = plots[key]?.data?.[i];
|
|
2328
|
+
row[key] = point?.value ?? null;
|
|
2329
|
+
}
|
|
2330
|
+
const point = plots[keys[0]]?.data?.[i];
|
|
2331
|
+
if (point?.time) {
|
|
2332
|
+
row.timestamp = new Date(point.time).toISOString();
|
|
2333
|
+
}
|
|
2334
|
+
rows.push(row);
|
|
2335
|
+
}
|
|
2336
|
+
return rows;
|
|
2337
|
+
};
|
|
2338
|
+
const main = async () => {
|
|
2339
|
+
if (!getEntry((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)))) {
|
|
2340
|
+
return;
|
|
2341
|
+
}
|
|
2342
|
+
const { values, positionals } = getArgs();
|
|
2343
|
+
if (!values.pine) {
|
|
2344
|
+
return;
|
|
2345
|
+
}
|
|
2346
|
+
const [entryPoint = null] = positionals.slice(-1);
|
|
2347
|
+
if (!entryPoint) {
|
|
2348
|
+
return;
|
|
2349
|
+
}
|
|
2350
|
+
const source = await cli.resolveService.attachPine(entryPoint);
|
|
2351
|
+
await cli.moduleConnectionService.loadModule("./pine.module");
|
|
2352
|
+
{
|
|
2353
|
+
await cli.exchangeSchemaService.addSchema();
|
|
2354
|
+
await cli.symbolSchemaService.addSchema();
|
|
2355
|
+
}
|
|
2356
|
+
const [defaultExchangeName = null] = await BacktestKit.listExchangeSchema();
|
|
2357
|
+
const exchangeName = values.exchange || defaultExchangeName?.exchangeName;
|
|
2358
|
+
const symbol = values.symbol || "BTCUSDT";
|
|
2359
|
+
const timeframe = values.timeframe || "15m";
|
|
2360
|
+
const limitStr = values.limit || "250";
|
|
2361
|
+
const limitNum = parseInt(limitStr);
|
|
2362
|
+
const limit = isNaN(limitNum) ? 250 : limitNum;
|
|
2363
|
+
const whenStr = values.when || Date.now().toString();
|
|
2364
|
+
const whenStamp = Date.parse(whenStr);
|
|
2365
|
+
const when = isNaN(whenStamp) ? new Date() : new Date(whenStamp);
|
|
2366
|
+
const plots = await BacktestKitPinets.run(BacktestKitPinets.Code.fromString(source), {
|
|
2367
|
+
symbol,
|
|
2368
|
+
timeframe: timeframe,
|
|
2369
|
+
limit,
|
|
2370
|
+
}, exchangeName, when);
|
|
2371
|
+
const signalId = `CLI execution ${new Date().toISOString()}`;
|
|
2372
|
+
const signalSchema = Object.fromEntries(Object.keys(plots)
|
|
2373
|
+
.filter((key) => plots[key].data.some((v) => {
|
|
2374
|
+
if (typeof v?.value !== "number") {
|
|
2375
|
+
return false;
|
|
2376
|
+
}
|
|
2377
|
+
if (!isFinite(v.value)) {
|
|
2378
|
+
return false;
|
|
2379
|
+
}
|
|
2380
|
+
return true;
|
|
2381
|
+
}))
|
|
2382
|
+
.map((key) => [key, key]));
|
|
2383
|
+
const jsonPath = values.json;
|
|
2384
|
+
if (jsonPath) {
|
|
2385
|
+
const rows = EXTRACT_ROWS_FN(plots, signalSchema);
|
|
2386
|
+
await fs$1.writeFile(jsonPath, JSON.stringify(rows, null, 2), "utf-8");
|
|
2387
|
+
return;
|
|
2388
|
+
}
|
|
2389
|
+
const jsonlPath = values.jsonl;
|
|
2390
|
+
if (jsonlPath) {
|
|
2391
|
+
const rows = EXTRACT_ROWS_FN(plots, signalSchema);
|
|
2392
|
+
await fs$1.writeFile(jsonlPath, rows.map((r) => JSON.stringify(r)).join("\n"), "utf-8");
|
|
2393
|
+
return;
|
|
2394
|
+
}
|
|
2395
|
+
const markdownPath = values.markdown;
|
|
2396
|
+
if (markdownPath) {
|
|
2397
|
+
await fs$1.writeFile(markdownPath, await BacktestKitPinets.toMarkdown(signalId, plots, signalSchema), "utf-8");
|
|
2398
|
+
return;
|
|
2399
|
+
}
|
|
2400
|
+
console.log(await BacktestKitPinets.toMarkdown(signalId, plots, signalSchema));
|
|
2401
|
+
};
|
|
2265
2402
|
main();
|
|
2266
2403
|
|
|
2267
2404
|
function setLogger(logger) {
|
package/build/index.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { getErrorMessage, errorData, singleshot, str, BehaviorSubject, compose,
|
|
|
5
5
|
import fs, { constants } from 'fs';
|
|
6
6
|
import * as stackTrace from 'stack-trace';
|
|
7
7
|
import path from 'path';
|
|
8
|
-
import fs$1, { access } from 'fs/promises';
|
|
8
|
+
import fs$1, { access, readFile, writeFile } from 'fs/promises';
|
|
9
9
|
import dotenv from 'dotenv';
|
|
10
10
|
import { createActivator } from 'di-kit';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
@@ -28,6 +28,7 @@ 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';
|
|
31
|
+
import { run as run$1, Code, toMarkdown } from '@backtest-kit/pinets';
|
|
31
32
|
import * as BacktestKitSignals from '@backtest-kit/signals';
|
|
32
33
|
|
|
33
34
|
/**
|
|
@@ -67,6 +68,8 @@ import * as BacktestKitSignals from '@backtest-kit/signals';
|
|
|
67
68
|
{
|
|
68
69
|
Markdown.enable();
|
|
69
70
|
Report.enable();
|
|
71
|
+
Dump.enable();
|
|
72
|
+
Memory.enable();
|
|
70
73
|
}
|
|
71
74
|
{
|
|
72
75
|
Dump.useMarkdown();
|
|
@@ -270,12 +273,31 @@ class ResolveService {
|
|
|
270
273
|
this.loggerService.log("resolveService getIsLaunched");
|
|
271
274
|
return _is_launched;
|
|
272
275
|
};
|
|
273
|
-
this.
|
|
274
|
-
this.loggerService.log("resolveService
|
|
276
|
+
this.attachPine = async (pinePath) => {
|
|
277
|
+
this.loggerService.log("resolveService attachPine");
|
|
275
278
|
if (_is_launched) {
|
|
276
279
|
throw new Error("Entry point is already attached. Multiple entry points are not allowed.");
|
|
277
280
|
}
|
|
278
|
-
const absolutePath = path.resolve(
|
|
281
|
+
const absolutePath = path.resolve(pinePath);
|
|
282
|
+
await access(absolutePath, constants.F_OK | constants.R_OK);
|
|
283
|
+
const pineDir = path.dirname(absolutePath);
|
|
284
|
+
{
|
|
285
|
+
const cwd = process.cwd();
|
|
286
|
+
process.chdir(pineDir);
|
|
287
|
+
dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
|
|
288
|
+
dotenv.config({ path: path.join(pineDir, '.env'), override: true, quiet: true });
|
|
289
|
+
}
|
|
290
|
+
_is_launched = true;
|
|
291
|
+
return await readFile(absolutePath, "utf-8");
|
|
292
|
+
};
|
|
293
|
+
this.attachJavascript = async (jsPath) => {
|
|
294
|
+
this.loggerService.log("resolveService attachJavascript", {
|
|
295
|
+
jsPath
|
|
296
|
+
});
|
|
297
|
+
if (_is_launched) {
|
|
298
|
+
throw new Error("Entry point is already attached. Multiple entry points are not allowed.");
|
|
299
|
+
}
|
|
300
|
+
const absolutePath = path.resolve(jsPath);
|
|
279
301
|
await access(absolutePath, constants.F_OK | constants.R_OK);
|
|
280
302
|
const moduleRoot = path.dirname(absolutePath);
|
|
281
303
|
{
|
|
@@ -402,6 +424,7 @@ const getArgs = singleshot(() => {
|
|
|
402
424
|
const { values, positionals } = parseArgs({
|
|
403
425
|
args: process.argv,
|
|
404
426
|
options: {
|
|
427
|
+
// backtest entry
|
|
405
428
|
symbol: {
|
|
406
429
|
type: "string",
|
|
407
430
|
default: "",
|
|
@@ -454,6 +477,35 @@ const getArgs = singleshot(() => {
|
|
|
454
477
|
type: "string",
|
|
455
478
|
default: "1m, 15m, 30m, 4h",
|
|
456
479
|
},
|
|
480
|
+
// pinescript entry
|
|
481
|
+
pine: {
|
|
482
|
+
type: "boolean",
|
|
483
|
+
default: false,
|
|
484
|
+
},
|
|
485
|
+
timeframe: {
|
|
486
|
+
type: "string",
|
|
487
|
+
default: "",
|
|
488
|
+
},
|
|
489
|
+
limit: {
|
|
490
|
+
type: "string",
|
|
491
|
+
default: "",
|
|
492
|
+
},
|
|
493
|
+
when: {
|
|
494
|
+
type: "string",
|
|
495
|
+
default: "",
|
|
496
|
+
},
|
|
497
|
+
json: {
|
|
498
|
+
type: "string",
|
|
499
|
+
default: "",
|
|
500
|
+
},
|
|
501
|
+
jsonl: {
|
|
502
|
+
type: "string",
|
|
503
|
+
default: "",
|
|
504
|
+
},
|
|
505
|
+
markdown: {
|
|
506
|
+
type: "string",
|
|
507
|
+
default: "",
|
|
508
|
+
},
|
|
457
509
|
},
|
|
458
510
|
strict: false,
|
|
459
511
|
allowPositionals: true,
|
|
@@ -595,7 +647,7 @@ class BacktestMainService {
|
|
|
595
647
|
this.frontendProviderService.connect();
|
|
596
648
|
this.telegramProviderService.connect();
|
|
597
649
|
}
|
|
598
|
-
await this.resolveService.
|
|
650
|
+
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
599
651
|
{
|
|
600
652
|
this.exchangeSchemaService.addSchema();
|
|
601
653
|
this.symbolSchemaService.addSchema();
|
|
@@ -688,7 +740,7 @@ class LiveMainService {
|
|
|
688
740
|
this.frontendProviderService.connect();
|
|
689
741
|
this.telegramProviderService.connect();
|
|
690
742
|
}
|
|
691
|
-
await this.resolveService.
|
|
743
|
+
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
692
744
|
{
|
|
693
745
|
this.exchangeSchemaService.addSchema();
|
|
694
746
|
this.symbolSchemaService.addSchema();
|
|
@@ -761,7 +813,7 @@ class PaperMainService {
|
|
|
761
813
|
this.frontendProviderService.connect();
|
|
762
814
|
this.telegramProviderService.connect();
|
|
763
815
|
}
|
|
764
|
-
await this.resolveService.
|
|
816
|
+
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
765
817
|
{
|
|
766
818
|
this.exchangeSchemaService.addSchema();
|
|
767
819
|
this.symbolSchemaService.addSchema();
|
|
@@ -2117,7 +2169,7 @@ const BEFORE_EXIT_FN$4 = singleshot(async () => {
|
|
|
2117
2169
|
const listenGracefulShutdown$4 = singleshot(() => {
|
|
2118
2170
|
process.on("SIGINT", BEFORE_EXIT_FN$4);
|
|
2119
2171
|
});
|
|
2120
|
-
const main$
|
|
2172
|
+
const main$5 = async () => {
|
|
2121
2173
|
if (!getEntry(import.meta.url)) {
|
|
2122
2174
|
return;
|
|
2123
2175
|
}
|
|
@@ -2128,7 +2180,7 @@ const main$4 = async () => {
|
|
|
2128
2180
|
await cli.backtestMainService.connect();
|
|
2129
2181
|
listenGracefulShutdown$4();
|
|
2130
2182
|
};
|
|
2131
|
-
main$
|
|
2183
|
+
main$5();
|
|
2132
2184
|
|
|
2133
2185
|
const BEFORE_EXIT_FN$3 = singleshot(async () => {
|
|
2134
2186
|
process.off("SIGINT", BEFORE_EXIT_FN$3);
|
|
@@ -2149,7 +2201,7 @@ const BEFORE_EXIT_FN$3 = singleshot(async () => {
|
|
|
2149
2201
|
const listenGracefulShutdown$3 = singleshot(() => {
|
|
2150
2202
|
process.on("SIGINT", BEFORE_EXIT_FN$3);
|
|
2151
2203
|
});
|
|
2152
|
-
const main$
|
|
2204
|
+
const main$4 = async () => {
|
|
2153
2205
|
if (!getEntry(import.meta.url)) {
|
|
2154
2206
|
return;
|
|
2155
2207
|
}
|
|
@@ -2160,7 +2212,7 @@ const main$3 = async () => {
|
|
|
2160
2212
|
cli.paperMainService.connect();
|
|
2161
2213
|
listenGracefulShutdown$3();
|
|
2162
2214
|
};
|
|
2163
|
-
main$
|
|
2215
|
+
main$4();
|
|
2164
2216
|
|
|
2165
2217
|
const BEFORE_EXIT_FN$2 = singleshot(async () => {
|
|
2166
2218
|
process.off("SIGINT", BEFORE_EXIT_FN$2);
|
|
@@ -2181,7 +2233,7 @@ const BEFORE_EXIT_FN$2 = singleshot(async () => {
|
|
|
2181
2233
|
const listenGracefulShutdown$2 = singleshot(() => {
|
|
2182
2234
|
process.on("SIGINT", BEFORE_EXIT_FN$2);
|
|
2183
2235
|
});
|
|
2184
|
-
const main$
|
|
2236
|
+
const main$3 = async () => {
|
|
2185
2237
|
if (!getEntry(import.meta.url)) {
|
|
2186
2238
|
return;
|
|
2187
2239
|
}
|
|
@@ -2192,7 +2244,7 @@ const main$2 = async () => {
|
|
|
2192
2244
|
await cli.liveMainService.connect();
|
|
2193
2245
|
listenGracefulShutdown$2();
|
|
2194
2246
|
};
|
|
2195
|
-
main$
|
|
2247
|
+
main$3();
|
|
2196
2248
|
|
|
2197
2249
|
const BEFORE_EXIT_FN$1 = singleshot(async () => {
|
|
2198
2250
|
process.off("SIGINT", BEFORE_EXIT_FN$1);
|
|
@@ -2202,7 +2254,7 @@ const BEFORE_EXIT_FN$1 = singleshot(async () => {
|
|
|
2202
2254
|
const listenGracefulShutdown$1 = singleshot(() => {
|
|
2203
2255
|
process.on("SIGINT", BEFORE_EXIT_FN$1);
|
|
2204
2256
|
});
|
|
2205
|
-
const main$
|
|
2257
|
+
const main$2 = async () => {
|
|
2206
2258
|
if (!getEntry(import.meta.url)) {
|
|
2207
2259
|
return;
|
|
2208
2260
|
}
|
|
@@ -2212,7 +2264,7 @@ const main$1 = async () => {
|
|
|
2212
2264
|
}
|
|
2213
2265
|
listenGracefulShutdown$1();
|
|
2214
2266
|
};
|
|
2215
|
-
main$
|
|
2267
|
+
main$2();
|
|
2216
2268
|
|
|
2217
2269
|
const BEFORE_EXIT_FN = singleshot(async () => {
|
|
2218
2270
|
process.off("SIGINT", BEFORE_EXIT_FN);
|
|
@@ -2222,7 +2274,7 @@ const BEFORE_EXIT_FN = singleshot(async () => {
|
|
|
2222
2274
|
const listenGracefulShutdown = singleshot(() => {
|
|
2223
2275
|
process.on("SIGINT", BEFORE_EXIT_FN);
|
|
2224
2276
|
});
|
|
2225
|
-
const main = async () => {
|
|
2277
|
+
const main$1 = async () => {
|
|
2226
2278
|
if (!getEntry(import.meta.url)) {
|
|
2227
2279
|
return;
|
|
2228
2280
|
}
|
|
@@ -2232,6 +2284,92 @@ const main = async () => {
|
|
|
2232
2284
|
}
|
|
2233
2285
|
listenGracefulShutdown();
|
|
2234
2286
|
};
|
|
2287
|
+
main$1();
|
|
2288
|
+
|
|
2289
|
+
const EXTRACT_ROWS_FN = (plots, schema) => {
|
|
2290
|
+
const keys = Object.keys(schema);
|
|
2291
|
+
const dataLength = keys
|
|
2292
|
+
.map((k) => plots[k]?.data?.length ?? 0)
|
|
2293
|
+
.reduce((acm, cur) => Math.max(acm, cur), 0);
|
|
2294
|
+
const rows = [];
|
|
2295
|
+
for (let i = 0; i < dataLength; i++) {
|
|
2296
|
+
const row = {};
|
|
2297
|
+
for (const key of keys) {
|
|
2298
|
+
const point = plots[key]?.data?.[i];
|
|
2299
|
+
row[key] = point?.value ?? null;
|
|
2300
|
+
}
|
|
2301
|
+
const point = plots[keys[0]]?.data?.[i];
|
|
2302
|
+
if (point?.time) {
|
|
2303
|
+
row.timestamp = new Date(point.time).toISOString();
|
|
2304
|
+
}
|
|
2305
|
+
rows.push(row);
|
|
2306
|
+
}
|
|
2307
|
+
return rows;
|
|
2308
|
+
};
|
|
2309
|
+
const main = async () => {
|
|
2310
|
+
if (!getEntry(import.meta.url)) {
|
|
2311
|
+
return;
|
|
2312
|
+
}
|
|
2313
|
+
const { values, positionals } = getArgs();
|
|
2314
|
+
if (!values.pine) {
|
|
2315
|
+
return;
|
|
2316
|
+
}
|
|
2317
|
+
const [entryPoint = null] = positionals.slice(-1);
|
|
2318
|
+
if (!entryPoint) {
|
|
2319
|
+
return;
|
|
2320
|
+
}
|
|
2321
|
+
const source = await cli.resolveService.attachPine(entryPoint);
|
|
2322
|
+
await cli.moduleConnectionService.loadModule("./pine.module");
|
|
2323
|
+
{
|
|
2324
|
+
await cli.exchangeSchemaService.addSchema();
|
|
2325
|
+
await cli.symbolSchemaService.addSchema();
|
|
2326
|
+
}
|
|
2327
|
+
const [defaultExchangeName = null] = await listExchangeSchema();
|
|
2328
|
+
const exchangeName = values.exchange || defaultExchangeName?.exchangeName;
|
|
2329
|
+
const symbol = values.symbol || "BTCUSDT";
|
|
2330
|
+
const timeframe = values.timeframe || "15m";
|
|
2331
|
+
const limitStr = values.limit || "250";
|
|
2332
|
+
const limitNum = parseInt(limitStr);
|
|
2333
|
+
const limit = isNaN(limitNum) ? 250 : limitNum;
|
|
2334
|
+
const whenStr = values.when || Date.now().toString();
|
|
2335
|
+
const whenStamp = Date.parse(whenStr);
|
|
2336
|
+
const when = isNaN(whenStamp) ? new Date() : new Date(whenStamp);
|
|
2337
|
+
const plots = await run$1(Code.fromString(source), {
|
|
2338
|
+
symbol,
|
|
2339
|
+
timeframe: timeframe,
|
|
2340
|
+
limit,
|
|
2341
|
+
}, exchangeName, when);
|
|
2342
|
+
const signalId = `CLI execution ${new Date().toISOString()}`;
|
|
2343
|
+
const signalSchema = Object.fromEntries(Object.keys(plots)
|
|
2344
|
+
.filter((key) => plots[key].data.some((v) => {
|
|
2345
|
+
if (typeof v?.value !== "number") {
|
|
2346
|
+
return false;
|
|
2347
|
+
}
|
|
2348
|
+
if (!isFinite(v.value)) {
|
|
2349
|
+
return false;
|
|
2350
|
+
}
|
|
2351
|
+
return true;
|
|
2352
|
+
}))
|
|
2353
|
+
.map((key) => [key, key]));
|
|
2354
|
+
const jsonPath = values.json;
|
|
2355
|
+
if (jsonPath) {
|
|
2356
|
+
const rows = EXTRACT_ROWS_FN(plots, signalSchema);
|
|
2357
|
+
await writeFile(jsonPath, JSON.stringify(rows, null, 2), "utf-8");
|
|
2358
|
+
return;
|
|
2359
|
+
}
|
|
2360
|
+
const jsonlPath = values.jsonl;
|
|
2361
|
+
if (jsonlPath) {
|
|
2362
|
+
const rows = EXTRACT_ROWS_FN(plots, signalSchema);
|
|
2363
|
+
await writeFile(jsonlPath, rows.map((r) => JSON.stringify(r)).join("\n"), "utf-8");
|
|
2364
|
+
return;
|
|
2365
|
+
}
|
|
2366
|
+
const markdownPath = values.markdown;
|
|
2367
|
+
if (markdownPath) {
|
|
2368
|
+
await writeFile(markdownPath, await toMarkdown(signalId, plots, signalSchema), "utf-8");
|
|
2369
|
+
return;
|
|
2370
|
+
}
|
|
2371
|
+
console.log(await toMarkdown(signalId, plots, signalSchema));
|
|
2372
|
+
};
|
|
2235
2373
|
main();
|
|
2236
2374
|
|
|
2237
2375
|
function setLogger(logger) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backtest-kit/cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.10.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",
|
|
@@ -45,7 +45,8 @@
|
|
|
45
45
|
"url": "https://github.com/tripolskypetr/backtest-kit/issues"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
|
-
"build": "rollup -c"
|
|
48
|
+
"build": "rollup -c",
|
|
49
|
+
"start": "npm run build && node build/index.mjs math/master_trend_15m.pine --pine --symbol BTCUSDT --timeframe 15m --limit 180"
|
|
49
50
|
},
|
|
50
51
|
"main": "build/index.cjs",
|
|
51
52
|
"module": "build/index.mjs",
|
|
@@ -60,11 +61,11 @@
|
|
|
60
61
|
"devDependencies": {
|
|
61
62
|
"@babel/plugin-transform-modules-umd": "7.27.1",
|
|
62
63
|
"@babel/standalone": "7.29.1",
|
|
63
|
-
"@backtest-kit/ui": "5.
|
|
64
|
-
"@backtest-kit/graph": "5.
|
|
65
|
-
"@backtest-kit/ollama": "5.
|
|
66
|
-
"@backtest-kit/pinets": "5.
|
|
67
|
-
"@backtest-kit/signals": "5.
|
|
64
|
+
"@backtest-kit/ui": "5.10.0",
|
|
65
|
+
"@backtest-kit/graph": "5.10.0",
|
|
66
|
+
"@backtest-kit/ollama": "5.10.0",
|
|
67
|
+
"@backtest-kit/pinets": "5.10.0",
|
|
68
|
+
"@backtest-kit/signals": "5.10.0",
|
|
68
69
|
"@rollup/plugin-replace": "6.0.3",
|
|
69
70
|
"@rollup/plugin-typescript": "11.1.6",
|
|
70
71
|
"@types/image-size": "0.7.0",
|
|
@@ -72,7 +73,7 @@
|
|
|
72
73
|
"@types/mustache": "4.2.6",
|
|
73
74
|
"@types/node": "22.9.0",
|
|
74
75
|
"@types/stack-trace": "0.0.33",
|
|
75
|
-
"backtest-kit": "5.
|
|
76
|
+
"backtest-kit": "5.10.0",
|
|
76
77
|
"glob": "11.0.1",
|
|
77
78
|
"markdown-it": "14.1.1",
|
|
78
79
|
"rimraf": "6.0.1",
|
|
@@ -87,12 +88,12 @@
|
|
|
87
88
|
"peerDependencies": {
|
|
88
89
|
"@babel/plugin-transform-modules-umd": "^7.27.1",
|
|
89
90
|
"@babel/standalone": "^7.29.1",
|
|
90
|
-
"@backtest-kit/ui": "^5.
|
|
91
|
-
"@backtest-kit/graph": "^5.
|
|
92
|
-
"@backtest-kit/ollama": "^5.
|
|
93
|
-
"@backtest-kit/pinets": "^5.
|
|
94
|
-
"@backtest-kit/signals": "^5.
|
|
95
|
-
"backtest-kit": "^5.
|
|
91
|
+
"@backtest-kit/ui": "^5.10.0",
|
|
92
|
+
"@backtest-kit/graph": "^5.10.0",
|
|
93
|
+
"@backtest-kit/ollama": "^5.10.0",
|
|
94
|
+
"@backtest-kit/pinets": "^5.10.0",
|
|
95
|
+
"@backtest-kit/signals": "^5.10.0",
|
|
96
|
+
"backtest-kit": "^5.10.0",
|
|
96
97
|
"markdown-it": "^14.1.1",
|
|
97
98
|
"typescript": "^5.0.0"
|
|
98
99
|
},
|
package/types.d.ts
CHANGED
|
@@ -103,7 +103,8 @@ declare class ResolveService {
|
|
|
103
103
|
readonly OVERRIDE_TEMPLATE_DIR: string;
|
|
104
104
|
readonly OVERRIDE_MODULES_DIR: string;
|
|
105
105
|
getIsLaunched: () => boolean;
|
|
106
|
-
|
|
106
|
+
attachPine: (pinePath: string) => Promise<string>;
|
|
107
|
+
attachJavascript: (jsPath: string) => Promise<void>;
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
declare class ErrorService {
|