@backtest-kit/cli 6.2.0 → 6.2.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/build/index.cjs +61 -15
- package/build/index.mjs +61 -15
- package/package.json +1 -1
- package/template/project/README.md +1 -1
- package/template/project/package.mustache +1 -1
package/build/index.cjs
CHANGED
|
@@ -829,8 +829,15 @@ class WalkerMainService {
|
|
|
829
829
|
this.moduleConnectionService = inject(TYPES.moduleConnectionService);
|
|
830
830
|
this.run = functoolsKit.singleshot(async (payload) => {
|
|
831
831
|
this.loggerService.log("walkerMainService run", { payload });
|
|
832
|
+
const strategyMap = new Map();
|
|
832
833
|
for (const entryPoint of payload.entryPoints) {
|
|
833
834
|
await this.resolveService.attachStrategy(entryPoint);
|
|
835
|
+
for (const { strategyName } of await BacktestKit.listStrategySchema()) {
|
|
836
|
+
if (strategyMap.has(strategyName)) {
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
strategyMap.set(strategyName, entryPoint);
|
|
840
|
+
}
|
|
834
841
|
}
|
|
835
842
|
await this.moduleConnectionService.loadModule("./walker.module");
|
|
836
843
|
{
|
|
@@ -867,19 +874,38 @@ class WalkerMainService {
|
|
|
867
874
|
if (!frameName) {
|
|
868
875
|
throw new Error("Frame name is required");
|
|
869
876
|
}
|
|
877
|
+
const cwd = process.cwd();
|
|
878
|
+
const self = this;
|
|
879
|
+
const callbacks = {
|
|
880
|
+
async onStrategyStart(strategyName) {
|
|
881
|
+
const entryPoint = strategyMap.get(strategyName);
|
|
882
|
+
if (!entryPoint) {
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
const absolutePath = path.resolve(entryPoint);
|
|
886
|
+
const moduleRoot = path.dirname(absolutePath);
|
|
887
|
+
{
|
|
888
|
+
process.chdir(moduleRoot);
|
|
889
|
+
cwd !== moduleRoot && BacktestKit.Log.useJsonl();
|
|
890
|
+
dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
|
|
891
|
+
dotenv.config({ path: path.join(moduleRoot, '.env'), override: true, quiet: true });
|
|
892
|
+
}
|
|
893
|
+
if (!payload.noCache) {
|
|
894
|
+
await self.cacheLogicService.execute(payload.cacheInterval, {
|
|
895
|
+
exchangeName,
|
|
896
|
+
frameName,
|
|
897
|
+
symbol,
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
},
|
|
901
|
+
};
|
|
870
902
|
BacktestKit.addWalkerSchema({
|
|
871
903
|
walkerName: WALKER_NAME,
|
|
872
904
|
exchangeName,
|
|
873
905
|
frameName,
|
|
874
906
|
strategies: strategyNames,
|
|
907
|
+
callbacks,
|
|
875
908
|
});
|
|
876
|
-
if (!payload.noCache) {
|
|
877
|
-
await this.cacheLogicService.execute(payload.cacheInterval, {
|
|
878
|
-
exchangeName,
|
|
879
|
-
frameName,
|
|
880
|
-
symbol,
|
|
881
|
-
});
|
|
882
|
-
}
|
|
883
909
|
if (payload.verbose) {
|
|
884
910
|
BacktestKit.overrideExchangeSchema({
|
|
885
911
|
exchangeName,
|
|
@@ -895,8 +921,9 @@ class WalkerMainService {
|
|
|
895
921
|
BacktestKit.overrideWalkerSchema({
|
|
896
922
|
walkerName: WALKER_NAME,
|
|
897
923
|
callbacks: {
|
|
898
|
-
onStrategyStart(strategyName, symbol) {
|
|
924
|
+
async onStrategyStart(strategyName, symbol) {
|
|
899
925
|
console.log(`Strategy started: ${strategyName} for symbol: ${symbol}`);
|
|
926
|
+
await callbacks.onStrategyStart(strategyName);
|
|
900
927
|
},
|
|
901
928
|
onStrategyError(strategyName, symbol, error) {
|
|
902
929
|
console.error(`Strategy error: ${strategyName} for symbol: ${symbol}`, error);
|
|
@@ -917,9 +944,12 @@ class WalkerMainService {
|
|
|
917
944
|
unWalker();
|
|
918
945
|
res();
|
|
919
946
|
});
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
947
|
+
{
|
|
948
|
+
payload.verbose && console.time("Walker");
|
|
949
|
+
await awaiter;
|
|
950
|
+
payload.verbose && console.timeEnd("Walker");
|
|
951
|
+
}
|
|
952
|
+
process.chdir(cwd);
|
|
923
953
|
const dumpName = payload.output || `walker_${symbol}_${Date.now()}`;
|
|
924
954
|
const dumpDir = path.join(process.cwd(), "dump");
|
|
925
955
|
if (payload.json) {
|
|
@@ -2119,7 +2149,7 @@ const TRANSPILE_FN = functoolsKit.memoize(([path]) => `${path}`, (path, code, se
|
|
|
2119
2149
|
eval(self.params.babel.transpile(code));
|
|
2120
2150
|
}
|
|
2121
2151
|
catch (error) {
|
|
2122
|
-
console.log(`Error during transpilation error=\`${functoolsKit.getErrorMessage(error)}\` path
|
|
2152
|
+
console.log(`Error during transpilation error=\`${functoolsKit.getErrorMessage(error)}\` path=\`${path}\` __filename=\`${__filename}\` __dirname=\`${__dirname}\``);
|
|
2123
2153
|
process.exit(-1);
|
|
2124
2154
|
}
|
|
2125
2155
|
return {
|
|
@@ -2426,7 +2456,7 @@ const main$b = async () => {
|
|
|
2426
2456
|
if (MODES.some((mode) => values[mode])) {
|
|
2427
2457
|
return;
|
|
2428
2458
|
}
|
|
2429
|
-
process.stdout.write(`@backtest-kit/cli ${"6.2.
|
|
2459
|
+
process.stdout.write(`@backtest-kit/cli ${"6.2.1"}\n`);
|
|
2430
2460
|
process.stdout.write("\n");
|
|
2431
2461
|
process.stdout.write(`Run with --help to see available commands.\n`);
|
|
2432
2462
|
process.stdout.write("\n");
|
|
@@ -2827,6 +2857,20 @@ function runScript(scriptPath, cwd) {
|
|
|
2827
2857
|
child.on("error", reject);
|
|
2828
2858
|
});
|
|
2829
2859
|
}
|
|
2860
|
+
function runNpmInstall(cwd) {
|
|
2861
|
+
return new Promise((resolve, reject) => {
|
|
2862
|
+
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
2863
|
+
const child = child_process.spawn(npm, ["install"], { cwd, stdio: "inherit", shell: true });
|
|
2864
|
+
child.on("close", (code) => {
|
|
2865
|
+
if (code !== 0) {
|
|
2866
|
+
reject(new Error(`npm install exited with code ${code}`));
|
|
2867
|
+
return;
|
|
2868
|
+
}
|
|
2869
|
+
resolve();
|
|
2870
|
+
});
|
|
2871
|
+
child.on("error", reject);
|
|
2872
|
+
});
|
|
2873
|
+
}
|
|
2830
2874
|
const main$2 = async () => {
|
|
2831
2875
|
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)))) {
|
|
2832
2876
|
return;
|
|
@@ -2847,6 +2891,8 @@ const main$2 = async () => {
|
|
|
2847
2891
|
await copyDir(templatePath, projectPath, { PROJECT_NAME: projectName });
|
|
2848
2892
|
console.log(`Fetching docs...`);
|
|
2849
2893
|
await runScript(path.join(projectPath, "scripts/fetch_docs.mjs"), projectPath);
|
|
2894
|
+
console.log(`Installing dependencies...`);
|
|
2895
|
+
await runNpmInstall(projectPath);
|
|
2850
2896
|
console.log(`Done! Project created at ${projectPath}`);
|
|
2851
2897
|
process.exit(0);
|
|
2852
2898
|
};
|
|
@@ -2978,7 +3024,7 @@ const main$1 = async () => {
|
|
|
2978
3024
|
if (!values.help) {
|
|
2979
3025
|
return;
|
|
2980
3026
|
}
|
|
2981
|
-
process.stdout.write(`@backtest-kit/cli ${"6.2.
|
|
3027
|
+
process.stdout.write(`@backtest-kit/cli ${"6.2.1"}\n\n`);
|
|
2982
3028
|
process.stdout.write(HELP_TEXT);
|
|
2983
3029
|
process.exit(0);
|
|
2984
3030
|
};
|
|
@@ -2992,7 +3038,7 @@ const main = async () => {
|
|
|
2992
3038
|
if (!values.version) {
|
|
2993
3039
|
return;
|
|
2994
3040
|
}
|
|
2995
|
-
process.stdout.write(`@backtest-kit/cli ${"6.2.
|
|
3041
|
+
process.stdout.write(`@backtest-kit/cli ${"6.2.1"}\n`);
|
|
2996
3042
|
process.exit(0);
|
|
2997
3043
|
};
|
|
2998
3044
|
main();
|
package/build/index.mjs
CHANGED
|
@@ -804,8 +804,15 @@ class WalkerMainService {
|
|
|
804
804
|
this.moduleConnectionService = inject(TYPES.moduleConnectionService);
|
|
805
805
|
this.run = singleshot(async (payload) => {
|
|
806
806
|
this.loggerService.log("walkerMainService run", { payload });
|
|
807
|
+
const strategyMap = new Map();
|
|
807
808
|
for (const entryPoint of payload.entryPoints) {
|
|
808
809
|
await this.resolveService.attachStrategy(entryPoint);
|
|
810
|
+
for (const { strategyName } of await listStrategySchema()) {
|
|
811
|
+
if (strategyMap.has(strategyName)) {
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
strategyMap.set(strategyName, entryPoint);
|
|
815
|
+
}
|
|
809
816
|
}
|
|
810
817
|
await this.moduleConnectionService.loadModule("./walker.module");
|
|
811
818
|
{
|
|
@@ -842,19 +849,38 @@ class WalkerMainService {
|
|
|
842
849
|
if (!frameName) {
|
|
843
850
|
throw new Error("Frame name is required");
|
|
844
851
|
}
|
|
852
|
+
const cwd = process.cwd();
|
|
853
|
+
const self = this;
|
|
854
|
+
const callbacks = {
|
|
855
|
+
async onStrategyStart(strategyName) {
|
|
856
|
+
const entryPoint = strategyMap.get(strategyName);
|
|
857
|
+
if (!entryPoint) {
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
const absolutePath = path.resolve(entryPoint);
|
|
861
|
+
const moduleRoot = path.dirname(absolutePath);
|
|
862
|
+
{
|
|
863
|
+
process.chdir(moduleRoot);
|
|
864
|
+
cwd !== moduleRoot && Log.useJsonl();
|
|
865
|
+
dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
|
|
866
|
+
dotenv.config({ path: path.join(moduleRoot, '.env'), override: true, quiet: true });
|
|
867
|
+
}
|
|
868
|
+
if (!payload.noCache) {
|
|
869
|
+
await self.cacheLogicService.execute(payload.cacheInterval, {
|
|
870
|
+
exchangeName,
|
|
871
|
+
frameName,
|
|
872
|
+
symbol,
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
},
|
|
876
|
+
};
|
|
845
877
|
addWalkerSchema({
|
|
846
878
|
walkerName: WALKER_NAME,
|
|
847
879
|
exchangeName,
|
|
848
880
|
frameName,
|
|
849
881
|
strategies: strategyNames,
|
|
882
|
+
callbacks,
|
|
850
883
|
});
|
|
851
|
-
if (!payload.noCache) {
|
|
852
|
-
await this.cacheLogicService.execute(payload.cacheInterval, {
|
|
853
|
-
exchangeName,
|
|
854
|
-
frameName,
|
|
855
|
-
symbol,
|
|
856
|
-
});
|
|
857
|
-
}
|
|
858
884
|
if (payload.verbose) {
|
|
859
885
|
overrideExchangeSchema({
|
|
860
886
|
exchangeName,
|
|
@@ -870,8 +896,9 @@ class WalkerMainService {
|
|
|
870
896
|
overrideWalkerSchema({
|
|
871
897
|
walkerName: WALKER_NAME,
|
|
872
898
|
callbacks: {
|
|
873
|
-
onStrategyStart(strategyName, symbol) {
|
|
899
|
+
async onStrategyStart(strategyName, symbol) {
|
|
874
900
|
console.log(`Strategy started: ${strategyName} for symbol: ${symbol}`);
|
|
901
|
+
await callbacks.onStrategyStart(strategyName);
|
|
875
902
|
},
|
|
876
903
|
onStrategyError(strategyName, symbol, error) {
|
|
877
904
|
console.error(`Strategy error: ${strategyName} for symbol: ${symbol}`, error);
|
|
@@ -892,9 +919,12 @@ class WalkerMainService {
|
|
|
892
919
|
unWalker();
|
|
893
920
|
res();
|
|
894
921
|
});
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
922
|
+
{
|
|
923
|
+
payload.verbose && console.time("Walker");
|
|
924
|
+
await awaiter;
|
|
925
|
+
payload.verbose && console.timeEnd("Walker");
|
|
926
|
+
}
|
|
927
|
+
process.chdir(cwd);
|
|
898
928
|
const dumpName = payload.output || `walker_${symbol}_${Date.now()}`;
|
|
899
929
|
const dumpDir = join(process.cwd(), "dump");
|
|
900
930
|
if (payload.json) {
|
|
@@ -2094,7 +2124,7 @@ const TRANSPILE_FN = memoize(([path]) => `${path}`, (path, code, self, require)
|
|
|
2094
2124
|
eval(self.params.babel.transpile(code));
|
|
2095
2125
|
}
|
|
2096
2126
|
catch (error) {
|
|
2097
|
-
console.log(`Error during transpilation error=\`${getErrorMessage(error)}\` path
|
|
2127
|
+
console.log(`Error during transpilation error=\`${getErrorMessage(error)}\` path=\`${path}\` __filename=\`${__filename}\` __dirname=\`${__dirname}\``);
|
|
2098
2128
|
process.exit(-1);
|
|
2099
2129
|
}
|
|
2100
2130
|
return {
|
|
@@ -2397,7 +2427,7 @@ const main$b = async () => {
|
|
|
2397
2427
|
if (MODES.some((mode) => values[mode])) {
|
|
2398
2428
|
return;
|
|
2399
2429
|
}
|
|
2400
|
-
process.stdout.write(`@backtest-kit/cli ${"6.2.
|
|
2430
|
+
process.stdout.write(`@backtest-kit/cli ${"6.2.1"}\n`);
|
|
2401
2431
|
process.stdout.write("\n");
|
|
2402
2432
|
process.stdout.write(`Run with --help to see available commands.\n`);
|
|
2403
2433
|
process.stdout.write("\n");
|
|
@@ -2798,6 +2828,20 @@ function runScript(scriptPath, cwd) {
|
|
|
2798
2828
|
child.on("error", reject);
|
|
2799
2829
|
});
|
|
2800
2830
|
}
|
|
2831
|
+
function runNpmInstall(cwd) {
|
|
2832
|
+
return new Promise((resolve, reject) => {
|
|
2833
|
+
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
2834
|
+
const child = spawn(npm, ["install"], { cwd, stdio: "inherit", shell: true });
|
|
2835
|
+
child.on("close", (code) => {
|
|
2836
|
+
if (code !== 0) {
|
|
2837
|
+
reject(new Error(`npm install exited with code ${code}`));
|
|
2838
|
+
return;
|
|
2839
|
+
}
|
|
2840
|
+
resolve();
|
|
2841
|
+
});
|
|
2842
|
+
child.on("error", reject);
|
|
2843
|
+
});
|
|
2844
|
+
}
|
|
2801
2845
|
const main$2 = async () => {
|
|
2802
2846
|
if (!getEntry(import.meta.url)) {
|
|
2803
2847
|
return;
|
|
@@ -2818,6 +2862,8 @@ const main$2 = async () => {
|
|
|
2818
2862
|
await copyDir(templatePath, projectPath, { PROJECT_NAME: projectName });
|
|
2819
2863
|
console.log(`Fetching docs...`);
|
|
2820
2864
|
await runScript(join(projectPath, "scripts/fetch_docs.mjs"), projectPath);
|
|
2865
|
+
console.log(`Installing dependencies...`);
|
|
2866
|
+
await runNpmInstall(projectPath);
|
|
2821
2867
|
console.log(`Done! Project created at ${projectPath}`);
|
|
2822
2868
|
process.exit(0);
|
|
2823
2869
|
};
|
|
@@ -2949,7 +2995,7 @@ const main$1 = async () => {
|
|
|
2949
2995
|
if (!values.help) {
|
|
2950
2996
|
return;
|
|
2951
2997
|
}
|
|
2952
|
-
process.stdout.write(`@backtest-kit/cli ${"6.2.
|
|
2998
|
+
process.stdout.write(`@backtest-kit/cli ${"6.2.1"}\n\n`);
|
|
2953
2999
|
process.stdout.write(HELP_TEXT);
|
|
2954
3000
|
process.exit(0);
|
|
2955
3001
|
};
|
|
@@ -2963,7 +3009,7 @@ const main = async () => {
|
|
|
2963
3009
|
if (!values.version) {
|
|
2964
3010
|
return;
|
|
2965
3011
|
}
|
|
2966
|
-
process.stdout.write(`@backtest-kit/cli ${"6.2.
|
|
3012
|
+
process.stdout.write(`@backtest-kit/cli ${"6.2.1"}\n`);
|
|
2967
3013
|
process.exit(0);
|
|
2968
3014
|
};
|
|
2969
3015
|
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backtest-kit/cli",
|
|
3
|
-
"version": "6.2.
|
|
3
|
+
"version": "6.2.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",
|
|
@@ -87,7 +87,7 @@ npm start -- --live --symbol BTCUSDT --ui --telegram ./content/feb_2026.strategy
|
|
|
87
87
|
|
|
88
88
|
Module file `./modules/live.module.ts` is loaded automatically if it exists. Use it to register a `Broker` adapter that intercepts every trade mutation before internal state changes — exchange rejection rolls back the operation atomically.
|
|
89
89
|
|
|
90
|
-
##
|
|
90
|
+
## ⚖️ Walker — A/B Strategy Comparison (`--walker`)
|
|
91
91
|
|
|
92
92
|
Runs the same historical period across multiple strategy files and prints a ranked comparison report. Use it to pick the best variant before committing to a single strategy.
|
|
93
93
|
|