@backtest-kit/cli 6.7.0 → 6.7.2

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 CHANGED
@@ -341,6 +341,67 @@ npm run backtest:dec
341
341
 
342
342
  Each strategy run produces its own `dump/` directory, making it straightforward to compare results across time periods — both by inspection and by pointing an AI agent at a specific strategy folder.
343
343
 
344
+ ## 🔗 Shared Import Aliases
345
+
346
+ `@backtest-kit/cli` automatically turns every **top-level folder in `cwd`** into a bare import alias available inside any strategy file. No configuration needed — just create the folder.
347
+
348
+ ### How It Works
349
+
350
+ When the CLI loads a strategy file, it scans the current working directory for subdirectories and registers each one as an import alias. The alias name is the folder name. Both barrel imports and deep subpath imports are supported:
351
+
352
+ | Import | Resolves to |
353
+ |--------|-------------|
354
+ | `import { fn } from "utils"` | `<cwd>/utils/index.ts` (or `.js`, `.mjs`, `.cjs`) |
355
+ | `import { calcRSI } from "math/rsi"` | `<cwd>/math/rsi.ts` |
356
+ | `import { research } from "logic"` | `<cwd>/logic/index.ts` |
357
+ | `import { ResearchResponseContract } from "logic/contract/ResearchResponse.contract"` | `<cwd>/logic/contract/ResearchResponse.contract.ts` |
358
+
359
+ ### Project Structure
360
+
361
+ ```
362
+ my-project/
363
+ ├── utils/ ← import { formatDate } from "utils"
364
+ │ └── index.ts
365
+ ├── math/ ← import { calcRSI } from "math/rsi"
366
+ │ └── rsi.ts
367
+ ├── logic/ ← import { research } from "logic"
368
+ │ ├── index.ts ← barrel
369
+ │ └── contract/
370
+ │ └── ResearchResponse.contract.ts ← import { ... } from "logic/contract/ResearchResponse.contract"
371
+ └── content/
372
+ ├── feb_2026.strategy.ts ← uses all three aliases freely
373
+ └── mar_2026.strategy.ts ← same aliases, no duplication
374
+ ```
375
+
376
+ This lets you extract shared utilities, math helpers, or AI agent logic (e.g. `agent-swarm-kit` workflows) into named folders and reuse them across every strategy in the project without relative path hell.
377
+
378
+ ### TypeScript Support
379
+
380
+ Add a matching `paths` entry to your `tsconfig.json` so the editor resolves the aliases:
381
+
382
+ ```json
383
+ {
384
+ "compilerOptions": {
385
+ "moduleResolution": "bundler",
386
+ "paths": {
387
+ "logic": ["./logic/index.ts"],
388
+ "logic/*": ["./logic/*"],
389
+ "math": ["./math/index.ts"],
390
+ "math/*": ["./math/*"],
391
+ "utils": ["./utils/index.ts"],
392
+ "utils/*": ["./utils/*"]
393
+ }
394
+ },
395
+ "include": [
396
+ "./logic",
397
+ "./math",
398
+ "./utils",
399
+ "./content",
400
+ "./modules",
401
+ ],
402
+ }
403
+ ```
404
+
344
405
  ## 🔔 Integrations
345
406
 
346
407
  ### Web Dashboard (`--ui`)
