@backtest-kit/cli 5.9.1 → 5.10.1
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 +177 -1
- package/build/index.cjs +158 -15
- package/build/index.mjs +161 -17
- package/package.json +15 -14
- package/types.d.ts +2 -1
package/README.md
CHANGED
|
@@ -436,6 +436,183 @@ 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
|
+
| `--output` | string | Output file base name without extension (default: `.pine` file name) |
|
|
454
|
+
| `--json` | boolean | Write plots as a JSON array to `<pine-dir>/dump/{output}.json` |
|
|
455
|
+
| `--jsonl` | boolean | Write plots as JSONL (one row per line) to `<pine-dir>/dump/{output}.jsonl` |
|
|
456
|
+
| `--markdown` | boolean | Write Markdown table to `<pine-dir>/dump/{output}.md` |
|
|
457
|
+
|
|
458
|
+
**Important:** `limit` must cover indicator warmup bars — rows before warmup completes will show `N/A`
|
|
459
|
+
|
|
460
|
+
**Positional argument:** path to the `.pine` file.
|
|
461
|
+
|
|
462
|
+
### Exchange via `pine.module`
|
|
463
|
+
|
|
464
|
+
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.
|
|
465
|
+
|
|
466
|
+
The CLI looks for `modules/pine.module` in two locations (first match wins):
|
|
467
|
+
|
|
468
|
+
1. **Next to the `.pine` file** — `<pine-file-dir>/modules/pine.module.ts`
|
|
469
|
+
2. **Project root** — `<cwd>/modules/pine.module.ts`
|
|
470
|
+
|
|
471
|
+
```
|
|
472
|
+
my-project/
|
|
473
|
+
├── math/
|
|
474
|
+
│ ├── impulse_trend_15m.pine ← indicator
|
|
475
|
+
│ └── modules/
|
|
476
|
+
│ └── pine.module.ts ← loaded first (next to .pine file)
|
|
477
|
+
├── modules/
|
|
478
|
+
│ └── pine.module.ts ← fallback (project root)
|
|
479
|
+
└── package.json
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
Inside `pine.module.ts` call `addExchangeSchema` from `backtest-kit` and give the exchange a name:
|
|
483
|
+
|
|
484
|
+
```typescript
|
|
485
|
+
// modules/pine.module.ts
|
|
486
|
+
import { addExchangeSchema } from "backtest-kit";
|
|
487
|
+
import ccxt from "ccxt";
|
|
488
|
+
|
|
489
|
+
addExchangeSchema({
|
|
490
|
+
exchangeName: "my-exchange",
|
|
491
|
+
getCandles: async (symbol, interval, since, limit) => {
|
|
492
|
+
const exchange = new ccxt.bybit({ enableRateLimit: true });
|
|
493
|
+
const ohlcv = await exchange.fetchOHLCV(symbol, interval, since.getTime(), limit);
|
|
494
|
+
return ohlcv.map(([timestamp, open, high, low, close, volume]) => ({
|
|
495
|
+
timestamp, open, high, low, close, volume,
|
|
496
|
+
}));
|
|
497
|
+
},
|
|
498
|
+
formatPrice: (symbol, price) => price.toFixed(2),
|
|
499
|
+
formatQuantity: (symbol, quantity) => quantity.toFixed(8),
|
|
500
|
+
});
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Environment variables (`.env`)
|
|
504
|
+
|
|
505
|
+
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):
|
|
506
|
+
|
|
507
|
+
```
|
|
508
|
+
my-project/
|
|
509
|
+
├── math/
|
|
510
|
+
│ ├── .env ← loaded second (overrides root)
|
|
511
|
+
│ └── impulse_trend_15m.pine
|
|
512
|
+
├── .env ← loaded first
|
|
513
|
+
└── package.json
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
Use this to store API keys without hardcoding them:
|
|
517
|
+
|
|
518
|
+
```env
|
|
519
|
+
# .env
|
|
520
|
+
BYBIT_API_KEY=xxx
|
|
521
|
+
BYBIT_API_SECRET=yyy
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
// modules/pine.module.ts
|
|
526
|
+
addExchangeSchema({
|
|
527
|
+
exchangeName: "my-exchange",
|
|
528
|
+
getCandles: async (symbol, interval, since, limit) => {
|
|
529
|
+
const exchange = new ccxt.bybit({
|
|
530
|
+
apiKey: process.env.BYBIT_API_KEY,
|
|
531
|
+
secret: process.env.BYBIT_API_SECRET,
|
|
532
|
+
enableRateLimit: true,
|
|
533
|
+
});
|
|
534
|
+
// ...
|
|
535
|
+
},
|
|
536
|
+
});
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
Then run:
|
|
540
|
+
|
|
541
|
+
```bash
|
|
542
|
+
npx @backtest-kit/cli --pine ./math/impulse_trend_15m.pine \
|
|
543
|
+
--exchange my-exchange \
|
|
544
|
+
--symbol BTCUSDT \
|
|
545
|
+
--timeframe 15m \
|
|
546
|
+
--limit 180 \
|
|
547
|
+
--when "2025-09-24T12:00:00.000Z"
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
Or add it to `package.json`:
|
|
551
|
+
|
|
552
|
+
```json
|
|
553
|
+
{
|
|
554
|
+
"scripts": {
|
|
555
|
+
"pine": "npx @backtest-kit/cli --pine ./math/impulse_trend_15m.pine --symbol BTCUSDT --timeframe 15m --limit 180"
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
```bash
|
|
561
|
+
npm run pine
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### PineScript Requirements
|
|
565
|
+
|
|
566
|
+
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:
|
|
567
|
+
|
|
568
|
+
```pine
|
|
569
|
+
//@version=5
|
|
570
|
+
indicator("MyIndicator", overlay=true)
|
|
571
|
+
|
|
572
|
+
// ... computation ...
|
|
573
|
+
|
|
574
|
+
plot(close, "Close", display=display.data_window)
|
|
575
|
+
plot(position, "Position", display=display.data_window)
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
The column names in the output Markdown table are taken directly from those plot names — no manual schema definition needed.
|
|
579
|
+
|
|
580
|
+
### Output
|
|
581
|
+
|
|
582
|
+
The CLI prints a Markdown table to stdout:
|
|
583
|
+
|
|
584
|
+
```
|
|
585
|
+
# PineScript Technical Analysis Dump
|
|
586
|
+
|
|
587
|
+
**Signal ID**: CLI execution 2025-09-24T12:00:00.000Z
|
|
588
|
+
|
|
589
|
+
| Close | Position | timestamp |
|
|
590
|
+
| --- | --- | --- |
|
|
591
|
+
| 112871.28 | -1.0000 | 2025-09-22T15:00:00.000Z |
|
|
592
|
+
| 112666.69 | -1.0000 | 2025-09-22T15:15:00.000Z |
|
|
593
|
+
| 112736.00 | 0.0000 | 2025-09-22T18:30:00.000Z |
|
|
594
|
+
| 112653.90 | 1.0000 | 2025-09-22T22:15:00.000Z |
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
Save to `./math/dump/impulse_trend_15m.md` (uses `.pine` file name automatically, dump is created next to the `.pine` file):
|
|
598
|
+
|
|
599
|
+
```bash
|
|
600
|
+
npx @backtest-kit/cli --pine ./math/impulse_trend_15m.pine --markdown
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
Override the output name with `--output`:
|
|
604
|
+
|
|
605
|
+
```bash
|
|
606
|
+
npx @backtest-kit/cli --pine ./math/impulse_trend_15m.pine --jsonl --output feb2026_bb
|
|
607
|
+
# → ./math/dump/feb2026_bb.jsonl
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
Print to stdout (no flag):
|
|
611
|
+
|
|
612
|
+
```bash
|
|
613
|
+
npx @backtest-kit/cli --pine ./math/impulse_trend_15m.pine
|
|
614
|
+
```
|
|
615
|
+
|
|
439
616
|
## 🌍 Environment Variables
|
|
440
617
|
|
|
441
618
|
Create a `.env` file in your project root:
|
|
@@ -474,7 +651,6 @@ When your strategy module does not register an exchange, frame, or strategy name
|
|
|
474
651
|
|
|
475
652
|
> **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
653
|
|
|
477
|
-
|
|
478
654
|
## 🔧 Programmatic API
|
|
479
655
|
|
|
480
656
|
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,39 @@ 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
|
+
output: {
|
|
523
|
+
type: "string",
|
|
524
|
+
default: "",
|
|
525
|
+
},
|
|
526
|
+
json: {
|
|
527
|
+
type: "boolean",
|
|
528
|
+
default: false,
|
|
529
|
+
},
|
|
530
|
+
jsonl: {
|
|
531
|
+
type: "boolean",
|
|
532
|
+
default: false,
|
|
533
|
+
},
|
|
534
|
+
markdown: {
|
|
535
|
+
type: "boolean",
|
|
536
|
+
default: false,
|
|
537
|
+
},
|
|
483
538
|
},
|
|
484
539
|
strict: false,
|
|
485
540
|
allowPositionals: true,
|
|
@@ -621,7 +676,7 @@ class BacktestMainService {
|
|
|
621
676
|
this.frontendProviderService.connect();
|
|
622
677
|
this.telegramProviderService.connect();
|
|
623
678
|
}
|
|
624
|
-
await this.resolveService.
|
|
679
|
+
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
625
680
|
{
|
|
626
681
|
this.exchangeSchemaService.addSchema();
|
|
627
682
|
this.symbolSchemaService.addSchema();
|
|
@@ -714,7 +769,7 @@ class LiveMainService {
|
|
|
714
769
|
this.frontendProviderService.connect();
|
|
715
770
|
this.telegramProviderService.connect();
|
|
716
771
|
}
|
|
717
|
-
await this.resolveService.
|
|
772
|
+
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
718
773
|
{
|
|
719
774
|
this.exchangeSchemaService.addSchema();
|
|
720
775
|
this.symbolSchemaService.addSchema();
|
|
@@ -787,7 +842,7 @@ class PaperMainService {
|
|
|
787
842
|
this.frontendProviderService.connect();
|
|
788
843
|
this.telegramProviderService.connect();
|
|
789
844
|
}
|
|
790
|
-
await this.resolveService.
|
|
845
|
+
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
791
846
|
{
|
|
792
847
|
this.exchangeSchemaService.addSchema();
|
|
793
848
|
this.symbolSchemaService.addSchema();
|
|
@@ -2147,7 +2202,7 @@ const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
|
|
|
2147
2202
|
const listenGracefulShutdown$4 = functoolsKit.singleshot(() => {
|
|
2148
2203
|
process.on("SIGINT", BEFORE_EXIT_FN$4);
|
|
2149
2204
|
});
|
|
2150
|
-
const main$
|
|
2205
|
+
const main$5 = async () => {
|
|
2151
2206
|
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
2207
|
return;
|
|
2153
2208
|
}
|
|
@@ -2158,7 +2213,7 @@ const main$4 = async () => {
|
|
|
2158
2213
|
await cli.backtestMainService.connect();
|
|
2159
2214
|
listenGracefulShutdown$4();
|
|
2160
2215
|
};
|
|
2161
|
-
main$
|
|
2216
|
+
main$5();
|
|
2162
2217
|
|
|
2163
2218
|
const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
|
|
2164
2219
|
process.off("SIGINT", BEFORE_EXIT_FN$3);
|
|
@@ -2179,7 +2234,7 @@ const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
|
|
|
2179
2234
|
const listenGracefulShutdown$3 = functoolsKit.singleshot(() => {
|
|
2180
2235
|
process.on("SIGINT", BEFORE_EXIT_FN$3);
|
|
2181
2236
|
});
|
|
2182
|
-
const main$
|
|
2237
|
+
const main$4 = async () => {
|
|
2183
2238
|
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
2239
|
return;
|
|
2185
2240
|
}
|
|
@@ -2190,7 +2245,7 @@ const main$3 = async () => {
|
|
|
2190
2245
|
cli.paperMainService.connect();
|
|
2191
2246
|
listenGracefulShutdown$3();
|
|
2192
2247
|
};
|
|
2193
|
-
main$
|
|
2248
|
+
main$4();
|
|
2194
2249
|
|
|
2195
2250
|
const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
|
|
2196
2251
|
process.off("SIGINT", BEFORE_EXIT_FN$2);
|
|
@@ -2211,7 +2266,7 @@ const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
|
|
|
2211
2266
|
const listenGracefulShutdown$2 = functoolsKit.singleshot(() => {
|
|
2212
2267
|
process.on("SIGINT", BEFORE_EXIT_FN$2);
|
|
2213
2268
|
});
|
|
2214
|
-
const main$
|
|
2269
|
+
const main$3 = async () => {
|
|
2215
2270
|
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
2271
|
return;
|
|
2217
2272
|
}
|
|
@@ -2222,7 +2277,7 @@ const main$2 = async () => {
|
|
|
2222
2277
|
await cli.liveMainService.connect();
|
|
2223
2278
|
listenGracefulShutdown$2();
|
|
2224
2279
|
};
|
|
2225
|
-
main$
|
|
2280
|
+
main$3();
|
|
2226
2281
|
|
|
2227
2282
|
const BEFORE_EXIT_FN$1 = functoolsKit.singleshot(async () => {
|
|
2228
2283
|
process.off("SIGINT", BEFORE_EXIT_FN$1);
|
|
@@ -2232,7 +2287,7 @@ const BEFORE_EXIT_FN$1 = functoolsKit.singleshot(async () => {
|
|
|
2232
2287
|
const listenGracefulShutdown$1 = functoolsKit.singleshot(() => {
|
|
2233
2288
|
process.on("SIGINT", BEFORE_EXIT_FN$1);
|
|
2234
2289
|
});
|
|
2235
|
-
const main$
|
|
2290
|
+
const main$2 = async () => {
|
|
2236
2291
|
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
2292
|
return;
|
|
2238
2293
|
}
|
|
@@ -2242,7 +2297,7 @@ const main$1 = async () => {
|
|
|
2242
2297
|
}
|
|
2243
2298
|
listenGracefulShutdown$1();
|
|
2244
2299
|
};
|
|
2245
|
-
main$
|
|
2300
|
+
main$2();
|
|
2246
2301
|
|
|
2247
2302
|
const BEFORE_EXIT_FN = functoolsKit.singleshot(async () => {
|
|
2248
2303
|
process.off("SIGINT", BEFORE_EXIT_FN);
|
|
@@ -2252,7 +2307,7 @@ const BEFORE_EXIT_FN = functoolsKit.singleshot(async () => {
|
|
|
2252
2307
|
const listenGracefulShutdown = functoolsKit.singleshot(() => {
|
|
2253
2308
|
process.on("SIGINT", BEFORE_EXIT_FN);
|
|
2254
2309
|
});
|
|
2255
|
-
const main = async () => {
|
|
2310
|
+
const main$1 = async () => {
|
|
2256
2311
|
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
2312
|
return;
|
|
2258
2313
|
}
|
|
@@ -2262,6 +2317,94 @@ const main = async () => {
|
|
|
2262
2317
|
}
|
|
2263
2318
|
listenGracefulShutdown();
|
|
2264
2319
|
};
|
|
2320
|
+
main$1();
|
|
2321
|
+
|
|
2322
|
+
const EXTRACT_ROWS_FN = (plots, schema) => {
|
|
2323
|
+
const keys = Object.keys(schema);
|
|
2324
|
+
const dataLength = keys
|
|
2325
|
+
.map((k) => plots[k]?.data?.length ?? 0)
|
|
2326
|
+
.reduce((acm, cur) => Math.max(acm, cur), 0);
|
|
2327
|
+
const rows = [];
|
|
2328
|
+
for (let i = 0; i < dataLength; i++) {
|
|
2329
|
+
const row = {};
|
|
2330
|
+
for (const key of keys) {
|
|
2331
|
+
const point = plots[key]?.data?.[i];
|
|
2332
|
+
row[key] = point?.value ?? null;
|
|
2333
|
+
}
|
|
2334
|
+
const point = plots[keys[0]]?.data?.[i];
|
|
2335
|
+
if (point?.time) {
|
|
2336
|
+
row.timestamp = new Date(point.time).toISOString();
|
|
2337
|
+
}
|
|
2338
|
+
rows.push(row);
|
|
2339
|
+
}
|
|
2340
|
+
return rows;
|
|
2341
|
+
};
|
|
2342
|
+
const main = async () => {
|
|
2343
|
+
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)))) {
|
|
2344
|
+
return;
|
|
2345
|
+
}
|
|
2346
|
+
const { values, positionals } = getArgs();
|
|
2347
|
+
if (!values.pine) {
|
|
2348
|
+
return;
|
|
2349
|
+
}
|
|
2350
|
+
const [entryPoint = null] = positionals.slice(-1);
|
|
2351
|
+
if (!entryPoint) {
|
|
2352
|
+
return;
|
|
2353
|
+
}
|
|
2354
|
+
const source = await cli.resolveService.attachPine(entryPoint);
|
|
2355
|
+
await cli.moduleConnectionService.loadModule("./pine.module");
|
|
2356
|
+
{
|
|
2357
|
+
await cli.exchangeSchemaService.addSchema();
|
|
2358
|
+
await cli.symbolSchemaService.addSchema();
|
|
2359
|
+
}
|
|
2360
|
+
const [defaultExchangeName = null] = await BacktestKit.listExchangeSchema();
|
|
2361
|
+
const exchangeName = values.exchange || defaultExchangeName?.exchangeName;
|
|
2362
|
+
const symbol = values.symbol || "BTCUSDT";
|
|
2363
|
+
const timeframe = values.timeframe || "15m";
|
|
2364
|
+
const limitStr = values.limit || "250";
|
|
2365
|
+
const limitNum = parseInt(limitStr);
|
|
2366
|
+
const limit = isNaN(limitNum) ? 250 : limitNum;
|
|
2367
|
+
const whenStr = values.when || Date.now().toString();
|
|
2368
|
+
const whenStamp = Date.parse(whenStr);
|
|
2369
|
+
const when = isNaN(whenStamp) ? new Date() : new Date(whenStamp);
|
|
2370
|
+
const plots = await BacktestKitPinets.run(BacktestKitPinets.Code.fromString(source), {
|
|
2371
|
+
symbol,
|
|
2372
|
+
timeframe: timeframe,
|
|
2373
|
+
limit,
|
|
2374
|
+
}, exchangeName, when);
|
|
2375
|
+
const signalId = `CLI execution ${new Date().toISOString()}`;
|
|
2376
|
+
const signalSchema = Object.fromEntries(Object.keys(plots)
|
|
2377
|
+
.filter((key) => plots[key].data.some((v) => {
|
|
2378
|
+
if (typeof v?.value !== "number") {
|
|
2379
|
+
return false;
|
|
2380
|
+
}
|
|
2381
|
+
if (!isFinite(v.value)) {
|
|
2382
|
+
return false;
|
|
2383
|
+
}
|
|
2384
|
+
return true;
|
|
2385
|
+
}))
|
|
2386
|
+
.map((key) => [key, key]));
|
|
2387
|
+
const dumpName = values.output || path.basename(entryPoint, path.extname(entryPoint));
|
|
2388
|
+
const dumpDir = path.join(process.cwd(), "dump");
|
|
2389
|
+
if (values.json) {
|
|
2390
|
+
const rows = EXTRACT_ROWS_FN(plots, signalSchema);
|
|
2391
|
+
await fs$1.mkdir(dumpDir, { recursive: true });
|
|
2392
|
+
await fs$1.writeFile(path.join(dumpDir, `${dumpName}.json`), JSON.stringify(rows, null, 2), "utf-8");
|
|
2393
|
+
return;
|
|
2394
|
+
}
|
|
2395
|
+
if (values.jsonl) {
|
|
2396
|
+
const rows = EXTRACT_ROWS_FN(plots, signalSchema);
|
|
2397
|
+
await fs$1.mkdir(dumpDir, { recursive: true });
|
|
2398
|
+
await fs$1.writeFile(path.join(dumpDir, `${dumpName}.jsonl`), rows.map((r) => JSON.stringify(r)).join("\n"), "utf-8");
|
|
2399
|
+
return;
|
|
2400
|
+
}
|
|
2401
|
+
if (values.markdown) {
|
|
2402
|
+
await fs$1.mkdir(dumpDir, { recursive: true });
|
|
2403
|
+
await fs$1.writeFile(path.join(dumpDir, `${dumpName}.md`), await BacktestKitPinets.toMarkdown(signalId, plots, signalSchema), "utf-8");
|
|
2404
|
+
return;
|
|
2405
|
+
}
|
|
2406
|
+
console.log(await BacktestKitPinets.toMarkdown(signalId, plots, signalSchema));
|
|
2407
|
+
};
|
|
2265
2408
|
main();
|
|
2266
2409
|
|
|
2267
2410
|
function setLogger(logger) {
|
package/build/index.mjs
CHANGED
|
@@ -4,8 +4,8 @@ import { Storage, Notification, Markdown, Report, Dump, Memory, StorageLive, Sto
|
|
|
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 path from 'path';
|
|
8
|
-
import fs$1, { access } from 'fs/promises';
|
|
7
|
+
import path, { basename, extname, join } from 'path';
|
|
8
|
+
import fs$1, { access, readFile, mkdir, 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,39 @@ 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
|
+
output: {
|
|
498
|
+
type: "string",
|
|
499
|
+
default: "",
|
|
500
|
+
},
|
|
501
|
+
json: {
|
|
502
|
+
type: "boolean",
|
|
503
|
+
default: false,
|
|
504
|
+
},
|
|
505
|
+
jsonl: {
|
|
506
|
+
type: "boolean",
|
|
507
|
+
default: false,
|
|
508
|
+
},
|
|
509
|
+
markdown: {
|
|
510
|
+
type: "boolean",
|
|
511
|
+
default: false,
|
|
512
|
+
},
|
|
457
513
|
},
|
|
458
514
|
strict: false,
|
|
459
515
|
allowPositionals: true,
|
|
@@ -595,7 +651,7 @@ class BacktestMainService {
|
|
|
595
651
|
this.frontendProviderService.connect();
|
|
596
652
|
this.telegramProviderService.connect();
|
|
597
653
|
}
|
|
598
|
-
await this.resolveService.
|
|
654
|
+
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
599
655
|
{
|
|
600
656
|
this.exchangeSchemaService.addSchema();
|
|
601
657
|
this.symbolSchemaService.addSchema();
|
|
@@ -688,7 +744,7 @@ class LiveMainService {
|
|
|
688
744
|
this.frontendProviderService.connect();
|
|
689
745
|
this.telegramProviderService.connect();
|
|
690
746
|
}
|
|
691
|
-
await this.resolveService.
|
|
747
|
+
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
692
748
|
{
|
|
693
749
|
this.exchangeSchemaService.addSchema();
|
|
694
750
|
this.symbolSchemaService.addSchema();
|
|
@@ -761,7 +817,7 @@ class PaperMainService {
|
|
|
761
817
|
this.frontendProviderService.connect();
|
|
762
818
|
this.telegramProviderService.connect();
|
|
763
819
|
}
|
|
764
|
-
await this.resolveService.
|
|
820
|
+
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
765
821
|
{
|
|
766
822
|
this.exchangeSchemaService.addSchema();
|
|
767
823
|
this.symbolSchemaService.addSchema();
|
|
@@ -2117,7 +2173,7 @@ const BEFORE_EXIT_FN$4 = singleshot(async () => {
|
|
|
2117
2173
|
const listenGracefulShutdown$4 = singleshot(() => {
|
|
2118
2174
|
process.on("SIGINT", BEFORE_EXIT_FN$4);
|
|
2119
2175
|
});
|
|
2120
|
-
const main$
|
|
2176
|
+
const main$5 = async () => {
|
|
2121
2177
|
if (!getEntry(import.meta.url)) {
|
|
2122
2178
|
return;
|
|
2123
2179
|
}
|
|
@@ -2128,7 +2184,7 @@ const main$4 = async () => {
|
|
|
2128
2184
|
await cli.backtestMainService.connect();
|
|
2129
2185
|
listenGracefulShutdown$4();
|
|
2130
2186
|
};
|
|
2131
|
-
main$
|
|
2187
|
+
main$5();
|
|
2132
2188
|
|
|
2133
2189
|
const BEFORE_EXIT_FN$3 = singleshot(async () => {
|
|
2134
2190
|
process.off("SIGINT", BEFORE_EXIT_FN$3);
|
|
@@ -2149,7 +2205,7 @@ const BEFORE_EXIT_FN$3 = singleshot(async () => {
|
|
|
2149
2205
|
const listenGracefulShutdown$3 = singleshot(() => {
|
|
2150
2206
|
process.on("SIGINT", BEFORE_EXIT_FN$3);
|
|
2151
2207
|
});
|
|
2152
|
-
const main$
|
|
2208
|
+
const main$4 = async () => {
|
|
2153
2209
|
if (!getEntry(import.meta.url)) {
|
|
2154
2210
|
return;
|
|
2155
2211
|
}
|
|
@@ -2160,7 +2216,7 @@ const main$3 = async () => {
|
|
|
2160
2216
|
cli.paperMainService.connect();
|
|
2161
2217
|
listenGracefulShutdown$3();
|
|
2162
2218
|
};
|
|
2163
|
-
main$
|
|
2219
|
+
main$4();
|
|
2164
2220
|
|
|
2165
2221
|
const BEFORE_EXIT_FN$2 = singleshot(async () => {
|
|
2166
2222
|
process.off("SIGINT", BEFORE_EXIT_FN$2);
|
|
@@ -2181,7 +2237,7 @@ const BEFORE_EXIT_FN$2 = singleshot(async () => {
|
|
|
2181
2237
|
const listenGracefulShutdown$2 = singleshot(() => {
|
|
2182
2238
|
process.on("SIGINT", BEFORE_EXIT_FN$2);
|
|
2183
2239
|
});
|
|
2184
|
-
const main$
|
|
2240
|
+
const main$3 = async () => {
|
|
2185
2241
|
if (!getEntry(import.meta.url)) {
|
|
2186
2242
|
return;
|
|
2187
2243
|
}
|
|
@@ -2192,7 +2248,7 @@ const main$2 = async () => {
|
|
|
2192
2248
|
await cli.liveMainService.connect();
|
|
2193
2249
|
listenGracefulShutdown$2();
|
|
2194
2250
|
};
|
|
2195
|
-
main$
|
|
2251
|
+
main$3();
|
|
2196
2252
|
|
|
2197
2253
|
const BEFORE_EXIT_FN$1 = singleshot(async () => {
|
|
2198
2254
|
process.off("SIGINT", BEFORE_EXIT_FN$1);
|
|
@@ -2202,7 +2258,7 @@ const BEFORE_EXIT_FN$1 = singleshot(async () => {
|
|
|
2202
2258
|
const listenGracefulShutdown$1 = singleshot(() => {
|
|
2203
2259
|
process.on("SIGINT", BEFORE_EXIT_FN$1);
|
|
2204
2260
|
});
|
|
2205
|
-
const main$
|
|
2261
|
+
const main$2 = async () => {
|
|
2206
2262
|
if (!getEntry(import.meta.url)) {
|
|
2207
2263
|
return;
|
|
2208
2264
|
}
|
|
@@ -2212,7 +2268,7 @@ const main$1 = async () => {
|
|
|
2212
2268
|
}
|
|
2213
2269
|
listenGracefulShutdown$1();
|
|
2214
2270
|
};
|
|
2215
|
-
main$
|
|
2271
|
+
main$2();
|
|
2216
2272
|
|
|
2217
2273
|
const BEFORE_EXIT_FN = singleshot(async () => {
|
|
2218
2274
|
process.off("SIGINT", BEFORE_EXIT_FN);
|
|
@@ -2222,7 +2278,7 @@ const BEFORE_EXIT_FN = singleshot(async () => {
|
|
|
2222
2278
|
const listenGracefulShutdown = singleshot(() => {
|
|
2223
2279
|
process.on("SIGINT", BEFORE_EXIT_FN);
|
|
2224
2280
|
});
|
|
2225
|
-
const main = async () => {
|
|
2281
|
+
const main$1 = async () => {
|
|
2226
2282
|
if (!getEntry(import.meta.url)) {
|
|
2227
2283
|
return;
|
|
2228
2284
|
}
|
|
@@ -2232,6 +2288,94 @@ const main = async () => {
|
|
|
2232
2288
|
}
|
|
2233
2289
|
listenGracefulShutdown();
|
|
2234
2290
|
};
|
|
2291
|
+
main$1();
|
|
2292
|
+
|
|
2293
|
+
const EXTRACT_ROWS_FN = (plots, schema) => {
|
|
2294
|
+
const keys = Object.keys(schema);
|
|
2295
|
+
const dataLength = keys
|
|
2296
|
+
.map((k) => plots[k]?.data?.length ?? 0)
|
|
2297
|
+
.reduce((acm, cur) => Math.max(acm, cur), 0);
|
|
2298
|
+
const rows = [];
|
|
2299
|
+
for (let i = 0; i < dataLength; i++) {
|
|
2300
|
+
const row = {};
|
|
2301
|
+
for (const key of keys) {
|
|
2302
|
+
const point = plots[key]?.data?.[i];
|
|
2303
|
+
row[key] = point?.value ?? null;
|
|
2304
|
+
}
|
|
2305
|
+
const point = plots[keys[0]]?.data?.[i];
|
|
2306
|
+
if (point?.time) {
|
|
2307
|
+
row.timestamp = new Date(point.time).toISOString();
|
|
2308
|
+
}
|
|
2309
|
+
rows.push(row);
|
|
2310
|
+
}
|
|
2311
|
+
return rows;
|
|
2312
|
+
};
|
|
2313
|
+
const main = async () => {
|
|
2314
|
+
if (!getEntry(import.meta.url)) {
|
|
2315
|
+
return;
|
|
2316
|
+
}
|
|
2317
|
+
const { values, positionals } = getArgs();
|
|
2318
|
+
if (!values.pine) {
|
|
2319
|
+
return;
|
|
2320
|
+
}
|
|
2321
|
+
const [entryPoint = null] = positionals.slice(-1);
|
|
2322
|
+
if (!entryPoint) {
|
|
2323
|
+
return;
|
|
2324
|
+
}
|
|
2325
|
+
const source = await cli.resolveService.attachPine(entryPoint);
|
|
2326
|
+
await cli.moduleConnectionService.loadModule("./pine.module");
|
|
2327
|
+
{
|
|
2328
|
+
await cli.exchangeSchemaService.addSchema();
|
|
2329
|
+
await cli.symbolSchemaService.addSchema();
|
|
2330
|
+
}
|
|
2331
|
+
const [defaultExchangeName = null] = await listExchangeSchema();
|
|
2332
|
+
const exchangeName = values.exchange || defaultExchangeName?.exchangeName;
|
|
2333
|
+
const symbol = values.symbol || "BTCUSDT";
|
|
2334
|
+
const timeframe = values.timeframe || "15m";
|
|
2335
|
+
const limitStr = values.limit || "250";
|
|
2336
|
+
const limitNum = parseInt(limitStr);
|
|
2337
|
+
const limit = isNaN(limitNum) ? 250 : limitNum;
|
|
2338
|
+
const whenStr = values.when || Date.now().toString();
|
|
2339
|
+
const whenStamp = Date.parse(whenStr);
|
|
2340
|
+
const when = isNaN(whenStamp) ? new Date() : new Date(whenStamp);
|
|
2341
|
+
const plots = await run$1(Code.fromString(source), {
|
|
2342
|
+
symbol,
|
|
2343
|
+
timeframe: timeframe,
|
|
2344
|
+
limit,
|
|
2345
|
+
}, exchangeName, when);
|
|
2346
|
+
const signalId = `CLI execution ${new Date().toISOString()}`;
|
|
2347
|
+
const signalSchema = Object.fromEntries(Object.keys(plots)
|
|
2348
|
+
.filter((key) => plots[key].data.some((v) => {
|
|
2349
|
+
if (typeof v?.value !== "number") {
|
|
2350
|
+
return false;
|
|
2351
|
+
}
|
|
2352
|
+
if (!isFinite(v.value)) {
|
|
2353
|
+
return false;
|
|
2354
|
+
}
|
|
2355
|
+
return true;
|
|
2356
|
+
}))
|
|
2357
|
+
.map((key) => [key, key]));
|
|
2358
|
+
const dumpName = values.output || basename(entryPoint, extname(entryPoint));
|
|
2359
|
+
const dumpDir = join(process.cwd(), "dump");
|
|
2360
|
+
if (values.json) {
|
|
2361
|
+
const rows = EXTRACT_ROWS_FN(plots, signalSchema);
|
|
2362
|
+
await mkdir(dumpDir, { recursive: true });
|
|
2363
|
+
await writeFile(join(dumpDir, `${dumpName}.json`), JSON.stringify(rows, null, 2), "utf-8");
|
|
2364
|
+
return;
|
|
2365
|
+
}
|
|
2366
|
+
if (values.jsonl) {
|
|
2367
|
+
const rows = EXTRACT_ROWS_FN(plots, signalSchema);
|
|
2368
|
+
await mkdir(dumpDir, { recursive: true });
|
|
2369
|
+
await writeFile(join(dumpDir, `${dumpName}.jsonl`), rows.map((r) => JSON.stringify(r)).join("\n"), "utf-8");
|
|
2370
|
+
return;
|
|
2371
|
+
}
|
|
2372
|
+
if (values.markdown) {
|
|
2373
|
+
await mkdir(dumpDir, { recursive: true });
|
|
2374
|
+
await writeFile(join(dumpDir, `${dumpName}.md`), await toMarkdown(signalId, plots, signalSchema), "utf-8");
|
|
2375
|
+
return;
|
|
2376
|
+
}
|
|
2377
|
+
console.log(await toMarkdown(signalId, plots, signalSchema));
|
|
2378
|
+
};
|
|
2235
2379
|
main();
|
|
2236
2380
|
|
|
2237
2381
|
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.1",
|
|
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 {
|