@backtest-kit/cli 9.8.0 → 9.8.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 +1854 -1854
- package/build/index.cjs +316 -269
- package/build/index.mjs +318 -271
- package/config/notification.config.mjs +13 -13
- package/config/symbol.config.mjs +460 -460
- package/docker/.env.example +2 -2
- package/docker/content/feb_2026/feb_2026.strategy.ts +11 -11
- package/docker/content/feb_2026/modules/backtest.module.ts +83 -83
- package/docker/docker-compose.yaml +46 -46
- package/docker/package.json +38 -38
- package/docker/tsconfig.json +36 -36
- package/package.json +126 -126
- package/template/average-buy.mustache +22 -22
- package/template/breakeven.mustache +21 -21
- package/template/cancel-scheduled.mustache +14 -14
- package/template/cancelled.mustache +21 -21
- package/template/close-pending.mustache +16 -16
- package/template/closed.mustache +23 -23
- package/template/opened.mustache +22 -22
- package/template/partial-loss.mustache +22 -22
- package/template/partial-profit.mustache +22 -22
- package/template/project/config/symbol.config.ts +460 -460
- package/template/project/package.mustache +28 -28
- package/template/risk.mustache +19 -19
- package/template/scheduled.mustache +22 -22
- package/template/signal-close.mustache +22 -22
- package/template/signal-info.mustache +20 -20
- package/template/signal-open.mustache +22 -22
- package/template/source/CLAUDE.md +160 -160
- package/template/trailing-stop.mustache +21 -21
- package/template/trailing-take.mustache +21 -21
- package/types.d.ts +2 -2
package/build/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as BacktestKit from 'backtest-kit';
|
|
3
|
-
import { setConfig, Notification, Recent, Storage, Markdown, Report, Dump, State, Memory, MarkdownWriter, ReportWriter, PersistSignalAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistPartialAdapter, PersistBreakevenAdapter, PersistCandleAdapter, PersistStorageAdapter, PersistNotificationAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistIntervalAdapter, PersistMemoryAdapter, PersistRecentAdapter, PersistStateAdapter, PersistSessionAdapter, Log, StorageLive, StorageBacktest, NotificationLive, NotificationBacktest, RecentLive, RecentBacktest, SessionLive, SessionBacktest, MemoryLive, MemoryBacktest, StateLive, StateBacktest, listExchangeSchema, addExchangeSchema, roundTicks, listFrameSchema, addFrameSchema, listenDoneLive, listenDoneBacktest, shutdown, listenSignal, listStrategySchema, overrideExchangeSchema, Backtest, System, Cache, Interval, alignToInterval, addWalkerSchema, overrideWalkerSchema, Walker, listenDoneWalker, Live, getCandles, checkCandles, warmCandles, listenRisk, listenStrategyCommit, listenSync, listenSignalNotify, Exchange } from 'backtest-kit';
|
|
4
|
-
import { getErrorMessage, errorData, singleshot, str,
|
|
3
|
+
import { setConfig, Notification, Recent, Storage, Markdown, Report, Dump, State, Memory, MarkdownWriter, ReportWriter, PersistSignalAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistPartialAdapter, PersistBreakevenAdapter, PersistCandleAdapter, PersistStorageAdapter, PersistNotificationAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistIntervalAdapter, PersistMemoryAdapter, PersistRecentAdapter, PersistStateAdapter, PersistSessionAdapter, Log, StorageLive, StorageBacktest, NotificationLive, NotificationBacktest, RecentLive, RecentBacktest, Broker, SessionLive, SessionBacktest, MemoryLive, MemoryBacktest, StateLive, StateBacktest, listExchangeSchema, addExchangeSchema, roundTicks, listFrameSchema, addFrameSchema, listenDoneLive, listenDoneBacktest, shutdown, listenSignal, listStrategySchema, overrideExchangeSchema, Backtest, System, Cache, Interval, alignToInterval, addWalkerSchema, overrideWalkerSchema, Walker, listenDoneWalker, Live, getCandles, checkCandles, warmCandles, listenRisk, listenStrategyCommit, listenSync, listenSignalNotify, Exchange } from 'backtest-kit';
|
|
4
|
+
import { getErrorMessage, errorData, singleshot, str, compose, BehaviorSubject, createAwaiter, execpool, queued, sleep, randomString, TIMEOUT_SYMBOL, typo, retry, trycatch, memoize, isObject } from 'functools-kit';
|
|
5
5
|
import fs, { constants, realpathSync } from 'fs';
|
|
6
6
|
import * as stackTrace from 'stack-trace';
|
|
7
7
|
import path, { join, resolve, dirname, basename, extname } from 'path';
|
|
@@ -33,36 +33,6 @@ import * as BacktestKitSignals from '@backtest-kit/signals';
|
|
|
33
33
|
import open from 'open';
|
|
34
34
|
import { spawn } from 'child_process';
|
|
35
35
|
|
|
36
|
-
/**
|
|
37
|
-
* Fix for `Attempted to assign to readonly property (at redactToken)`
|
|
38
|
-
* @see https://github.com/telegraf/telegraf/issues/2078
|
|
39
|
-
*/
|
|
40
|
-
{
|
|
41
|
-
const OriginalError = globalThis.Error;
|
|
42
|
-
globalThis.Error = new Proxy(OriginalError, {
|
|
43
|
-
construct(target, args) {
|
|
44
|
-
const instance = new target(...args);
|
|
45
|
-
let customMessage = instance.message;
|
|
46
|
-
const handler = {
|
|
47
|
-
get(target, prop, receiver) {
|
|
48
|
-
if (prop === "message") {
|
|
49
|
-
return customMessage;
|
|
50
|
-
}
|
|
51
|
-
return Reflect.get(target, prop, receiver);
|
|
52
|
-
},
|
|
53
|
-
set(target, prop, value, receiver) {
|
|
54
|
-
if (prop === "message") {
|
|
55
|
-
customMessage = value;
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
return Reflect.set(target, prop, value, receiver);
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
return new Proxy(instance, handler);
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
36
|
setConfig({
|
|
67
37
|
CC_MAX_NOTIFICATIONS: 5000,
|
|
68
38
|
CC_MAX_SIGNALS: 750,
|
|
@@ -239,8 +209,6 @@ const TYPES = {
|
|
|
239
209
|
...templateServices$1,
|
|
240
210
|
};
|
|
241
211
|
|
|
242
|
-
const entrySubject = new BehaviorSubject();
|
|
243
|
-
|
|
244
212
|
const SETUP_ADAPTER_FN = () => {
|
|
245
213
|
{
|
|
246
214
|
Dump.useMarkdown();
|
|
@@ -358,6 +326,9 @@ class SetupUtils {
|
|
|
358
326
|
RecentLive.clear();
|
|
359
327
|
RecentBacktest.clear();
|
|
360
328
|
}
|
|
329
|
+
{
|
|
330
|
+
Broker.clear();
|
|
331
|
+
}
|
|
361
332
|
};
|
|
362
333
|
this.update = () => {
|
|
363
334
|
cli.loggerService.debug("SetupUtils update");
|
|
@@ -436,9 +407,9 @@ class ResolveService {
|
|
|
436
407
|
dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
|
|
437
408
|
dotenv.config({ path: path.join(moduleRoot, '.env'), override: true, quiet: true });
|
|
438
409
|
this.loaderService.import(absolutePath);
|
|
439
|
-
await entrySubject.next(absolutePath);
|
|
440
410
|
}
|
|
441
411
|
_is_launched = true;
|
|
412
|
+
return absolutePath;
|
|
442
413
|
};
|
|
443
414
|
this.attachEntry = async (jsPath) => {
|
|
444
415
|
this.loggerService.log("resolveService attachEntry", {
|
|
@@ -453,9 +424,9 @@ class ResolveService {
|
|
|
453
424
|
dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
|
|
454
425
|
{
|
|
455
426
|
this.loaderService.import(absolutePath);
|
|
456
|
-
await entrySubject.next(absolutePath);
|
|
457
427
|
}
|
|
458
428
|
_is_launched = true;
|
|
429
|
+
return absolutePath;
|
|
459
430
|
};
|
|
460
431
|
}
|
|
461
432
|
}
|
|
@@ -858,6 +829,8 @@ const notifyVerbose = singleshot(() => {
|
|
|
858
829
|
});
|
|
859
830
|
});
|
|
860
831
|
|
|
832
|
+
const entrySubject = new BehaviorSubject();
|
|
833
|
+
|
|
861
834
|
const DEFAULT_CACHE_LIST$1 = ["1m", "15m", "30m", "1h", "4h"];
|
|
862
835
|
const GET_CACHE_INTERVAL_LIST_FN$1 = () => {
|
|
863
836
|
const { values } = getArgs();
|
|
@@ -888,11 +861,17 @@ class BacktestMainService {
|
|
|
888
861
|
await this.configConnectionService.loadConfig("setup.config");
|
|
889
862
|
{
|
|
890
863
|
const loader = await this.configConnectionService.loadConfig("loader.config");
|
|
891
|
-
|
|
892
|
-
|
|
864
|
+
try {
|
|
865
|
+
if (typeof loader === "function") {
|
|
866
|
+
await loader();
|
|
867
|
+
}
|
|
868
|
+
if (typeof loader?.loader === "function") {
|
|
869
|
+
await loader.loader();
|
|
870
|
+
}
|
|
893
871
|
}
|
|
894
|
-
|
|
895
|
-
|
|
872
|
+
catch (error) {
|
|
873
|
+
console.error("Module loader failed", error);
|
|
874
|
+
process.exit(-1);
|
|
896
875
|
}
|
|
897
876
|
}
|
|
898
877
|
{
|
|
@@ -907,8 +886,9 @@ class BacktestMainService {
|
|
|
907
886
|
const cwd = process.cwd();
|
|
908
887
|
dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
|
|
909
888
|
}
|
|
889
|
+
let absolutePath;
|
|
910
890
|
{
|
|
911
|
-
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
891
|
+
absolutePath = await this.resolveService.attachJavascript(payload.entryPoint);
|
|
912
892
|
await this.moduleConnectionService.loadModule("backtest.module");
|
|
913
893
|
}
|
|
914
894
|
{
|
|
@@ -950,6 +930,7 @@ class BacktestMainService {
|
|
|
950
930
|
});
|
|
951
931
|
notifyVerbose();
|
|
952
932
|
}
|
|
933
|
+
await entrySubject.next(absolutePath);
|
|
953
934
|
Backtest.background(symbol, {
|
|
954
935
|
strategyName,
|
|
955
936
|
frameName,
|
|
@@ -1016,11 +997,17 @@ class WalkerMainService {
|
|
|
1016
997
|
await this.configConnectionService.loadConfig("setup.config");
|
|
1017
998
|
{
|
|
1018
999
|
const loader = await this.configConnectionService.loadConfig("loader.config");
|
|
1019
|
-
|
|
1020
|
-
|
|
1000
|
+
try {
|
|
1001
|
+
if (typeof loader === "function") {
|
|
1002
|
+
await loader();
|
|
1003
|
+
}
|
|
1004
|
+
if (typeof loader?.loader === "function") {
|
|
1005
|
+
await loader.loader();
|
|
1006
|
+
}
|
|
1021
1007
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1008
|
+
catch (error) {
|
|
1009
|
+
console.error("Module loader failed", error);
|
|
1010
|
+
process.exit(-1);
|
|
1024
1011
|
}
|
|
1025
1012
|
}
|
|
1026
1013
|
{
|
|
@@ -1250,11 +1237,17 @@ class LiveMainService {
|
|
|
1250
1237
|
await this.configConnectionService.loadConfig("setup.config");
|
|
1251
1238
|
{
|
|
1252
1239
|
const loader = await this.configConnectionService.loadConfig("loader.config");
|
|
1253
|
-
|
|
1254
|
-
|
|
1240
|
+
try {
|
|
1241
|
+
if (typeof loader === "function") {
|
|
1242
|
+
await loader();
|
|
1243
|
+
}
|
|
1244
|
+
if (typeof loader?.loader === "function") {
|
|
1245
|
+
await loader.loader();
|
|
1246
|
+
}
|
|
1255
1247
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1248
|
+
catch (error) {
|
|
1249
|
+
console.error("Module loader failed", error);
|
|
1250
|
+
process.exit(-1);
|
|
1258
1251
|
}
|
|
1259
1252
|
}
|
|
1260
1253
|
{
|
|
@@ -1269,8 +1262,9 @@ class LiveMainService {
|
|
|
1269
1262
|
const cwd = process.cwd();
|
|
1270
1263
|
dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
|
|
1271
1264
|
}
|
|
1265
|
+
let absolutePath;
|
|
1272
1266
|
{
|
|
1273
|
-
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
1267
|
+
absolutePath = await this.resolveService.attachJavascript(payload.entryPoint);
|
|
1274
1268
|
await this.moduleConnectionService.loadModule("live.module");
|
|
1275
1269
|
}
|
|
1276
1270
|
{
|
|
@@ -1299,6 +1293,7 @@ class LiveMainService {
|
|
|
1299
1293
|
});
|
|
1300
1294
|
notifyVerbose();
|
|
1301
1295
|
}
|
|
1296
|
+
await entrySubject.next(absolutePath);
|
|
1302
1297
|
Live.background(symbol, {
|
|
1303
1298
|
strategyName,
|
|
1304
1299
|
exchangeName,
|
|
@@ -1345,11 +1340,17 @@ class PaperMainService {
|
|
|
1345
1340
|
await this.configConnectionService.loadConfig("setup.config");
|
|
1346
1341
|
{
|
|
1347
1342
|
const loader = await this.configConnectionService.loadConfig("loader.config");
|
|
1348
|
-
|
|
1349
|
-
|
|
1343
|
+
try {
|
|
1344
|
+
if (typeof loader === "function") {
|
|
1345
|
+
await loader();
|
|
1346
|
+
}
|
|
1347
|
+
if (typeof loader?.loader === "function") {
|
|
1348
|
+
await loader.loader();
|
|
1349
|
+
}
|
|
1350
1350
|
}
|
|
1351
|
-
|
|
1352
|
-
|
|
1351
|
+
catch (error) {
|
|
1352
|
+
console.error("Module loader failed", error);
|
|
1353
|
+
process.exit(-1);
|
|
1353
1354
|
}
|
|
1354
1355
|
}
|
|
1355
1356
|
{
|
|
@@ -1364,8 +1365,9 @@ class PaperMainService {
|
|
|
1364
1365
|
const cwd = process.cwd();
|
|
1365
1366
|
dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
|
|
1366
1367
|
}
|
|
1368
|
+
let absolutePath;
|
|
1367
1369
|
{
|
|
1368
|
-
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
1370
|
+
absolutePath = await this.resolveService.attachJavascript(payload.entryPoint);
|
|
1369
1371
|
await this.moduleConnectionService.loadModule("paper.module");
|
|
1370
1372
|
}
|
|
1371
1373
|
{
|
|
@@ -1394,6 +1396,7 @@ class PaperMainService {
|
|
|
1394
1396
|
});
|
|
1395
1397
|
notifyVerbose();
|
|
1396
1398
|
}
|
|
1399
|
+
await entrySubject.next(absolutePath);
|
|
1397
1400
|
Live.background(symbol, {
|
|
1398
1401
|
strategyName,
|
|
1399
1402
|
exchangeName,
|
|
@@ -1987,7 +1990,8 @@ const GET_TIMEFRAME_RANGE_FN = async (frameName) => {
|
|
|
1987
1990
|
};
|
|
1988
1991
|
const CACHE_CANDLES_FN = retry(async (interval, dto) => {
|
|
1989
1992
|
try {
|
|
1990
|
-
|
|
1993
|
+
process.stdout.write("\n");
|
|
1994
|
+
process.stdout.write(`Checking candles cache for ${dto.symbol} ${interval} from ${dto.from} to ${dto.to}\n`);
|
|
1991
1995
|
await checkCandles({
|
|
1992
1996
|
exchangeName: dto.exchangeName,
|
|
1993
1997
|
from: dto.from,
|
|
@@ -1997,7 +2001,8 @@ const CACHE_CANDLES_FN = retry(async (interval, dto) => {
|
|
|
1997
2001
|
});
|
|
1998
2002
|
}
|
|
1999
2003
|
catch (error) {
|
|
2000
|
-
|
|
2004
|
+
process.stdout.write("\n\n");
|
|
2005
|
+
process.stdout.write(`Caching candles for ${dto.symbol} ${interval} from ${dto.from} to ${dto.to}\n`);
|
|
2001
2006
|
await warmCandles({
|
|
2002
2007
|
symbol: dto.symbol,
|
|
2003
2008
|
exchangeName: dto.exchangeName,
|
|
@@ -3178,10 +3183,10 @@ init();
|
|
|
3178
3183
|
|
|
3179
3184
|
const MODES = ["backtest", "walker", "paper", "live", "pine", "editor", "dump", "pnldebug", "brokerdebug", "flush", "init", "docker", "help", "version"];
|
|
3180
3185
|
const ENTRY_PATH$1 = "./node_modules/@backtest-kit/cli/build/index.mjs";
|
|
3181
|
-
const HELP_TEXT$1 = `
|
|
3182
|
-
Example:
|
|
3183
|
-
|
|
3184
|
-
node ${ENTRY_PATH$1} --help
|
|
3186
|
+
const HELP_TEXT$1 = `
|
|
3187
|
+
Example:
|
|
3188
|
+
|
|
3189
|
+
node ${ENTRY_PATH$1} --help
|
|
3185
3190
|
`.trimStart();
|
|
3186
3191
|
const main$g = async () => {
|
|
3187
3192
|
if (!getEntry(import.meta.url)) {
|
|
@@ -3191,7 +3196,7 @@ const main$g = async () => {
|
|
|
3191
3196
|
if (MODES.some((mode) => values[mode])) {
|
|
3192
3197
|
return;
|
|
3193
3198
|
}
|
|
3194
|
-
process.stdout.write(`@backtest-kit/cli ${"9.8.
|
|
3199
|
+
process.stdout.write(`@backtest-kit/cli ${"9.8.2"}\n`);
|
|
3195
3200
|
process.stdout.write("\n");
|
|
3196
3201
|
process.stdout.write(`Run with --help to see available commands.\n`);
|
|
3197
3202
|
process.stdout.write("\n");
|
|
@@ -3476,20 +3481,20 @@ const main$a = async () => {
|
|
|
3476
3481
|
if (!entryPoints.length) {
|
|
3477
3482
|
throw new Error("At least one entry point is required");
|
|
3478
3483
|
}
|
|
3479
|
-
for (const entryPoint of entryPoints) {
|
|
3480
|
-
if (values.noFlush) {
|
|
3481
|
-
continue;
|
|
3482
|
-
}
|
|
3483
|
-
await flush(entryPoint);
|
|
3484
|
-
}
|
|
3485
3484
|
await cli.configConnectionService.loadConfig("setup.config");
|
|
3486
3485
|
{
|
|
3487
3486
|
const loader = await cli.configConnectionService.loadConfig("loader.config");
|
|
3488
|
-
|
|
3489
|
-
|
|
3487
|
+
try {
|
|
3488
|
+
if (typeof loader === "function") {
|
|
3489
|
+
await loader();
|
|
3490
|
+
}
|
|
3491
|
+
if (typeof loader?.loader === "function") {
|
|
3492
|
+
await loader.loader();
|
|
3493
|
+
}
|
|
3490
3494
|
}
|
|
3491
|
-
|
|
3492
|
-
|
|
3495
|
+
catch (error) {
|
|
3496
|
+
console.error("Module loader failed", error);
|
|
3497
|
+
process.exit(-1);
|
|
3493
3498
|
}
|
|
3494
3499
|
}
|
|
3495
3500
|
{
|
|
@@ -3498,19 +3503,33 @@ const main$a = async () => {
|
|
|
3498
3503
|
}
|
|
3499
3504
|
cli.frontendProviderService.connect();
|
|
3500
3505
|
cli.telegramProviderService.connect();
|
|
3506
|
+
const cwd = process.cwd();
|
|
3501
3507
|
{
|
|
3502
|
-
const cwd = process.cwd();
|
|
3503
3508
|
dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
|
|
3504
3509
|
}
|
|
3505
3510
|
if (entryPoints.length === 1) {
|
|
3506
|
-
|
|
3511
|
+
const absolutePath = path.resolve(entryPoints[0]);
|
|
3512
|
+
const moduleRoot = path.dirname(absolutePath);
|
|
3513
|
+
process.chdir(moduleRoot);
|
|
3514
|
+
cwd !== moduleRoot && Setup.update();
|
|
3515
|
+
dotenv.config({ path: path.join(moduleRoot, '.env'), override: true, quiet: true });
|
|
3516
|
+
}
|
|
3517
|
+
for (const entryPoint of entryPoints) {
|
|
3518
|
+
if (values.noFlush) {
|
|
3519
|
+
continue;
|
|
3520
|
+
}
|
|
3521
|
+
const absolutePath = path.resolve(cwd, entryPoint);
|
|
3522
|
+
await flush(absolutePath);
|
|
3507
3523
|
}
|
|
3508
3524
|
await cli.moduleConnectionService.loadModule(MODE_MODULE[mode]);
|
|
3509
3525
|
listenFinish();
|
|
3510
3526
|
createGracefulShutdown(mode)();
|
|
3527
|
+
let absolutePath;
|
|
3511
3528
|
for (const entryPoint of entryPoints) {
|
|
3512
|
-
|
|
3529
|
+
absolutePath = path.resolve(cwd, entryPoint);
|
|
3530
|
+
await cli.resolveService.attachEntry(absolutePath);
|
|
3513
3531
|
}
|
|
3532
|
+
await entrySubject.next(absolutePath);
|
|
3514
3533
|
};
|
|
3515
3534
|
main$a();
|
|
3516
3535
|
|
|
@@ -3598,11 +3617,17 @@ const main$7 = async () => {
|
|
|
3598
3617
|
await cli.configConnectionService.loadConfig("setup.config");
|
|
3599
3618
|
{
|
|
3600
3619
|
const loader = await cli.configConnectionService.loadConfig("loader.config");
|
|
3601
|
-
|
|
3602
|
-
|
|
3620
|
+
try {
|
|
3621
|
+
if (typeof loader === "function") {
|
|
3622
|
+
await loader();
|
|
3623
|
+
}
|
|
3624
|
+
if (typeof loader?.loader === "function") {
|
|
3625
|
+
await loader.loader();
|
|
3626
|
+
}
|
|
3603
3627
|
}
|
|
3604
|
-
|
|
3605
|
-
|
|
3628
|
+
catch (error) {
|
|
3629
|
+
console.error("Module loader failed", error);
|
|
3630
|
+
process.exit(-1);
|
|
3606
3631
|
}
|
|
3607
3632
|
}
|
|
3608
3633
|
await cli.moduleConnectionService.loadModule("pine.module");
|
|
@@ -3685,11 +3710,17 @@ const main$6 = async () => {
|
|
|
3685
3710
|
await cli.configConnectionService.loadConfig("setup.config");
|
|
3686
3711
|
{
|
|
3687
3712
|
const loader = await cli.configConnectionService.loadConfig("loader.config");
|
|
3688
|
-
|
|
3689
|
-
|
|
3713
|
+
try {
|
|
3714
|
+
if (typeof loader === "function") {
|
|
3715
|
+
await loader();
|
|
3716
|
+
}
|
|
3717
|
+
if (typeof loader?.loader === "function") {
|
|
3718
|
+
await loader.loader();
|
|
3719
|
+
}
|
|
3690
3720
|
}
|
|
3691
|
-
|
|
3692
|
-
|
|
3721
|
+
catch (error) {
|
|
3722
|
+
console.error("Module loader failed", error);
|
|
3723
|
+
process.exit(-1);
|
|
3693
3724
|
}
|
|
3694
3725
|
}
|
|
3695
3726
|
{
|
|
@@ -3706,11 +3737,15 @@ const main$6 = async () => {
|
|
|
3706
3737
|
}
|
|
3707
3738
|
const { CC_WWWROOT_HOST, CC_WWWROOT_PORT } = getEnv();
|
|
3708
3739
|
const unServer = serve(CC_WWWROOT_HOST, CC_WWWROOT_PORT, cli.resolveService.PROJECT_ROOT_DIR);
|
|
3740
|
+
let isOk = true;
|
|
3709
3741
|
try {
|
|
3710
3742
|
await open(`http://localhost:${CC_WWWROOT_PORT}?pine=1`);
|
|
3743
|
+
isOk = true;
|
|
3744
|
+
}
|
|
3745
|
+
catch {
|
|
3711
3746
|
}
|
|
3712
3747
|
finally {
|
|
3713
|
-
console.log(`Editor launched: http://localhost:${CC_WWWROOT_PORT}?pine=1`);
|
|
3748
|
+
isOk && console.log(`Editor launched: http://localhost:${CC_WWWROOT_PORT}?pine=1`);
|
|
3714
3749
|
}
|
|
3715
3750
|
const beforeExit = () => {
|
|
3716
3751
|
process.off("SIGINT", beforeExit);
|
|
@@ -3736,11 +3771,17 @@ const main$5 = async () => {
|
|
|
3736
3771
|
await cli.configConnectionService.loadConfig("setup.config");
|
|
3737
3772
|
{
|
|
3738
3773
|
const loader = await cli.configConnectionService.loadConfig("loader.config");
|
|
3739
|
-
|
|
3740
|
-
|
|
3774
|
+
try {
|
|
3775
|
+
if (typeof loader === "function") {
|
|
3776
|
+
await loader();
|
|
3777
|
+
}
|
|
3778
|
+
if (typeof loader?.loader === "function") {
|
|
3779
|
+
await loader.loader();
|
|
3780
|
+
}
|
|
3741
3781
|
}
|
|
3742
|
-
|
|
3743
|
-
|
|
3782
|
+
catch (error) {
|
|
3783
|
+
console.error("Module loader failed", error);
|
|
3784
|
+
process.exit(-1);
|
|
3744
3785
|
}
|
|
3745
3786
|
}
|
|
3746
3787
|
await cli.moduleConnectionService.loadModule("dump.module");
|
|
@@ -3813,11 +3854,17 @@ const main$4 = async () => {
|
|
|
3813
3854
|
await cli.configConnectionService.loadConfig("setup.config");
|
|
3814
3855
|
{
|
|
3815
3856
|
const loader = await cli.configConnectionService.loadConfig("loader.config");
|
|
3816
|
-
|
|
3817
|
-
|
|
3857
|
+
try {
|
|
3858
|
+
if (typeof loader === "function") {
|
|
3859
|
+
await loader();
|
|
3860
|
+
}
|
|
3861
|
+
if (typeof loader?.loader === "function") {
|
|
3862
|
+
await loader.loader();
|
|
3863
|
+
}
|
|
3818
3864
|
}
|
|
3819
|
-
|
|
3820
|
-
|
|
3865
|
+
catch (error) {
|
|
3866
|
+
console.error("Module loader failed", error);
|
|
3867
|
+
process.exit(-1);
|
|
3821
3868
|
}
|
|
3822
3869
|
}
|
|
3823
3870
|
await cli.moduleConnectionService.loadModule("pnldebug.module");
|
|
@@ -4088,183 +4135,183 @@ const main$2 = async () => {
|
|
|
4088
4135
|
main$2();
|
|
4089
4136
|
|
|
4090
4137
|
const ENTRY_PATH = "./node_modules/@backtest-kit/cli/build/index.mjs";
|
|
4091
|
-
const HELP_TEXT = `
|
|
4092
|
-
Usage:
|
|
4093
|
-
node index.mjs --<mode> [flags] [entry-point]
|
|
4094
|
-
|
|
4095
|
-
Modes:
|
|
4096
|
-
|
|
4097
|
-
--backtest <entry> Run strategy against historical candle data
|
|
4098
|
-
--walker <entry...> Run Walker A/B strategy comparison across multiple strategies
|
|
4099
|
-
--paper <entry> Paper trading (live prices, no real orders)
|
|
4100
|
-
--live <entry> Live trading with real orders
|
|
4101
|
-
--pine <entry> Execute a local .pine indicator file
|
|
4102
|
-
--editor Open the Pine Script visual editor in the browser
|
|
4103
|
-
--dump Fetch and save raw OHLCV candles
|
|
4104
|
-
--pnldebug Simulate PnL per minute for a given entry price and direction
|
|
4105
|
-
--brokerdebug Fire a single broker commit against the live broker adapter
|
|
4106
|
-
--flush <entry...> Delete report/log/markdown/agent folders from strategy dump dir
|
|
4107
|
-
--init Scaffold a new project in the current directory
|
|
4108
|
-
--docker Scaffold a Docker workspace for running strategies in a container
|
|
4109
|
-
--help Print this help message
|
|
4110
|
-
|
|
4111
|
-
Backtest flags:
|
|
4112
|
-
|
|
4113
|
-
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4114
|
-
--strategy <string> Strategy name from addStrategySchema (default: first registered)
|
|
4115
|
-
--exchange <string> Exchange name from addExchangeSchema (default: first registered)
|
|
4116
|
-
--frame <string> Frame name from addFrameSchema (default: first registered)
|
|
4117
|
-
--cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
|
|
4118
|
-
--noCache Skip candle cache warming before the run
|
|
4119
|
-
--noFlush Skip removing report/log/markdown/agent folders before backtest run
|
|
4120
|
-
--verbose Log every candle fetch to stdout
|
|
4121
|
-
--ui Start web dashboard at http://localhost:60050
|
|
4122
|
-
--telegram Send trade notifications to Telegram
|
|
4123
|
-
|
|
4124
|
-
Walker flags (--walker):
|
|
4125
|
-
|
|
4126
|
-
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4127
|
-
--cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
|
|
4128
|
-
--noCache Skip candle cache warming before the run
|
|
4129
|
-
--noFlush Skip removing report/log/markdown/agent folders before walker run
|
|
4130
|
-
--verbose Log every candle fetch to stdout
|
|
4131
|
-
--output <string> Output file base name (default: walker_{SYMBOL}_{TIMESTAMP})
|
|
4132
|
-
--json Save results as JSON to ./dump/<output>.json
|
|
4133
|
-
--markdown Save report as Markdown to ./dump/<output>.md
|
|
4134
|
-
|
|
4135
|
-
Each positional argument is a strategy entry point. All strategy files are loaded without
|
|
4136
|
-
changing process.cwd() — .env is read from the working directory only.
|
|
4137
|
-
addWalkerSchema is called automatically using the registered exchange and frame.
|
|
4138
|
-
After comparison completes the report is printed to stdout (or saved if --json/--markdown).
|
|
4139
|
-
|
|
4140
|
-
Module file ./modules/walker.module is loaded automatically if it exists.
|
|
4141
|
-
|
|
4142
|
-
Paper / Live flags:
|
|
4143
|
-
|
|
4144
|
-
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4145
|
-
--strategy <string> Strategy name (default: first registered)
|
|
4146
|
-
--exchange <string> Exchange name (default: first registered)
|
|
4147
|
-
--verbose Log every candle fetch to stdout
|
|
4148
|
-
--ui Start web dashboard
|
|
4149
|
-
--telegram Send Telegram notifications
|
|
4150
|
-
|
|
4151
|
-
PineScript flags (--pine):
|
|
4152
|
-
|
|
4153
|
-
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4154
|
-
--timeframe <string> Candle interval (default: 15m)
|
|
4155
|
-
--limit <string> Number of candles to fetch (default: 250)
|
|
4156
|
-
--when <string> End date — ISO 8601 or Unix ms (default: now)
|
|
4157
|
-
--exchange <string> Exchange name (default: first registered)
|
|
4158
|
-
--output <string> Output file base name without extension
|
|
4159
|
-
--json Save output as JSON array to <pine-dir>/dump/<output>.json
|
|
4160
|
-
--jsonl Save output as JSONL to <pine-dir>/dump/<output>.jsonl
|
|
4161
|
-
--markdown Save output as Markdown table to <pine-dir>/dump/<output>.md
|
|
4162
|
-
|
|
4163
|
-
Only plot() calls with display=display.data_window produce output columns.
|
|
4164
|
-
Module file ./modules/pine.module is loaded automatically if it exists.
|
|
4165
|
-
|
|
4166
|
-
Candle dump flags (--dump):
|
|
4167
|
-
|
|
4168
|
-
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4169
|
-
--timeframe <string> Candle interval (default: 15m)
|
|
4170
|
-
--limit <string> Number of candles (default: 250)
|
|
4171
|
-
--when <string> End date — ISO 8601 or Unix ms (default: now)
|
|
4172
|
-
--exchange <string> Exchange name (default: first registered)
|
|
4173
|
-
--output <string> Output file base name (default: {SYMBOL}_{LIMIT}_{TIMEFRAME}_{TIMESTAMP})
|
|
4174
|
-
--json Save as JSON array to ./dump/<output>.json
|
|
4175
|
-
--jsonl Save as JSONL to ./dump/<output>.jsonl
|
|
4176
|
-
|
|
4177
|
-
Module file ./modules/dump.module is loaded automatically if it exists.
|
|
4178
|
-
|
|
4179
|
-
PnL debug flags (--pnldebug):
|
|
4180
|
-
|
|
4181
|
-
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4182
|
-
--priceopen <number> Entry price (required)
|
|
4183
|
-
--direction <string> Position direction: long or short (default: long)
|
|
4184
|
-
--when <string> Start timestamp — ISO 8601 or Unix ms (default: now)
|
|
4185
|
-
--minutes <string> Number of 1m candles to simulate (default: 60)
|
|
4186
|
-
--exchange <string> Exchange name (default: first registered)
|
|
4187
|
-
--output <string> Output file base name (default: {SYMBOL}_{DIRECTION}_{PRICEOPEN}_{TIMESTAMP})
|
|
4188
|
-
--json Save as JSON array to ./dump/<output>.json
|
|
4189
|
-
--jsonl Save as JSONL to ./dump/<output>.jsonl
|
|
4190
|
-
--markdown Save as Markdown table to ./dump/<output>.md
|
|
4191
|
-
|
|
4192
|
-
Module file ./modules/pnldebug.module is loaded automatically if it exists.
|
|
4193
|
-
|
|
4194
|
-
Broker debug flags (--brokerdebug):
|
|
4195
|
-
|
|
4196
|
-
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4197
|
-
--exchange <string> Exchange name (default: first registered)
|
|
4198
|
-
--commit <string> Commit type to fire: signal-open, signal-close, partial-profit,
|
|
4199
|
-
partial-loss, average-buy, trailing-stop, trailing-take, breakeven
|
|
4200
|
-
(default: signal-open)
|
|
4201
|
-
|
|
4202
|
-
Loads ./live.module, fetches the last candle for --symbol/--timeframe, and calls
|
|
4203
|
-
the selected broker commit with synthetic payload values derived from current price.
|
|
4204
|
-
|
|
4205
|
-
Flush flags (--flush):
|
|
4206
|
-
|
|
4207
|
-
One or more positional entry points. For each entry point the following
|
|
4208
|
-
subdirectories are removed from <entry-dir>/dump/:
|
|
4209
|
-
|
|
4210
|
-
report log markdown agent
|
|
4211
|
-
|
|
4212
|
-
Init flags (--init):
|
|
4213
|
-
|
|
4214
|
-
--output <string> Target directory name (default: backtest-kit-project)
|
|
4215
|
-
|
|
4216
|
-
Scaffolds a project and runs scripts/fetch_docs.mjs to download library docs.
|
|
4217
|
-
|
|
4218
|
-
Docker flags (--docker):
|
|
4219
|
-
|
|
4220
|
-
--output <string> Target directory name (default: backtest-kit-docker)
|
|
4221
|
-
|
|
4222
|
-
Scaffolds a Docker workspace: docker-compose.yaml, .env.example, package.json,
|
|
4223
|
-
tsconfig.json, and a sample strategy under content/. Run npm install then
|
|
4224
|
-
docker compose up to start the container.
|
|
4225
|
-
|
|
4226
|
-
Module hooks (loaded automatically by each mode):
|
|
4227
|
-
|
|
4228
|
-
modules/backtest.module --backtest Broker adapter for backtest
|
|
4229
|
-
modules/walker.module --walker Broker adapter for walker comparison
|
|
4230
|
-
modules/paper.module --paper Broker adapter for paper trading
|
|
4231
|
-
modules/live.module --live Broker adapter for live trading
|
|
4232
|
-
modules/pine.module --pine Exchange schema for PineScript runs
|
|
4233
|
-
modules/editor.module --editor Exchange schema for the visual Pine editor
|
|
4234
|
-
modules/dump.module --dump Exchange schema for candle dumps
|
|
4235
|
-
modules/pnldebug.module --pnldebug Exchange schema for PnL debug runs
|
|
4236
|
-
modules/brokerdebug.module --brokerdebug Broker adapter used for broker commit testing
|
|
4237
|
-
|
|
4238
|
-
--flush has no associated module. It only removes dump subdirectories.
|
|
4239
|
-
|
|
4240
|
-
Extensions .ts, .mjs, .cjs are tried automatically. Missing module = soft warning.
|
|
4241
|
-
|
|
4242
|
-
Environment variables:
|
|
4243
|
-
|
|
4244
|
-
CC_TELEGRAM_TOKEN Telegram bot token (required for --telegram)
|
|
4245
|
-
CC_TELEGRAM_CHANNEL Telegram channel or chat ID (required for --telegram)
|
|
4246
|
-
CC_WWWROOT_HOST UI server bind address (default: 0.0.0.0)
|
|
4247
|
-
CC_WWWROOT_PORT UI server port (default: 60050)
|
|
4248
|
-
|
|
4249
|
-
Examples:
|
|
4250
|
-
|
|
4251
|
-
node ${ENTRY_PATH} --backtest ./content/feb_2026.strategy.ts
|
|
4252
|
-
node ${ENTRY_PATH} --backtest --symbol BTCUSDT --noCache --noFlush --ui ./content/feb_2026.strategy.ts
|
|
4253
|
-
node ${ENTRY_PATH} --walker ./content/feb_2026_v1.strategy.ts ./content/feb_2026_v2.strategy.ts ./content/feb_2026_v3.strategy.ts
|
|
4254
|
-
node ${ENTRY_PATH} --walker --symbol BTCUSDT --noCache --noFlush --markdown ./content/feb_2026_v1.ts ./content/feb_2026_v2.ts
|
|
4255
|
-
node ${ENTRY_PATH} --paper --symbol ETHUSDT ./content/feb_2026.strategy.ts
|
|
4256
|
-
node ${ENTRY_PATH} --live --ui --telegram ./content/feb_2026.strategy.ts
|
|
4257
|
-
node ${ENTRY_PATH} --pine ./math/feb_2026.pine --timeframe 15m --limit 500 --jsonl
|
|
4258
|
-
node ${ENTRY_PATH} --editor
|
|
4259
|
-
node ${ENTRY_PATH} --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl
|
|
4260
|
-
node ${ENTRY_PATH} --pnldebug --symbol BTCUSDT --priceopen 64069.50 --direction short --when "2025-02-25" --minutes 120
|
|
4261
|
-
node ${ENTRY_PATH} --pnldebug --priceopen 67956.73 --direction long --when 1772064000000 --minutes 60 --markdown
|
|
4262
|
-
node ${ENTRY_PATH} --brokerdebug --commit signal-open --symbol BTCUSDT
|
|
4263
|
-
node ${ENTRY_PATH} --brokerdebug --commit partial-profit --symbol ETHUSDT
|
|
4264
|
-
node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts
|
|
4265
|
-
node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts ./content/feb_2026.strategy/feb_2026.test.ts
|
|
4266
|
-
node ${ENTRY_PATH} --init --output my-trading-bot
|
|
4267
|
-
node ${ENTRY_PATH} --docker --output my-docker-workspace
|
|
4138
|
+
const HELP_TEXT = `
|
|
4139
|
+
Usage:
|
|
4140
|
+
node index.mjs --<mode> [flags] [entry-point]
|
|
4141
|
+
|
|
4142
|
+
Modes:
|
|
4143
|
+
|
|
4144
|
+
--backtest <entry> Run strategy against historical candle data
|
|
4145
|
+
--walker <entry...> Run Walker A/B strategy comparison across multiple strategies
|
|
4146
|
+
--paper <entry> Paper trading (live prices, no real orders)
|
|
4147
|
+
--live <entry> Live trading with real orders
|
|
4148
|
+
--pine <entry> Execute a local .pine indicator file
|
|
4149
|
+
--editor Open the Pine Script visual editor in the browser
|
|
4150
|
+
--dump Fetch and save raw OHLCV candles
|
|
4151
|
+
--pnldebug Simulate PnL per minute for a given entry price and direction
|
|
4152
|
+
--brokerdebug Fire a single broker commit against the live broker adapter
|
|
4153
|
+
--flush <entry...> Delete report/log/markdown/agent folders from strategy dump dir
|
|
4154
|
+
--init Scaffold a new project in the current directory
|
|
4155
|
+
--docker Scaffold a Docker workspace for running strategies in a container
|
|
4156
|
+
--help Print this help message
|
|
4157
|
+
|
|
4158
|
+
Backtest flags:
|
|
4159
|
+
|
|
4160
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4161
|
+
--strategy <string> Strategy name from addStrategySchema (default: first registered)
|
|
4162
|
+
--exchange <string> Exchange name from addExchangeSchema (default: first registered)
|
|
4163
|
+
--frame <string> Frame name from addFrameSchema (default: first registered)
|
|
4164
|
+
--cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
|
|
4165
|
+
--noCache Skip candle cache warming before the run
|
|
4166
|
+
--noFlush Skip removing report/log/markdown/agent folders before backtest run
|
|
4167
|
+
--verbose Log every candle fetch to stdout
|
|
4168
|
+
--ui Start web dashboard at http://localhost:60050
|
|
4169
|
+
--telegram Send trade notifications to Telegram
|
|
4170
|
+
|
|
4171
|
+
Walker flags (--walker):
|
|
4172
|
+
|
|
4173
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4174
|
+
--cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
|
|
4175
|
+
--noCache Skip candle cache warming before the run
|
|
4176
|
+
--noFlush Skip removing report/log/markdown/agent folders before walker run
|
|
4177
|
+
--verbose Log every candle fetch to stdout
|
|
4178
|
+
--output <string> Output file base name (default: walker_{SYMBOL}_{TIMESTAMP})
|
|
4179
|
+
--json Save results as JSON to ./dump/<output>.json
|
|
4180
|
+
--markdown Save report as Markdown to ./dump/<output>.md
|
|
4181
|
+
|
|
4182
|
+
Each positional argument is a strategy entry point. All strategy files are loaded without
|
|
4183
|
+
changing process.cwd() — .env is read from the working directory only.
|
|
4184
|
+
addWalkerSchema is called automatically using the registered exchange and frame.
|
|
4185
|
+
After comparison completes the report is printed to stdout (or saved if --json/--markdown).
|
|
4186
|
+
|
|
4187
|
+
Module file ./modules/walker.module is loaded automatically if it exists.
|
|
4188
|
+
|
|
4189
|
+
Paper / Live flags:
|
|
4190
|
+
|
|
4191
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4192
|
+
--strategy <string> Strategy name (default: first registered)
|
|
4193
|
+
--exchange <string> Exchange name (default: first registered)
|
|
4194
|
+
--verbose Log every candle fetch to stdout
|
|
4195
|
+
--ui Start web dashboard
|
|
4196
|
+
--telegram Send Telegram notifications
|
|
4197
|
+
|
|
4198
|
+
PineScript flags (--pine):
|
|
4199
|
+
|
|
4200
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4201
|
+
--timeframe <string> Candle interval (default: 15m)
|
|
4202
|
+
--limit <string> Number of candles to fetch (default: 250)
|
|
4203
|
+
--when <string> End date — ISO 8601 or Unix ms (default: now)
|
|
4204
|
+
--exchange <string> Exchange name (default: first registered)
|
|
4205
|
+
--output <string> Output file base name without extension
|
|
4206
|
+
--json Save output as JSON array to <pine-dir>/dump/<output>.json
|
|
4207
|
+
--jsonl Save output as JSONL to <pine-dir>/dump/<output>.jsonl
|
|
4208
|
+
--markdown Save output as Markdown table to <pine-dir>/dump/<output>.md
|
|
4209
|
+
|
|
4210
|
+
Only plot() calls with display=display.data_window produce output columns.
|
|
4211
|
+
Module file ./modules/pine.module is loaded automatically if it exists.
|
|
4212
|
+
|
|
4213
|
+
Candle dump flags (--dump):
|
|
4214
|
+
|
|
4215
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4216
|
+
--timeframe <string> Candle interval (default: 15m)
|
|
4217
|
+
--limit <string> Number of candles (default: 250)
|
|
4218
|
+
--when <string> End date — ISO 8601 or Unix ms (default: now)
|
|
4219
|
+
--exchange <string> Exchange name (default: first registered)
|
|
4220
|
+
--output <string> Output file base name (default: {SYMBOL}_{LIMIT}_{TIMEFRAME}_{TIMESTAMP})
|
|
4221
|
+
--json Save as JSON array to ./dump/<output>.json
|
|
4222
|
+
--jsonl Save as JSONL to ./dump/<output>.jsonl
|
|
4223
|
+
|
|
4224
|
+
Module file ./modules/dump.module is loaded automatically if it exists.
|
|
4225
|
+
|
|
4226
|
+
PnL debug flags (--pnldebug):
|
|
4227
|
+
|
|
4228
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4229
|
+
--priceopen <number> Entry price (required)
|
|
4230
|
+
--direction <string> Position direction: long or short (default: long)
|
|
4231
|
+
--when <string> Start timestamp — ISO 8601 or Unix ms (default: now)
|
|
4232
|
+
--minutes <string> Number of 1m candles to simulate (default: 60)
|
|
4233
|
+
--exchange <string> Exchange name (default: first registered)
|
|
4234
|
+
--output <string> Output file base name (default: {SYMBOL}_{DIRECTION}_{PRICEOPEN}_{TIMESTAMP})
|
|
4235
|
+
--json Save as JSON array to ./dump/<output>.json
|
|
4236
|
+
--jsonl Save as JSONL to ./dump/<output>.jsonl
|
|
4237
|
+
--markdown Save as Markdown table to ./dump/<output>.md
|
|
4238
|
+
|
|
4239
|
+
Module file ./modules/pnldebug.module is loaded automatically if it exists.
|
|
4240
|
+
|
|
4241
|
+
Broker debug flags (--brokerdebug):
|
|
4242
|
+
|
|
4243
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
4244
|
+
--exchange <string> Exchange name (default: first registered)
|
|
4245
|
+
--commit <string> Commit type to fire: signal-open, signal-close, partial-profit,
|
|
4246
|
+
partial-loss, average-buy, trailing-stop, trailing-take, breakeven
|
|
4247
|
+
(default: signal-open)
|
|
4248
|
+
|
|
4249
|
+
Loads ./live.module, fetches the last candle for --symbol/--timeframe, and calls
|
|
4250
|
+
the selected broker commit with synthetic payload values derived from current price.
|
|
4251
|
+
|
|
4252
|
+
Flush flags (--flush):
|
|
4253
|
+
|
|
4254
|
+
One or more positional entry points. For each entry point the following
|
|
4255
|
+
subdirectories are removed from <entry-dir>/dump/:
|
|
4256
|
+
|
|
4257
|
+
report log markdown agent
|
|
4258
|
+
|
|
4259
|
+
Init flags (--init):
|
|
4260
|
+
|
|
4261
|
+
--output <string> Target directory name (default: backtest-kit-project)
|
|
4262
|
+
|
|
4263
|
+
Scaffolds a project and runs scripts/fetch_docs.mjs to download library docs.
|
|
4264
|
+
|
|
4265
|
+
Docker flags (--docker):
|
|
4266
|
+
|
|
4267
|
+
--output <string> Target directory name (default: backtest-kit-docker)
|
|
4268
|
+
|
|
4269
|
+
Scaffolds a Docker workspace: docker-compose.yaml, .env.example, package.json,
|
|
4270
|
+
tsconfig.json, and a sample strategy under content/. Run npm install then
|
|
4271
|
+
docker compose up to start the container.
|
|
4272
|
+
|
|
4273
|
+
Module hooks (loaded automatically by each mode):
|
|
4274
|
+
|
|
4275
|
+
modules/backtest.module --backtest Broker adapter for backtest
|
|
4276
|
+
modules/walker.module --walker Broker adapter for walker comparison
|
|
4277
|
+
modules/paper.module --paper Broker adapter for paper trading
|
|
4278
|
+
modules/live.module --live Broker adapter for live trading
|
|
4279
|
+
modules/pine.module --pine Exchange schema for PineScript runs
|
|
4280
|
+
modules/editor.module --editor Exchange schema for the visual Pine editor
|
|
4281
|
+
modules/dump.module --dump Exchange schema for candle dumps
|
|
4282
|
+
modules/pnldebug.module --pnldebug Exchange schema for PnL debug runs
|
|
4283
|
+
modules/brokerdebug.module --brokerdebug Broker adapter used for broker commit testing
|
|
4284
|
+
|
|
4285
|
+
--flush has no associated module. It only removes dump subdirectories.
|
|
4286
|
+
|
|
4287
|
+
Extensions .ts, .mjs, .cjs are tried automatically. Missing module = soft warning.
|
|
4288
|
+
|
|
4289
|
+
Environment variables:
|
|
4290
|
+
|
|
4291
|
+
CC_TELEGRAM_TOKEN Telegram bot token (required for --telegram)
|
|
4292
|
+
CC_TELEGRAM_CHANNEL Telegram channel or chat ID (required for --telegram)
|
|
4293
|
+
CC_WWWROOT_HOST UI server bind address (default: 0.0.0.0)
|
|
4294
|
+
CC_WWWROOT_PORT UI server port (default: 60050)
|
|
4295
|
+
|
|
4296
|
+
Examples:
|
|
4297
|
+
|
|
4298
|
+
node ${ENTRY_PATH} --backtest ./content/feb_2026.strategy.ts
|
|
4299
|
+
node ${ENTRY_PATH} --backtest --symbol BTCUSDT --noCache --noFlush --ui ./content/feb_2026.strategy.ts
|
|
4300
|
+
node ${ENTRY_PATH} --walker ./content/feb_2026_v1.strategy.ts ./content/feb_2026_v2.strategy.ts ./content/feb_2026_v3.strategy.ts
|
|
4301
|
+
node ${ENTRY_PATH} --walker --symbol BTCUSDT --noCache --noFlush --markdown ./content/feb_2026_v1.ts ./content/feb_2026_v2.ts
|
|
4302
|
+
node ${ENTRY_PATH} --paper --symbol ETHUSDT ./content/feb_2026.strategy.ts
|
|
4303
|
+
node ${ENTRY_PATH} --live --ui --telegram ./content/feb_2026.strategy.ts
|
|
4304
|
+
node ${ENTRY_PATH} --pine ./math/feb_2026.pine --timeframe 15m --limit 500 --jsonl
|
|
4305
|
+
node ${ENTRY_PATH} --editor
|
|
4306
|
+
node ${ENTRY_PATH} --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl
|
|
4307
|
+
node ${ENTRY_PATH} --pnldebug --symbol BTCUSDT --priceopen 64069.50 --direction short --when "2025-02-25" --minutes 120
|
|
4308
|
+
node ${ENTRY_PATH} --pnldebug --priceopen 67956.73 --direction long --when 1772064000000 --minutes 60 --markdown
|
|
4309
|
+
node ${ENTRY_PATH} --brokerdebug --commit signal-open --symbol BTCUSDT
|
|
4310
|
+
node ${ENTRY_PATH} --brokerdebug --commit partial-profit --symbol ETHUSDT
|
|
4311
|
+
node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts
|
|
4312
|
+
node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts ./content/feb_2026.strategy/feb_2026.test.ts
|
|
4313
|
+
node ${ENTRY_PATH} --init --output my-trading-bot
|
|
4314
|
+
node ${ENTRY_PATH} --docker --output my-docker-workspace
|
|
4268
4315
|
`.trimStart();
|
|
4269
4316
|
const main$1 = async () => {
|
|
4270
4317
|
if (!getEntry(import.meta.url)) {
|
|
@@ -4274,7 +4321,7 @@ const main$1 = async () => {
|
|
|
4274
4321
|
if (!values.help) {
|
|
4275
4322
|
return;
|
|
4276
4323
|
}
|
|
4277
|
-
process.stdout.write(`@backtest-kit/cli ${"9.8.
|
|
4324
|
+
process.stdout.write(`@backtest-kit/cli ${"9.8.2"}\n\n`);
|
|
4278
4325
|
process.stdout.write(HELP_TEXT);
|
|
4279
4326
|
process.exit(0);
|
|
4280
4327
|
};
|
|
@@ -4288,7 +4335,7 @@ const main = async () => {
|
|
|
4288
4335
|
if (!values.version) {
|
|
4289
4336
|
return;
|
|
4290
4337
|
}
|
|
4291
|
-
process.stdout.write(`@backtest-kit/cli ${"9.8.
|
|
4338
|
+
process.stdout.write(`@backtest-kit/cli ${"9.8.2"}\n`);
|
|
4292
4339
|
process.exit(0);
|
|
4293
4340
|
};
|
|
4294
4341
|
main();
|