package/build/index.cjs CHANGED
@@ -239,6 +239,7 @@ class ResolveService {
239
239
  this.DEFAULT_MODULES_DIR = path.resolve(__dirname$2, '..', 'modules');
240
240
  this.OVERRIDE_TEMPLATE_DIR = path.resolve(process.cwd(), 'template');
241
241
  this.OVERRIDE_MODULES_DIR = path.resolve(process.cwd(), 'modules');
242
+ this.IMPORT_PATHS_DIR = process.cwd();
242
243
  this.getIsLaunched = () => {
243
244
  this.loggerService.log("resolveService getIsLaunched");
244
245
  return _is_launched;
@@ -2107,6 +2108,8 @@ class BabelService {
2107
2108
  }
2108
2109
  }
2109
2110
 
2111
+ const USE_ESMODULE_DEFAULT = false;
2112
+ const IMPORT_PATHS_EXCLUDE = new Set(["dump", "logs", "node_modules"]);
2110
2113
  const TRANSPILE_FN = functoolsKit.memoize(([path]) => `${path}`, (path, code, self, require) => {
2111
2114
  const __filename = self.__filename;
2112
2115
  const __dirname = self.__dirname;
@@ -2142,6 +2145,9 @@ const BABEL_ENTRY_FACTORY = (filePath, self, seen) => {
2142
2145
  const code = fs.readFileSync(resolvedPath, "utf-8");
2143
2146
  const child = self.fork(path.dirname(resolvedPath));
2144
2147
  const { module } = TRANSPILE_FN(resolvedPath, code, child, CREATE_BASE_REQUIRE_FN(child, seen));
2148
+ if (!USE_ESMODULE_DEFAULT) {
2149
+ return module.exports;
2150
+ }
2145
2151
  return "default" in module.exports
2146
2152
  ? module.exports.default
2147
2153
  : module.exports;
@@ -2202,6 +2208,20 @@ const ENTRY_FACTORY = (filePath, self, seen) => {
2202
2208
  }
2203
2209
  throw new Error(`Failed to load module at ${filePath} (basepath: ${self.params.path})`);
2204
2210
  };
2211
+ const READ_IMPORT_PATHS_MAP_FN = functoolsKit.singleshot((importPathsDir) => {
2212
+ const entries = fs.readdirSync(importPathsDir, { withFileTypes: true });
2213
+ const map = {};
2214
+ for (const entry of entries) {
2215
+ if (!entry.isDirectory()) {
2216
+ continue;
2217
+ }
2218
+ if (IMPORT_PATHS_EXCLUDE.has(entry.name)) {
2219
+ continue;
2220
+ }
2221
+ map[entry.name] = path.join(importPathsDir, entry.name);
2222
+ }
2223
+ return map;
2224
+ });
2205
2225
  const CREATE_BASE_REQUIRE_FN = (self, seen) => {
2206
2226
  const baseRequire = self.baseRequire();
2207
2227
  return new Proxy(baseRequire, {
@@ -2226,6 +2246,19 @@ const CREATE_BASE_REQUIRE_FN = (self, seen) => {
2226
2246
  const child = self.fork(path.dirname(resolved));
2227
2247
  return child.import(resolved, seen);
2228
2248
  }
2249
+ const importPathsMap = READ_IMPORT_PATHS_MAP_FN(self.params.resolve.IMPORT_PATHS_DIR);
2250
+ if (id in importPathsMap) {
2251
+ const resolved = importPathsMap[id];
2252
+ const child = self.fork(resolved);
2253
+ return child.import(resolved, seen);
2254
+ }
2255
+ const importPathsKey = Object.keys(importPathsMap).find((key) => id === key || id.startsWith(`${key}/`));
2256
+ if (importPathsKey) {
2257
+ const subPath = id.slice(importPathsKey.length);
2258
+ const resolved = path.join(importPathsMap[importPathsKey], subPath);
2259
+ const child = self.fork(path.dirname(resolved));
2260
+ return child.import(resolved, seen);
2261
+ }
2229
2262
  return baseRequire(id);
2230
2263
  },
2231
2264
  });
@@ -2256,6 +2289,7 @@ class ClientLoader {
2256
2289
  path: basePath,
2257
2290
  babel: this.params.babel,
2258
2291
  logger: this.params.logger,
2292
+ resolve: this.params.resolve,
2259
2293
  });
2260
2294
  }
2261
2295
  import(filePath, seen = new Set()) {
@@ -2300,9 +2334,11 @@ class LoaderService {
2300
2334
  constructor() {
2301
2335
  this.babelService = inject(TYPES.babelService);
2302
2336
  this.loggerService = inject(TYPES.loggerService);
2337
+ this.resolveService = inject(TYPES.resolveService);
2303
2338
  this.getInstance = functoolsKit.memoize(([basePath]) => `${basePath}`, (basePath) => new ClientLoader({
2304
2339
  babel: this.babelService,
2305
2340
  logger: this.loggerService,
2341
+ resolve: this.resolveService,
2306
2342
  path: basePath,
2307
2343
  }));
2308
2344
  this.import = (filePath, basePath = process.cwd()) => {
@@ -2537,7 +2573,7 @@ const main$b = async () => {
2537
2573
  if (MODES.some((mode) => values[mode])) {
2538
2574
  return;
2539
2575
  }
2540
- process.stdout.write(`@backtest-kit/cli ${"6.7.0"}\n`);
2576
+ process.stdout.write(`@backtest-kit/cli ${"6.7.2"}\n`);
2541
2577
  process.stdout.write("\n");
2542
2578
  process.stdout.write(`Run with --help to see available commands.\n`);
2543
2579
  process.stdout.write("\n");
@@ -3105,7 +3141,7 @@ const main$1 = async () => {
3105
3141
  if (!values.help) {
3106
3142
  return;
3107
3143
  }
3108
- process.stdout.write(`@backtest-kit/cli ${"6.7.0"}\n\n`);
3144
+ process.stdout.write(`@backtest-kit/cli ${"6.7.2"}\n\n`);
3109
3145
  process.stdout.write(HELP_TEXT);
3110
3146
  process.exit(0);
3111
3147
  };
@@ -3119,7 +3155,7 @@ const main = async () => {
3119
3155
  if (!values.version) {
3120
3156
  return;
3121
3157
  }
3122
- process.stdout.write(`@backtest-kit/cli ${"6.7.0"}\n`);
3158
+ process.stdout.write(`@backtest-kit/cli ${"6.7.2"}\n`);
3123
3159
  process.exit(0);
3124
3160
  };
3125
3161
  main();
package/build/index.mjs CHANGED
@@ -214,6 +214,7 @@ class ResolveService {
214
214
  this.DEFAULT_MODULES_DIR = path.resolve(__dirname$1, '..', 'modules');
215
215
  this.OVERRIDE_TEMPLATE_DIR = path.resolve(process.cwd(), 'template');
216
216
  this.OVERRIDE_MODULES_DIR = path.resolve(process.cwd(), 'modules');
217
+ this.IMPORT_PATHS_DIR = process.cwd();
217
218
  this.getIsLaunched = () => {
218
219
  this.loggerService.log("resolveService getIsLaunched");
219
220
  return _is_launched;
@@ -2082,6 +2083,8 @@ class BabelService {
2082
2083
  }
2083
2084
  }
2084
2085
 
2086
+ const USE_ESMODULE_DEFAULT = false;
2087
+ const IMPORT_PATHS_EXCLUDE = new Set(["dump", "logs", "node_modules"]);
2085
2088
  const TRANSPILE_FN = memoize(([path]) => `${path}`, (path, code, self, require) => {
2086
2089
  const __filename = self.__filename;
2087
2090
  const __dirname = self.__dirname;
@@ -2113,6 +2116,9 @@ const BABEL_ENTRY_FACTORY = (filePath, self, seen) => {
2113
2116
  const code = fs.readFileSync(resolvedPath, "utf-8");
2114
2117
  const child = self.fork(path.dirname(resolvedPath));
2115
2118
  const { module } = TRANSPILE_FN(resolvedPath, code, child, CREATE_BASE_REQUIRE_FN(child, seen));
2119
+ if (!USE_ESMODULE_DEFAULT) {
2120
+ return module.exports;
2121
+ }
2116
2122
  return "default" in module.exports
2117
2123
  ? module.exports.default
2118
2124
  : module.exports;
@@ -2173,6 +2179,20 @@ const ENTRY_FACTORY = (filePath, self, seen) => {
2173
2179
  }
2174
2180
  throw new Error(`Failed to load module at ${filePath} (basepath: ${self.params.path})`);
2175
2181
  };
2182
+ const READ_IMPORT_PATHS_MAP_FN = singleshot((importPathsDir) => {
2183
+ const entries = fs.readdirSync(importPathsDir, { withFileTypes: true });
2184
+ const map = {};
2185
+ for (const entry of entries) {
2186
+ if (!entry.isDirectory()) {
2187
+ continue;
2188
+ }
2189
+ if (IMPORT_PATHS_EXCLUDE.has(entry.name)) {
2190
+ continue;
2191
+ }
2192
+ map[entry.name] = path.join(importPathsDir, entry.name);
2193
+ }
2194
+ return map;
2195
+ });
2176
2196
  const CREATE_BASE_REQUIRE_FN = (self, seen) => {
2177
2197
  const baseRequire = self.baseRequire();
2178
2198
  return new Proxy(baseRequire, {
@@ -2197,6 +2217,19 @@ const CREATE_BASE_REQUIRE_FN = (self, seen) => {
2197
2217
  const child = self.fork(path.dirname(resolved));
2198
2218
  return child.import(resolved, seen);
2199
2219
  }
2220
+ const importPathsMap = READ_IMPORT_PATHS_MAP_FN(self.params.resolve.IMPORT_PATHS_DIR);
2221
+ if (id in importPathsMap) {
2222
+ const resolved = importPathsMap[id];
2223
+ const child = self.fork(resolved);
2224
+ return child.import(resolved, seen);
2225
+ }
2226
+ const importPathsKey = Object.keys(importPathsMap).find((key) => id === key || id.startsWith(`${key}/`));
2227
+ if (importPathsKey) {
2228
+ const subPath = id.slice(importPathsKey.length);
2229
+ const resolved = path.join(importPathsMap[importPathsKey], subPath);
2230
+ const child = self.fork(path.dirname(resolved));
2231
+ return child.import(resolved, seen);
2232
+ }
2200
2233
  return baseRequire(id);
2201
2234
  },
2202
2235
  });
@@ -2227,6 +2260,7 @@ class ClientLoader {
2227
2260
  path: basePath,
2228
2261
  babel: this.params.babel,
2229
2262
  logger: this.params.logger,
2263
+ resolve: this.params.resolve,
2230
2264
  });
2231
2265
  }
2232
2266
  import(filePath, seen = new Set()) {
@@ -2271,9 +2305,11 @@ class LoaderService {
2271
2305
  constructor() {
2272
2306
  this.babelService = inject(TYPES.babelService);
2273
2307
  this.loggerService = inject(TYPES.loggerService);
2308
+ this.resolveService = inject(TYPES.resolveService);
2274
2309
  this.getInstance = memoize(([basePath]) => `${basePath}`, (basePath) => new ClientLoader({
2275
2310
  babel: this.babelService,
2276
2311
  logger: this.loggerService,
2312
+ resolve: this.resolveService,
2277
2313
  path: basePath,
2278
2314
  }));
2279
2315
  this.import = (filePath, basePath = process.cwd()) => {
@@ -2508,7 +2544,7 @@ const main$b = async () => {
2508
2544
  if (MODES.some((mode) => values[mode])) {
2509
2545
  return;
2510
2546
  }
2511
- process.stdout.write(`@backtest-kit/cli ${"6.7.0"}\n`);
2547
+ process.stdout.write(`@backtest-kit/cli ${"6.7.2"}\n`);
2512
2548
  process.stdout.write("\n");
2513
2549
  process.stdout.write(`Run with --help to see available commands.\n`);
2514
2550
  process.stdout.write("\n");
@@ -3076,7 +3112,7 @@ const main$1 = async () => {
3076
3112
  if (!values.help) {
3077
3113
  return;
3078
3114
  }
3079
- process.stdout.write(`@backtest-kit/cli ${"6.7.0"}\n\n`);
3115
+ process.stdout.write(`@backtest-kit/cli ${"6.7.2"}\n\n`);
3080
3116
  process.stdout.write(HELP_TEXT);
3081
3117
  process.exit(0);
3082
3118
  };
@@ -3090,7 +3126,7 @@ const main = async () => {
3090
3126
  if (!values.version) {
3091
3127
  return;
3092
3128
  }
3093
- process.stdout.write(`@backtest-kit/cli ${"6.7.0"}\n`);
3129
+ process.stdout.write(`@backtest-kit/cli ${"6.7.2"}\n`);
3094
3130
  process.exit(0);
3095
3131
  };
3096
3132
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backtest-kit/cli",
3
- "version": "6.7.0",
3
+ "version": "6.7.2",
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",
@@ -12,7 +12,7 @@
12
12
  "license": "ISC",
13
13
  "type": "commonjs",
14
14
  "dependencies": {
15
- "@backtest-kit/cli": "^6.7.0",
15
+ "@backtest-kit/cli": "^6.7.2",
16
16
  "@backtest-kit/graph": "^6.7.0",
17
17
  "@backtest-kit/pinets": "^6.7.0",
18
18
  "@backtest-kit/ui": "^6.7.0",
package/types.d.ts CHANGED
@@ -110,18 +110,28 @@ declare class FrameSchemaService {
110
110
  declare class LoaderService {
111
111
  private readonly babelService;
112
112
  private readonly loggerService;
113
+ private readonly resolveService;
113
114
  private getInstance;
114
115
  import: (filePath: string, basePath?: string) => any;
115
116
  check: (filePath: string, basePath?: string) => Promise<boolean>;
116
117
  }
117
118
 
118
- declare class ResolveService {
119
+ interface IResolve {
120
+ DEFAULT_TEMPLATE_DIR: string;
121
+ DEFAULT_MODULES_DIR: string;
122
+ OVERRIDE_TEMPLATE_DIR: string;
123
+ OVERRIDE_MODULES_DIR: string;
124
+ IMPORT_PATHS_DIR: string;
125
+ }
126
+
127
+ declare class ResolveService implements IResolve {
119
128
  readonly loggerService: LoggerService;
120
129
  readonly loaderService: LoaderService;
121
130
  readonly DEFAULT_TEMPLATE_DIR: string;
122
131
  readonly DEFAULT_MODULES_DIR: string;
123
132
  readonly OVERRIDE_TEMPLATE_DIR: string;
124
133
  readonly OVERRIDE_MODULES_DIR: string;
134
+ readonly IMPORT_PATHS_DIR: string;
125
135
  getIsLaunched: () => boolean;
126
136
  attachPine: (pinePath: string) => Promise<string>;
127
137
  attachStrategy: (jsPath: string) => Promise<void>;