@backtest-kit/cli 5.10.2 → 5.11.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 +90 -0
- package/build/index.cjs +127 -25
- package/build/index.mjs +129 -27
- package/package.json +13 -13
- package/template/project/CLAUDE.md +158 -0
- package/template/project/content/feb_2026.strategy.ts +114 -0
- package/template/project/docs/backtest_actions.md +158 -0
- package/template/project/docs/backtest_graph_multiple_outputs.md +137 -0
- package/template/project/docs/backtest_graph_pattern.md +235 -0
- package/template/project/docs/backtest_logging_jsonl.md +246 -0
- package/template/project/docs/backtest_pinets_usage.md +190 -0
- package/template/project/docs/backtest_risk_async.md +153 -0
- package/template/project/docs/backtest_strategy_structure.md +250 -0
- package/template/project/docs/pine_debug.md +174 -0
- package/template/project/docs/pine_indicator_warmup.md +88 -0
- package/template/project/gitignore.mustache +30 -0
- package/template/project/math/feb_2026.pine +120 -0
- package/template/project/modules/dump.module.ts +37 -0
- package/template/project/modules/pine.module.ts +37 -0
- package/template/project/package.mustache +27 -0
- package/template/project/report/feb_2026.md +78 -0
- package/template/project/scripts/fetch_docs.mjs +58 -0
- /package/template/project/{.gitkeep → docs/lib/.gitkeep} +0 -0
package/README.md
CHANGED
|
@@ -38,6 +38,9 @@ Point the CLI at your strategy file, choose a mode, and it handles exchange conn
|
|
|
38
38
|
| **Live** | `--live` | Real trades via exchange API |
|
|
39
39
|
| **UI Dashboard** | `--ui` | Web dashboard at `http://localhost:60050` |
|
|
40
40
|
| **Telegram** | `--telegram` | Trade notifications with price charts |
|
|
41
|
+
| **PineScript** | `--pine` | Run a local `.pine` indicator against exchange data |
|
|
42
|
+
| **Candle Dump** | `--dump` | Fetch and save raw OHLCV candles to a file |
|
|
43
|
+
| **Init Project** | `--init` | Scaffold a new backtest-kit project |
|
|
41
44
|
|
|
42
45
|
## 🚀 Installation
|
|
43
46
|
|
|
@@ -705,6 +708,93 @@ Or add it to `package.json`:
|
|
|
705
708
|
npx @backtest-kit/cli --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl
|
|
706
709
|
```
|
|
707
710
|
|
|
711
|
+
## 🗂️ Scaffolding a New Project (`--init`)
|
|
712
|
+
|
|
713
|
+
`@backtest-kit/cli` can bootstrap a ready-to-use project directory with a pre-configured layout, example strategy files, and all documentation fetched automatically.
|
|
714
|
+
|
|
715
|
+
### CLI Flags
|
|
716
|
+
|
|
717
|
+
| Flag | Type | Description |
|
|
718
|
+
|------|------|-------------|
|
|
719
|
+
| `--init` | boolean | Scaffold a new project |
|
|
720
|
+
| `--output` | string | Target directory name (default: `backtest-kit-project`) |
|
|
721
|
+
|
|
722
|
+
### Usage
|
|
723
|
+
|
|
724
|
+
```bash
|
|
725
|
+
npx @backtest-kit/cli --init
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
Creates `./backtest-kit-project/` in the current working directory.
|
|
729
|
+
|
|
730
|
+
Override the directory name with `--output`:
|
|
731
|
+
|
|
732
|
+
```bash
|
|
733
|
+
npx @backtest-kit/cli --init --output my-trading-bot
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
Creates `./my-trading-bot/`.
|
|
737
|
+
|
|
738
|
+
The target directory must not exist or must be empty — the command aborts if it contains any files.
|
|
739
|
+
|
|
740
|
+
### Generated Project Structure
|
|
741
|
+
|
|
742
|
+
```
|
|
743
|
+
backtest-kit-project/
|
|
744
|
+
├── package.json # pre-configured with all backtest-kit dependencies
|
|
745
|
+
├── .gitignore
|
|
746
|
+
├── CLAUDE.md # AI-agent guide for writing strategies
|
|
747
|
+
├── content/
|
|
748
|
+
│ └── feb_2026.strategy.ts # example strategy entry point
|
|
749
|
+
├── docs/
|
|
750
|
+
│ ├── lib/ # fetched automatically (see below)
|
|
751
|
+
│ ├── backtest_actions.md
|
|
752
|
+
│ ├── backtest_graph_pattern.md
|
|
753
|
+
│ ├── backtest_logging_jsonl.md
|
|
754
|
+
│ ├── backtest_pinets_usage.md
|
|
755
|
+
│ ├── backtest_risk_async.md
|
|
756
|
+
│ ├── backtest_strategy_structure.md
|
|
757
|
+
│ ├── pine_debug.md
|
|
758
|
+
│ └── pine_indicator_warmup.md
|
|
759
|
+
├── math/
|
|
760
|
+
│ └── feb_2026.pine # example PineScript indicator
|
|
761
|
+
├── modules/
|
|
762
|
+
│ ├── dump.module.ts # exchange schema for --dump mode
|
|
763
|
+
│ └── pine.module.ts # exchange schema for --pine mode
|
|
764
|
+
├── report/
|
|
765
|
+
│ └── feb_2026.md # example strategy research report
|
|
766
|
+
└── scripts/
|
|
767
|
+
└── fetch_docs.mjs # utility: downloads library READMEs into docs/lib/
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### Automatic Documentation Fetch
|
|
771
|
+
|
|
772
|
+
After scaffolding, the CLI immediately runs `scripts/fetch_docs.mjs` inside the new project, which downloads the latest README files for all bundled libraries into `docs/lib/`:
|
|
773
|
+
|
|
774
|
+
| File | Source |
|
|
775
|
+
|------|--------|
|
|
776
|
+
| `backtest-kit.md` | `backtest-kit` README |
|
|
777
|
+
| `backtest-kit__graph.md` | `@backtest-kit/graph` README |
|
|
778
|
+
| `backtest-kit__pinets.md` | `@backtest-kit/pinets` README |
|
|
779
|
+
| `backtest-kit__cli.md` | `@backtest-kit/cli` README |
|
|
780
|
+
| `garch.md` | `garch` README |
|
|
781
|
+
| `volume-anomaly.md` | `volume-anomaly` README |
|
|
782
|
+
| `agent-swarm-kit.md` | `agent-swarm-kit` README |
|
|
783
|
+
| `functools-kit.md` | `functools-kit` README |
|
|
784
|
+
|
|
785
|
+
You can re-run this script at any time to refresh the docs:
|
|
786
|
+
|
|
787
|
+
```bash
|
|
788
|
+
cd backtest-kit-project
|
|
789
|
+
node ./scripts/fetch_docs.mjs
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
Or via the pre-configured npm script:
|
|
793
|
+
|
|
794
|
+
```bash
|
|
795
|
+
npm run sync:lib
|
|
796
|
+
```
|
|
797
|
+
|
|
708
798
|
## 🌍 Environment Variables
|
|
709
799
|
|
|
710
800
|
Create a `.env` file in your project root:
|
package/build/index.cjs
CHANGED
|
@@ -29,6 +29,7 @@ var BacktestKitGraph = require('@backtest-kit/graph');
|
|
|
29
29
|
var BacktestKitOllama = require('@backtest-kit/ollama');
|
|
30
30
|
var BacktestKitPinets = require('@backtest-kit/pinets');
|
|
31
31
|
var BacktestKitSignals = require('@backtest-kit/signals');
|
|
32
|
+
var child_process = require('child_process');
|
|
32
33
|
|
|
33
34
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
34
35
|
function _interopNamespaceDefault(e) {
|
|
@@ -118,6 +119,7 @@ BacktestKit.setConfig({
|
|
|
118
119
|
BacktestKit.setConfig({
|
|
119
120
|
CC_ENABLE_DCA_EVERYWHERE: true,
|
|
120
121
|
CC_ENABLE_PPPL_EVERYWHERE: true,
|
|
122
|
+
CC_ENABLE_TRAILING_EVERYWHERE: true,
|
|
121
123
|
});
|
|
122
124
|
BacktestKit.setConfig({
|
|
123
125
|
CC_MAX_SIGNAL_GENERATION_SECONDS: 15 * 60,
|
|
@@ -283,15 +285,15 @@ const TYPES = {
|
|
|
283
285
|
|
|
284
286
|
const entrySubject = new functoolsKit.BehaviorSubject();
|
|
285
287
|
|
|
286
|
-
const __filename$
|
|
287
|
-
const __dirname$
|
|
288
|
+
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)));
|
|
289
|
+
const __dirname$2 = path.dirname(__filename$2);
|
|
288
290
|
let _is_launched = false;
|
|
289
291
|
class ResolveService {
|
|
290
292
|
constructor() {
|
|
291
293
|
this.loggerService = inject(TYPES.loggerService);
|
|
292
294
|
this.loaderService = inject(TYPES.loaderService);
|
|
293
|
-
this.DEFAULT_TEMPLATE_DIR = path.resolve(__dirname$
|
|
294
|
-
this.DEFAULT_MODULES_DIR = path.resolve(__dirname$
|
|
295
|
+
this.DEFAULT_TEMPLATE_DIR = path.resolve(__dirname$2, '..', 'template');
|
|
296
|
+
this.DEFAULT_MODULES_DIR = path.resolve(__dirname$2, '..', 'modules');
|
|
295
297
|
this.OVERRIDE_TEMPLATE_DIR = path.resolve(process.cwd(), 'template');
|
|
296
298
|
this.OVERRIDE_MODULES_DIR = path.resolve(process.cwd(), 'modules');
|
|
297
299
|
this.getIsLaunched = () => {
|
|
@@ -445,6 +447,18 @@ var FrameName;
|
|
|
445
447
|
})(FrameName || (FrameName = {}));
|
|
446
448
|
var FrameName$1 = FrameName;
|
|
447
449
|
|
|
450
|
+
const ALLOWED_EXTENSIONS = [
|
|
451
|
+
`.cjs`,
|
|
452
|
+
`.mjs`,
|
|
453
|
+
`.ts`,
|
|
454
|
+
`.tsx`,
|
|
455
|
+
`.js`,
|
|
456
|
+
`.pine`,
|
|
457
|
+
];
|
|
458
|
+
const DISALLOWED_PATHS = [
|
|
459
|
+
"node_modules",
|
|
460
|
+
"@backtest-kit",
|
|
461
|
+
];
|
|
448
462
|
const getArgs = functoolsKit.singleshot(() => {
|
|
449
463
|
const { values, positionals } = util.parseArgs({
|
|
450
464
|
args: process.argv,
|
|
@@ -539,6 +553,10 @@ const getArgs = functoolsKit.singleshot(() => {
|
|
|
539
553
|
type: "boolean",
|
|
540
554
|
default: false,
|
|
541
555
|
},
|
|
556
|
+
init: {
|
|
557
|
+
type: "boolean",
|
|
558
|
+
default: false,
|
|
559
|
+
},
|
|
542
560
|
},
|
|
543
561
|
strict: false,
|
|
544
562
|
allowPositionals: true,
|
|
@@ -548,6 +566,13 @@ const getArgs = functoolsKit.singleshot(() => {
|
|
|
548
566
|
positionals,
|
|
549
567
|
};
|
|
550
568
|
});
|
|
569
|
+
const getPositional = functoolsKit.singleshot(() => {
|
|
570
|
+
const { positionals = [] } = getArgs();
|
|
571
|
+
const result = positionals
|
|
572
|
+
.filter((value) => !DISALLOWED_PATHS.some((path) => value.includes(path)))
|
|
573
|
+
.find((value) => ALLOWED_EXTENSIONS.some((ext) => value.endsWith(ext)));
|
|
574
|
+
return result || null;
|
|
575
|
+
});
|
|
551
576
|
|
|
552
577
|
const ADD_FRAME_FN = (self) => {
|
|
553
578
|
self.loggerService.log("Adding February 2024 as a default frame schema");
|
|
@@ -733,11 +758,11 @@ class BacktestMainService {
|
|
|
733
758
|
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)))) {
|
|
734
759
|
return;
|
|
735
760
|
}
|
|
736
|
-
const { values
|
|
761
|
+
const { values } = getArgs();
|
|
737
762
|
if (!values.backtest) {
|
|
738
763
|
return;
|
|
739
764
|
}
|
|
740
|
-
const
|
|
765
|
+
const entryPoint = getPositional();
|
|
741
766
|
if (!entryPoint) {
|
|
742
767
|
throw new Error("Entry point is required");
|
|
743
768
|
}
|
|
@@ -812,11 +837,11 @@ class LiveMainService {
|
|
|
812
837
|
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)))) {
|
|
813
838
|
return;
|
|
814
839
|
}
|
|
815
|
-
const { values
|
|
840
|
+
const { values } = getArgs();
|
|
816
841
|
if (!values.live) {
|
|
817
842
|
return;
|
|
818
843
|
}
|
|
819
|
-
const
|
|
844
|
+
const entryPoint = getPositional();
|
|
820
845
|
if (!entryPoint) {
|
|
821
846
|
throw new Error("Entry point is required");
|
|
822
847
|
}
|
|
@@ -885,11 +910,11 @@ class PaperMainService {
|
|
|
885
910
|
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)))) {
|
|
886
911
|
return;
|
|
887
912
|
}
|
|
888
|
-
const { values
|
|
913
|
+
const { values } = getArgs();
|
|
889
914
|
if (!values.paper) {
|
|
890
915
|
return;
|
|
891
916
|
}
|
|
892
|
-
const
|
|
917
|
+
const entryPoint = getPositional();
|
|
893
918
|
if (!entryPoint) {
|
|
894
919
|
throw new Error("Entry point is required");
|
|
895
920
|
}
|
|
@@ -2206,7 +2231,7 @@ const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
|
|
|
2206
2231
|
const listenGracefulShutdown$4 = functoolsKit.singleshot(() => {
|
|
2207
2232
|
process.on("SIGINT", BEFORE_EXIT_FN$4);
|
|
2208
2233
|
});
|
|
2209
|
-
const main$
|
|
2234
|
+
const main$7 = async () => {
|
|
2210
2235
|
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)))) {
|
|
2211
2236
|
return;
|
|
2212
2237
|
}
|
|
@@ -2217,7 +2242,7 @@ const main$6 = async () => {
|
|
|
2217
2242
|
await cli.backtestMainService.connect();
|
|
2218
2243
|
listenGracefulShutdown$4();
|
|
2219
2244
|
};
|
|
2220
|
-
main$
|
|
2245
|
+
main$7();
|
|
2221
2246
|
|
|
2222
2247
|
const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
|
|
2223
2248
|
process.off("SIGINT", BEFORE_EXIT_FN$3);
|
|
@@ -2238,7 +2263,7 @@ const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
|
|
|
2238
2263
|
const listenGracefulShutdown$3 = functoolsKit.singleshot(() => {
|
|
2239
2264
|
process.on("SIGINT", BEFORE_EXIT_FN$3);
|
|
2240
2265
|
});
|
|
2241
|
-
const main$
|
|
2266
|
+
const main$6 = async () => {
|
|
2242
2267
|
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)))) {
|
|
2243
2268
|
return;
|
|
2244
2269
|
}
|
|
@@ -2249,7 +2274,7 @@ const main$5 = async () => {
|
|
|
2249
2274
|
cli.paperMainService.connect();
|
|
2250
2275
|
listenGracefulShutdown$3();
|
|
2251
2276
|
};
|
|
2252
|
-
main$
|
|
2277
|
+
main$6();
|
|
2253
2278
|
|
|
2254
2279
|
const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
|
|
2255
2280
|
process.off("SIGINT", BEFORE_EXIT_FN$2);
|
|
@@ -2270,7 +2295,7 @@ const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
|
|
|
2270
2295
|
const listenGracefulShutdown$2 = functoolsKit.singleshot(() => {
|
|
2271
2296
|
process.on("SIGINT", BEFORE_EXIT_FN$2);
|
|
2272
2297
|
});
|
|
2273
|
-
const main$
|
|
2298
|
+
const main$5 = async () => {
|
|
2274
2299
|
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)))) {
|
|
2275
2300
|
return;
|
|
2276
2301
|
}
|
|
@@ -2281,7 +2306,7 @@ const main$4 = async () => {
|
|
|
2281
2306
|
await cli.liveMainService.connect();
|
|
2282
2307
|
listenGracefulShutdown$2();
|
|
2283
2308
|
};
|
|
2284
|
-
main$
|
|
2309
|
+
main$5();
|
|
2285
2310
|
|
|
2286
2311
|
const BEFORE_EXIT_FN$1 = functoolsKit.singleshot(async () => {
|
|
2287
2312
|
process.off("SIGINT", BEFORE_EXIT_FN$1);
|
|
@@ -2291,7 +2316,7 @@ const BEFORE_EXIT_FN$1 = functoolsKit.singleshot(async () => {
|
|
|
2291
2316
|
const listenGracefulShutdown$1 = functoolsKit.singleshot(() => {
|
|
2292
2317
|
process.on("SIGINT", BEFORE_EXIT_FN$1);
|
|
2293
2318
|
});
|
|
2294
|
-
const main$
|
|
2319
|
+
const main$4 = async () => {
|
|
2295
2320
|
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)))) {
|
|
2296
2321
|
return;
|
|
2297
2322
|
}
|
|
@@ -2301,7 +2326,7 @@ const main$3 = async () => {
|
|
|
2301
2326
|
}
|
|
2302
2327
|
listenGracefulShutdown$1();
|
|
2303
2328
|
};
|
|
2304
|
-
main$
|
|
2329
|
+
main$4();
|
|
2305
2330
|
|
|
2306
2331
|
const BEFORE_EXIT_FN = functoolsKit.singleshot(async () => {
|
|
2307
2332
|
process.off("SIGINT", BEFORE_EXIT_FN);
|
|
@@ -2311,7 +2336,7 @@ const BEFORE_EXIT_FN = functoolsKit.singleshot(async () => {
|
|
|
2311
2336
|
const listenGracefulShutdown = functoolsKit.singleshot(() => {
|
|
2312
2337
|
process.on("SIGINT", BEFORE_EXIT_FN);
|
|
2313
2338
|
});
|
|
2314
|
-
const main$
|
|
2339
|
+
const main$3 = async () => {
|
|
2315
2340
|
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)))) {
|
|
2316
2341
|
return;
|
|
2317
2342
|
}
|
|
@@ -2321,7 +2346,7 @@ const main$2 = async () => {
|
|
|
2321
2346
|
}
|
|
2322
2347
|
listenGracefulShutdown();
|
|
2323
2348
|
};
|
|
2324
|
-
main$
|
|
2349
|
+
main$3();
|
|
2325
2350
|
|
|
2326
2351
|
const EXTRACT_ROWS_FN = (plots, schema) => {
|
|
2327
2352
|
const keys = Object.keys(schema);
|
|
@@ -2343,15 +2368,15 @@ const EXTRACT_ROWS_FN = (plots, schema) => {
|
|
|
2343
2368
|
}
|
|
2344
2369
|
return rows;
|
|
2345
2370
|
};
|
|
2346
|
-
const main$
|
|
2371
|
+
const main$2 = async () => {
|
|
2347
2372
|
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)))) {
|
|
2348
2373
|
return;
|
|
2349
2374
|
}
|
|
2350
|
-
const { values
|
|
2375
|
+
const { values } = getArgs();
|
|
2351
2376
|
if (!values.pine) {
|
|
2352
2377
|
return;
|
|
2353
2378
|
}
|
|
2354
|
-
const
|
|
2379
|
+
const entryPoint = getPositional();
|
|
2355
2380
|
if (!entryPoint) {
|
|
2356
2381
|
return;
|
|
2357
2382
|
}
|
|
@@ -2415,9 +2440,9 @@ const main$1 = async () => {
|
|
|
2415
2440
|
}
|
|
2416
2441
|
console.log(await BacktestKitPinets.toMarkdown(signalId, plots, signalSchema));
|
|
2417
2442
|
};
|
|
2418
|
-
main$
|
|
2443
|
+
main$2();
|
|
2419
2444
|
|
|
2420
|
-
const main = async () => {
|
|
2445
|
+
const main$1 = async () => {
|
|
2421
2446
|
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)))) {
|
|
2422
2447
|
return;
|
|
2423
2448
|
}
|
|
@@ -2462,6 +2487,83 @@ const main = async () => {
|
|
|
2462
2487
|
}
|
|
2463
2488
|
console.log(JSON.stringify(candles, null, 2));
|
|
2464
2489
|
};
|
|
2490
|
+
main$1();
|
|
2491
|
+
|
|
2492
|
+
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)));
|
|
2493
|
+
const __dirname$1 = path.dirname(__filename$1);
|
|
2494
|
+
const MUSTACHE_EXT = ".mustache";
|
|
2495
|
+
async function isDirEmpty(dirPath) {
|
|
2496
|
+
try {
|
|
2497
|
+
const files = await fs$1.readdir(dirPath);
|
|
2498
|
+
return files.length === 0;
|
|
2499
|
+
}
|
|
2500
|
+
catch (error) {
|
|
2501
|
+
if (error.code === "ENOENT") {
|
|
2502
|
+
return true;
|
|
2503
|
+
}
|
|
2504
|
+
throw error;
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
async function copyDir(srcDir, destDir, data) {
|
|
2508
|
+
await fs$1.mkdir(destDir, { recursive: true });
|
|
2509
|
+
const entries = await fs$1.readdir(srcDir, { withFileTypes: true });
|
|
2510
|
+
for (const entry of entries) {
|
|
2511
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
2512
|
+
if (entry.isDirectory()) {
|
|
2513
|
+
await copyDir(srcPath, path.join(destDir, entry.name), data);
|
|
2514
|
+
continue;
|
|
2515
|
+
}
|
|
2516
|
+
if (entry.name.endsWith(MUSTACHE_EXT)) {
|
|
2517
|
+
const destName = entry.name.slice(0, -MUSTACHE_EXT.length);
|
|
2518
|
+
const destPath = path.join(destDir, destName);
|
|
2519
|
+
const template = await fs$1.readFile(srcPath, "utf-8");
|
|
2520
|
+
const rendered = Mustache.render(template, data);
|
|
2521
|
+
await fs$1.writeFile(destPath, rendered, "utf-8");
|
|
2522
|
+
console.log(` -> ${destPath}`);
|
|
2523
|
+
}
|
|
2524
|
+
else {
|
|
2525
|
+
const destPath = path.join(destDir, entry.name);
|
|
2526
|
+
await fs$1.copyFile(srcPath, destPath);
|
|
2527
|
+
console.log(` -> ${destPath}`);
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
function runScript(scriptPath, cwd) {
|
|
2532
|
+
return new Promise((resolve, reject) => {
|
|
2533
|
+
const node = process.execPath;
|
|
2534
|
+
const child = child_process.spawn(node, [scriptPath], { cwd, stdio: "inherit" });
|
|
2535
|
+
child.on("close", (code) => {
|
|
2536
|
+
if (code !== 0) {
|
|
2537
|
+
reject(new Error(`Script exited with code ${code}`));
|
|
2538
|
+
return;
|
|
2539
|
+
}
|
|
2540
|
+
resolve();
|
|
2541
|
+
});
|
|
2542
|
+
child.on("error", reject);
|
|
2543
|
+
});
|
|
2544
|
+
}
|
|
2545
|
+
const main = async () => {
|
|
2546
|
+
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)))) {
|
|
2547
|
+
return;
|
|
2548
|
+
}
|
|
2549
|
+
const { values } = getArgs();
|
|
2550
|
+
if (!values.init) {
|
|
2551
|
+
return;
|
|
2552
|
+
}
|
|
2553
|
+
const projectName = values.output || "backtest-kit-project";
|
|
2554
|
+
const projectPath = path.join(process.cwd(), projectName);
|
|
2555
|
+
const templatePath = path.join(__dirname$1, "../../template/project");
|
|
2556
|
+
const isEmpty = await isDirEmpty(projectPath);
|
|
2557
|
+
if (!isEmpty) {
|
|
2558
|
+
console.error(`Directory "${projectName}" already exists and is not empty.`);
|
|
2559
|
+
process.exit(1);
|
|
2560
|
+
}
|
|
2561
|
+
console.log(`Creating project in ${projectPath}`);
|
|
2562
|
+
await copyDir(templatePath, projectPath, { PROJECT_NAME: projectName });
|
|
2563
|
+
console.log(`Fetching docs...`);
|
|
2564
|
+
await runScript(path.join(projectPath, "scripts/fetch_docs.mjs"), projectPath);
|
|
2565
|
+
console.log(`Done! Project created at ${projectPath}`);
|
|
2566
|
+
};
|
|
2465
2567
|
main();
|
|
2466
2568
|
|
|
2467
2569
|
function setLogger(logger) {
|