@backtest-kit/cli 6.1.5 → 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/README.md +60 -1
- package/build/index.cjs +440 -149
- package/build/index.mjs +441 -150
- package/package.json +13 -13
- package/template/project/README.md +29 -0
- package/template/project/package.mustache +5 -5
- package/types.d.ts +22 -0
package/build/index.cjs
CHANGED
|
@@ -249,6 +249,7 @@ const connectionServices$1 = {
|
|
|
249
249
|
};
|
|
250
250
|
const mainServices$1 = {
|
|
251
251
|
backtestMainService: Symbol('backtestMainService'),
|
|
252
|
+
walkerMainService: Symbol('walkerMainService'),
|
|
252
253
|
paperMainService: Symbol('paperMainService'),
|
|
253
254
|
liveMainService: Symbol('liveMainService'),
|
|
254
255
|
};
|
|
@@ -317,6 +318,19 @@ class ResolveService {
|
|
|
317
318
|
_is_launched = true;
|
|
318
319
|
return await fs$1.readFile(absolutePath, "utf-8");
|
|
319
320
|
};
|
|
321
|
+
this.attachStrategy = async (jsPath) => {
|
|
322
|
+
this.loggerService.log("resolveService attachStrategy", {
|
|
323
|
+
jsPath
|
|
324
|
+
});
|
|
325
|
+
const absolutePath = path.resolve(jsPath);
|
|
326
|
+
await fs$1.access(absolutePath, fs.constants.F_OK | fs.constants.R_OK);
|
|
327
|
+
const moduleRoot = path.dirname(absolutePath);
|
|
328
|
+
{
|
|
329
|
+
const cwd = process.cwd();
|
|
330
|
+
dotenv.config({ path: path.join(cwd, '.env'), override: true, quiet: true });
|
|
331
|
+
}
|
|
332
|
+
this.loaderService.import(absolutePath, moduleRoot);
|
|
333
|
+
};
|
|
320
334
|
this.attachJavascript = async (jsPath) => {
|
|
321
335
|
this.loggerService.log("resolveService attachJavascript", {
|
|
322
336
|
jsPath
|
|
@@ -484,6 +498,10 @@ const getArgs = functoolsKit.singleshot(() => {
|
|
|
484
498
|
type: "boolean",
|
|
485
499
|
default: false,
|
|
486
500
|
},
|
|
501
|
+
walker: {
|
|
502
|
+
type: "boolean",
|
|
503
|
+
default: false,
|
|
504
|
+
},
|
|
487
505
|
live: {
|
|
488
506
|
type: "boolean",
|
|
489
507
|
default: false,
|
|
@@ -574,12 +592,11 @@ const getArgs = functoolsKit.singleshot(() => {
|
|
|
574
592
|
positionals,
|
|
575
593
|
};
|
|
576
594
|
});
|
|
577
|
-
const
|
|
595
|
+
const getPositionals = functoolsKit.singleshot(() => {
|
|
578
596
|
const { positionals = [] } = getArgs();
|
|
579
|
-
|
|
597
|
+
return positionals
|
|
580
598
|
.filter((value) => !DISALLOWED_PATHS.some((path) => value.includes(path)))
|
|
581
|
-
.
|
|
582
|
-
return result || null;
|
|
599
|
+
.filter((value) => ALLOWED_EXTENSIONS.some((ext) => value.endsWith(ext)));
|
|
583
600
|
});
|
|
584
601
|
|
|
585
602
|
const ADD_FRAME_FN = (self) => {
|
|
@@ -684,11 +701,11 @@ const notifyVerbose = functoolsKit.singleshot(() => {
|
|
|
684
701
|
});
|
|
685
702
|
});
|
|
686
703
|
|
|
687
|
-
const DEFAULT_CACHE_LIST = ["1m", "15m", "30m", "1h", "4h"];
|
|
688
|
-
const GET_CACHE_INTERVAL_LIST_FN = () => {
|
|
704
|
+
const DEFAULT_CACHE_LIST$1 = ["1m", "15m", "30m", "1h", "4h"];
|
|
705
|
+
const GET_CACHE_INTERVAL_LIST_FN$1 = () => {
|
|
689
706
|
const { values } = getArgs();
|
|
690
707
|
if (!values.cacheInterval) {
|
|
691
|
-
return DEFAULT_CACHE_LIST;
|
|
708
|
+
return DEFAULT_CACHE_LIST$1;
|
|
692
709
|
}
|
|
693
710
|
return String(values.cacheInterval)
|
|
694
711
|
.split(",")
|
|
@@ -713,7 +730,10 @@ class BacktestMainService {
|
|
|
713
730
|
this.frontendProviderService.connect();
|
|
714
731
|
this.telegramProviderService.connect();
|
|
715
732
|
}
|
|
716
|
-
|
|
733
|
+
{
|
|
734
|
+
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
735
|
+
await this.moduleConnectionService.loadModule("./backtest.module");
|
|
736
|
+
}
|
|
717
737
|
{
|
|
718
738
|
this.exchangeSchemaService.addSchema();
|
|
719
739
|
this.symbolSchemaService.addSchema();
|
|
@@ -753,7 +773,6 @@ class BacktestMainService {
|
|
|
753
773
|
});
|
|
754
774
|
notifyVerbose();
|
|
755
775
|
}
|
|
756
|
-
await this.moduleConnectionService.loadModule("./backtest.module");
|
|
757
776
|
BacktestKit.Backtest.background(symbol, {
|
|
758
777
|
strategyName,
|
|
759
778
|
frameName,
|
|
@@ -770,11 +789,11 @@ class BacktestMainService {
|
|
|
770
789
|
if (!values.backtest) {
|
|
771
790
|
return;
|
|
772
791
|
}
|
|
773
|
-
const entryPoint =
|
|
792
|
+
const [entryPoint = null] = getPositionals();
|
|
774
793
|
if (!entryPoint) {
|
|
775
794
|
throw new Error("Entry point is required");
|
|
776
795
|
}
|
|
777
|
-
const cacheInterval = GET_CACHE_INTERVAL_LIST_FN();
|
|
796
|
+
const cacheInterval = GET_CACHE_INTERVAL_LIST_FN$1();
|
|
778
797
|
return await this.run({
|
|
779
798
|
symbol: values.symbol,
|
|
780
799
|
entryPoint,
|
|
@@ -789,6 +808,200 @@ class BacktestMainService {
|
|
|
789
808
|
}
|
|
790
809
|
}
|
|
791
810
|
|
|
811
|
+
const DEFAULT_CACHE_LIST = ["1m", "15m", "30m", "1h", "4h"];
|
|
812
|
+
const WALKER_NAME = "cli-walker";
|
|
813
|
+
const GET_CACHE_INTERVAL_LIST_FN = () => {
|
|
814
|
+
const { values } = getArgs();
|
|
815
|
+
if (!values.cacheInterval) {
|
|
816
|
+
return DEFAULT_CACHE_LIST;
|
|
817
|
+
}
|
|
818
|
+
return String(values.cacheInterval)
|
|
819
|
+
.split(",")
|
|
820
|
+
.map((timeframe) => timeframe.trim());
|
|
821
|
+
};
|
|
822
|
+
class WalkerMainService {
|
|
823
|
+
constructor() {
|
|
824
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
825
|
+
this.resolveService = inject(TYPES.resolveService);
|
|
826
|
+
this.exchangeSchemaService = inject(TYPES.exchangeSchemaService);
|
|
827
|
+
this.symbolSchemaService = inject(TYPES.symbolSchemaService);
|
|
828
|
+
this.cacheLogicService = inject(TYPES.cacheLogicService);
|
|
829
|
+
this.moduleConnectionService = inject(TYPES.moduleConnectionService);
|
|
830
|
+
this.run = functoolsKit.singleshot(async (payload) => {
|
|
831
|
+
this.loggerService.log("walkerMainService run", { payload });
|
|
832
|
+
const strategyMap = new Map();
|
|
833
|
+
for (const entryPoint of payload.entryPoints) {
|
|
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
|
+
}
|
|
841
|
+
}
|
|
842
|
+
await this.moduleConnectionService.loadModule("./walker.module");
|
|
843
|
+
{
|
|
844
|
+
this.exchangeSchemaService.addSchema();
|
|
845
|
+
this.symbolSchemaService.addSchema();
|
|
846
|
+
}
|
|
847
|
+
{
|
|
848
|
+
const { length } = await BacktestKit.listFrameSchema();
|
|
849
|
+
if (!length) {
|
|
850
|
+
const endDate = BacktestKit.alignToInterval(new Date(Date.now() - 1 * 24 * 60 * 60 * 1000), "1m");
|
|
851
|
+
const startDate = BacktestKit.alignToInterval(new Date(Date.now() - 31 * 24 * 60 * 60 * 1000), "1m");
|
|
852
|
+
console.warn(`Warning: The default frame schema is set to the interval ${startDate.toISOString()} — ${endDate.toISOString()}. Please make sure to update it according to your needs using addFrameSchema in your strategy files.`);
|
|
853
|
+
BacktestKit.addFrameSchema({
|
|
854
|
+
frameName: FrameName$1.DefaultFrame,
|
|
855
|
+
interval: "1m",
|
|
856
|
+
startDate,
|
|
857
|
+
endDate,
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
const symbol = payload.symbol || "BTCUSDT";
|
|
862
|
+
const strategyList = await BacktestKit.listStrategySchema();
|
|
863
|
+
const strategyNames = strategyList.map((s) => s.strategyName);
|
|
864
|
+
if (!strategyNames.length) {
|
|
865
|
+
throw new Error("No strategies found in provided entry points");
|
|
866
|
+
}
|
|
867
|
+
const [defaultExchangeName = null] = await BacktestKit.listExchangeSchema();
|
|
868
|
+
const [defaultFrameName = null] = await BacktestKit.listFrameSchema();
|
|
869
|
+
const exchangeName = defaultExchangeName?.exchangeName;
|
|
870
|
+
const frameName = defaultFrameName?.frameName;
|
|
871
|
+
if (!exchangeName) {
|
|
872
|
+
throw new Error("Exchange name is required");
|
|
873
|
+
}
|
|
874
|
+
if (!frameName) {
|
|
875
|
+
throw new Error("Frame name is required");
|
|
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
|
+
};
|
|
902
|
+
BacktestKit.addWalkerSchema({
|
|
903
|
+
walkerName: WALKER_NAME,
|
|
904
|
+
exchangeName,
|
|
905
|
+
frameName,
|
|
906
|
+
strategies: strategyNames,
|
|
907
|
+
callbacks,
|
|
908
|
+
});
|
|
909
|
+
if (payload.verbose) {
|
|
910
|
+
BacktestKit.overrideExchangeSchema({
|
|
911
|
+
exchangeName,
|
|
912
|
+
callbacks: {
|
|
913
|
+
onCandleData(symbol, interval, since) {
|
|
914
|
+
console.log(`Received candle data for symbol: ${symbol}, interval: ${interval}, since: ${since.toUTCString()}`);
|
|
915
|
+
},
|
|
916
|
+
},
|
|
917
|
+
});
|
|
918
|
+
notifyVerbose();
|
|
919
|
+
}
|
|
920
|
+
if (payload.verbose) {
|
|
921
|
+
BacktestKit.overrideWalkerSchema({
|
|
922
|
+
walkerName: WALKER_NAME,
|
|
923
|
+
callbacks: {
|
|
924
|
+
async onStrategyStart(strategyName, symbol) {
|
|
925
|
+
console.log(`Strategy started: ${strategyName} for symbol: ${symbol}`);
|
|
926
|
+
await callbacks.onStrategyStart(strategyName);
|
|
927
|
+
},
|
|
928
|
+
onStrategyError(strategyName, symbol, error) {
|
|
929
|
+
console.error(`Strategy error: ${strategyName} for symbol: ${symbol}`, error);
|
|
930
|
+
},
|
|
931
|
+
onStrategyComplete(strategyName, symbol) {
|
|
932
|
+
console.log(`Strategy completed: ${strategyName} for symbol: ${symbol}`);
|
|
933
|
+
},
|
|
934
|
+
onComplete(results) {
|
|
935
|
+
console.log(`Walker completed for symbol: ${results.symbol}`, results);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
BacktestKit.Walker.background(symbol, { walkerName: WALKER_NAME });
|
|
941
|
+
const [awaiter, { resolve: res }] = functoolsKit.createAwaiter();
|
|
942
|
+
const unWalker = BacktestKit.listenDoneWalker(() => {
|
|
943
|
+
console.log("Walker comparison finished");
|
|
944
|
+
unWalker();
|
|
945
|
+
res();
|
|
946
|
+
});
|
|
947
|
+
{
|
|
948
|
+
payload.verbose && console.time("Walker");
|
|
949
|
+
await awaiter;
|
|
950
|
+
payload.verbose && console.timeEnd("Walker");
|
|
951
|
+
}
|
|
952
|
+
process.chdir(cwd);
|
|
953
|
+
const dumpName = payload.output || `walker_${symbol}_${Date.now()}`;
|
|
954
|
+
const dumpDir = path.join(process.cwd(), "dump");
|
|
955
|
+
if (payload.json) {
|
|
956
|
+
const filePath = path.resolve(dumpDir, `${dumpName}.json`);
|
|
957
|
+
const data = await BacktestKit.Walker.getData(symbol, { walkerName: WALKER_NAME });
|
|
958
|
+
await fs$1.mkdir(dumpDir, { recursive: true });
|
|
959
|
+
await fs$1.writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
960
|
+
console.log(`Saved: ${filePath}`);
|
|
961
|
+
process.exit(0);
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
if (payload.markdown) {
|
|
965
|
+
const filePath = path.resolve(dumpDir, `${dumpName}.md`);
|
|
966
|
+
const report = await BacktestKit.Walker.getReport(symbol, { walkerName: WALKER_NAME });
|
|
967
|
+
await fs$1.mkdir(dumpDir, { recursive: true });
|
|
968
|
+
await fs$1.writeFile(filePath, report, "utf-8");
|
|
969
|
+
console.log(`Saved: ${filePath}`);
|
|
970
|
+
process.exit(0);
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
const report = await BacktestKit.Walker.getReport(symbol, { walkerName: WALKER_NAME });
|
|
974
|
+
console.log(report);
|
|
975
|
+
process.exit(0);
|
|
976
|
+
});
|
|
977
|
+
this.connect = functoolsKit.singleshot(async () => {
|
|
978
|
+
this.loggerService.log("walkerMainService connect");
|
|
979
|
+
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)))) {
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
const { values } = getArgs();
|
|
983
|
+
if (!values.walker) {
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
const entryPoints = getPositionals();
|
|
987
|
+
if (!entryPoints.length) {
|
|
988
|
+
throw new Error("At least one entry point is required");
|
|
989
|
+
}
|
|
990
|
+
const cacheInterval = GET_CACHE_INTERVAL_LIST_FN();
|
|
991
|
+
return await this.run({
|
|
992
|
+
entryPoints,
|
|
993
|
+
json: values.json,
|
|
994
|
+
markdown: values.markdown,
|
|
995
|
+
symbol: values.symbol,
|
|
996
|
+
output: values.output,
|
|
997
|
+
cacheInterval,
|
|
998
|
+
verbose: values.verbose,
|
|
999
|
+
noCache: values.noCache,
|
|
1000
|
+
});
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
792
1005
|
class LiveMainService {
|
|
793
1006
|
constructor() {
|
|
794
1007
|
this.loggerService = inject(TYPES.loggerService);
|
|
@@ -806,7 +1019,10 @@ class LiveMainService {
|
|
|
806
1019
|
this.frontendProviderService.connect();
|
|
807
1020
|
this.telegramProviderService.connect();
|
|
808
1021
|
}
|
|
809
|
-
|
|
1022
|
+
{
|
|
1023
|
+
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
1024
|
+
await this.moduleConnectionService.loadModule("./live.module");
|
|
1025
|
+
}
|
|
810
1026
|
{
|
|
811
1027
|
this.exchangeSchemaService.addSchema();
|
|
812
1028
|
this.symbolSchemaService.addSchema();
|
|
@@ -833,7 +1049,6 @@ class LiveMainService {
|
|
|
833
1049
|
});
|
|
834
1050
|
notifyVerbose();
|
|
835
1051
|
}
|
|
836
|
-
await this.moduleConnectionService.loadModule("./live.module");
|
|
837
1052
|
BacktestKit.Live.background(symbol, {
|
|
838
1053
|
strategyName,
|
|
839
1054
|
exchangeName,
|
|
@@ -849,7 +1064,7 @@ class LiveMainService {
|
|
|
849
1064
|
if (!values.live) {
|
|
850
1065
|
return;
|
|
851
1066
|
}
|
|
852
|
-
const entryPoint =
|
|
1067
|
+
const [entryPoint = null] = getPositionals();
|
|
853
1068
|
if (!entryPoint) {
|
|
854
1069
|
throw new Error("Entry point is required");
|
|
855
1070
|
}
|
|
@@ -879,7 +1094,10 @@ class PaperMainService {
|
|
|
879
1094
|
this.frontendProviderService.connect();
|
|
880
1095
|
this.telegramProviderService.connect();
|
|
881
1096
|
}
|
|
882
|
-
|
|
1097
|
+
{
|
|
1098
|
+
await this.resolveService.attachJavascript(payload.entryPoint);
|
|
1099
|
+
await this.moduleConnectionService.loadModule("./paper.module");
|
|
1100
|
+
}
|
|
883
1101
|
{
|
|
884
1102
|
this.exchangeSchemaService.addSchema();
|
|
885
1103
|
this.symbolSchemaService.addSchema();
|
|
@@ -906,7 +1124,6 @@ class PaperMainService {
|
|
|
906
1124
|
});
|
|
907
1125
|
notifyVerbose();
|
|
908
1126
|
}
|
|
909
|
-
await this.moduleConnectionService.loadModule("./paper.module");
|
|
910
1127
|
BacktestKit.Live.background(symbol, {
|
|
911
1128
|
strategyName,
|
|
912
1129
|
exchangeName,
|
|
@@ -922,7 +1139,7 @@ class PaperMainService {
|
|
|
922
1139
|
if (!values.paper) {
|
|
923
1140
|
return;
|
|
924
1141
|
}
|
|
925
|
-
const entryPoint =
|
|
1142
|
+
const [entryPoint = null] = getPositionals();
|
|
926
1143
|
if (!entryPoint) {
|
|
927
1144
|
throw new Error("Entry point is required");
|
|
928
1145
|
}
|
|
@@ -1923,8 +2140,7 @@ class BabelService {
|
|
|
1923
2140
|
}
|
|
1924
2141
|
}
|
|
1925
2142
|
|
|
1926
|
-
const TRANSPILE_FN = (code, self) => {
|
|
1927
|
-
const require = self.getBaseRequire();
|
|
2143
|
+
const TRANSPILE_FN = functoolsKit.memoize(([path]) => `${path}`, (path, code, self, require) => {
|
|
1928
2144
|
const __filename = self.__filename;
|
|
1929
2145
|
const __dirname = self.__dirname;
|
|
1930
2146
|
const module = { exports: {} };
|
|
@@ -1933,7 +2149,7 @@ const TRANSPILE_FN = (code, self) => {
|
|
|
1933
2149
|
eval(self.params.babel.transpile(code));
|
|
1934
2150
|
}
|
|
1935
2151
|
catch (error) {
|
|
1936
|
-
console.log(`Error during transpilation error=\`${functoolsKit.getErrorMessage(error)}\` __filename=\`${__filename}\` __dirname=\`${__dirname}\``);
|
|
2152
|
+
console.log(`Error during transpilation error=\`${functoolsKit.getErrorMessage(error)}\` path=\`${path}\` __filename=\`${__filename}\` __dirname=\`${__dirname}\``);
|
|
1937
2153
|
process.exit(-1);
|
|
1938
2154
|
}
|
|
1939
2155
|
return {
|
|
@@ -1943,9 +2159,9 @@ const TRANSPILE_FN = (code, self) => {
|
|
|
1943
2159
|
exports,
|
|
1944
2160
|
module,
|
|
1945
2161
|
};
|
|
1946
|
-
};
|
|
1947
|
-
const REQUIRE_ENTRY_FACTORY = (filePath, self) => {
|
|
1948
|
-
const baseRequire = self
|
|
2162
|
+
});
|
|
2163
|
+
const REQUIRE_ENTRY_FACTORY = (filePath, self, seen) => {
|
|
2164
|
+
const baseRequire = CREATE_BASE_REQUIRE_FN(self, seen);
|
|
1949
2165
|
try {
|
|
1950
2166
|
return baseRequire(filePath);
|
|
1951
2167
|
}
|
|
@@ -1953,12 +2169,12 @@ const REQUIRE_ENTRY_FACTORY = (filePath, self) => {
|
|
|
1953
2169
|
return null;
|
|
1954
2170
|
}
|
|
1955
2171
|
};
|
|
1956
|
-
const BABEL_ENTRY_FACTORY = (filePath, self) => {
|
|
2172
|
+
const BABEL_ENTRY_FACTORY = (filePath, self, seen) => {
|
|
1957
2173
|
try {
|
|
1958
2174
|
const resolvedPath = path.resolve(self.__dirname, filePath);
|
|
1959
2175
|
const code = fs.readFileSync(resolvedPath, "utf-8");
|
|
1960
2176
|
const child = self.fork(path.dirname(resolvedPath));
|
|
1961
|
-
const { module } = TRANSPILE_FN(code, child);
|
|
2177
|
+
const { module } = TRANSPILE_FN(resolvedPath, code, child, CREATE_BASE_REQUIRE_FN(child, seen));
|
|
1962
2178
|
return "default" in module.exports
|
|
1963
2179
|
? module.exports.default
|
|
1964
2180
|
: module.exports;
|
|
@@ -1999,21 +2215,20 @@ const GET_RESOLVED_EXT_FN = (filePath) => {
|
|
|
1999
2215
|
}
|
|
2000
2216
|
return filePath;
|
|
2001
2217
|
};
|
|
2002
|
-
const ENTRY_FACTORY = (filePath, self) => {
|
|
2003
|
-
filePath = GET_RESOLVED_EXT_FN(filePath);
|
|
2218
|
+
const ENTRY_FACTORY = (filePath, self, seen) => {
|
|
2004
2219
|
{
|
|
2005
2220
|
let result = null;
|
|
2006
|
-
if ((result = REQUIRE_ENTRY_FACTORY(filePath, self))) {
|
|
2221
|
+
if ((result = REQUIRE_ENTRY_FACTORY(filePath, self, seen))) {
|
|
2007
2222
|
return result;
|
|
2008
2223
|
}
|
|
2009
|
-
if ((result = BABEL_ENTRY_FACTORY(filePath, self))) {
|
|
2224
|
+
if ((result = BABEL_ENTRY_FACTORY(filePath, self, seen))) {
|
|
2010
2225
|
return result;
|
|
2011
2226
|
}
|
|
2012
2227
|
}
|
|
2013
2228
|
throw new Error(`Failed to load module at ${filePath} (basepath: ${self.params.path})`);
|
|
2014
2229
|
};
|
|
2015
|
-
const CREATE_BASE_REQUIRE_FN = (self) => {
|
|
2016
|
-
const baseRequire =
|
|
2230
|
+
const CREATE_BASE_REQUIRE_FN = (self, seen) => {
|
|
2231
|
+
const baseRequire = self.baseRequire();
|
|
2017
2232
|
return new Proxy(baseRequire, {
|
|
2018
2233
|
apply(_target, _this, args) {
|
|
2019
2234
|
const id = args[0];
|
|
@@ -2034,7 +2249,7 @@ const CREATE_BASE_REQUIRE_FN = (self) => {
|
|
|
2034
2249
|
if (id.startsWith("./") || id.startsWith("../")) {
|
|
2035
2250
|
const resolved = path.resolve(self.__dirname, id);
|
|
2036
2251
|
const child = self.fork(path.dirname(resolved));
|
|
2037
|
-
return child.import(resolved);
|
|
2252
|
+
return child.import(resolved, seen);
|
|
2038
2253
|
}
|
|
2039
2254
|
return baseRequire(id);
|
|
2040
2255
|
},
|
|
@@ -2048,11 +2263,11 @@ const BacktestKitCli = new Proxy({}, {
|
|
|
2048
2263
|
class ClientLoader {
|
|
2049
2264
|
constructor(params) {
|
|
2050
2265
|
this.params = params;
|
|
2051
|
-
this.
|
|
2052
|
-
this.params.logger.log("ClientLoader
|
|
2266
|
+
this.baseRequire = functoolsKit.singleshot(() => {
|
|
2267
|
+
this.params.logger.log("ClientLoader baseRequire", {
|
|
2053
2268
|
basePath: this.params.path,
|
|
2054
2269
|
});
|
|
2055
|
-
return
|
|
2270
|
+
return module$1.createRequire(this.__filename);
|
|
2056
2271
|
});
|
|
2057
2272
|
this.__filename = path.join(params.path, "index.cjs");
|
|
2058
2273
|
this.__dirname = path.dirname(this.__filename);
|
|
@@ -2068,12 +2283,21 @@ class ClientLoader {
|
|
|
2068
2283
|
logger: this.params.logger,
|
|
2069
2284
|
});
|
|
2070
2285
|
}
|
|
2071
|
-
import(filePath) {
|
|
2286
|
+
import(filePath, seen = new Set()) {
|
|
2072
2287
|
this.params.logger.log("ClientLoader import", {
|
|
2073
2288
|
filePath,
|
|
2074
2289
|
basePath: this.params.path,
|
|
2075
2290
|
});
|
|
2076
|
-
|
|
2291
|
+
const resolved = GET_RESOLVED_EXT_FN(filePath);
|
|
2292
|
+
if (seen.has(resolved)) {
|
|
2293
|
+
throw new Error(`Circular dependency detected: ${resolved} (seen: ${[...seen].join("->")}->${resolved})`);
|
|
2294
|
+
}
|
|
2295
|
+
const currentSeen = new Set(seen);
|
|
2296
|
+
if (!seen.size) {
|
|
2297
|
+
currentSeen.add(path.resolve(this.__dirname, filePath));
|
|
2298
|
+
}
|
|
2299
|
+
currentSeen.add(resolved);
|
|
2300
|
+
return ENTRY_FACTORY(resolved, this, currentSeen);
|
|
2077
2301
|
}
|
|
2078
2302
|
check(filePath) {
|
|
2079
2303
|
this.params.logger.log("ClientLoader check", {
|
|
@@ -2141,6 +2365,7 @@ class LoaderService {
|
|
|
2141
2365
|
}
|
|
2142
2366
|
{
|
|
2143
2367
|
provide(TYPES.backtestMainService, () => new BacktestMainService());
|
|
2368
|
+
provide(TYPES.walkerMainService, () => new WalkerMainService());
|
|
2144
2369
|
provide(TYPES.paperMainService, () => new PaperMainService());
|
|
2145
2370
|
provide(TYPES.liveMainService, () => new LiveMainService());
|
|
2146
2371
|
}
|
|
@@ -2180,6 +2405,7 @@ const connectionServices = {
|
|
|
2180
2405
|
};
|
|
2181
2406
|
const mainServices = {
|
|
2182
2407
|
backtestMainService: inject(TYPES.backtestMainService),
|
|
2408
|
+
walkerMainService: inject(TYPES.walkerMainService),
|
|
2183
2409
|
paperMainService: inject(TYPES.paperMainService),
|
|
2184
2410
|
liveMainService: inject(TYPES.liveMainService),
|
|
2185
2411
|
};
|
|
@@ -2215,14 +2441,14 @@ const cli = {
|
|
|
2215
2441
|
};
|
|
2216
2442
|
init();
|
|
2217
2443
|
|
|
2218
|
-
const MODES = ["backtest", "paper", "live", "pine", "dump", "init", "help", "version"];
|
|
2444
|
+
const MODES = ["backtest", "walker", "paper", "live", "pine", "dump", "init", "help", "version"];
|
|
2219
2445
|
const ENTRY_PATH$1 = "./node_modules/@backtest-kit/cli/build/index.mjs";
|
|
2220
|
-
const HELP_TEXT$1 = `
|
|
2221
|
-
Example:
|
|
2222
|
-
|
|
2223
|
-
node ${ENTRY_PATH$1} --help
|
|
2446
|
+
const HELP_TEXT$1 = `
|
|
2447
|
+
Example:
|
|
2448
|
+
|
|
2449
|
+
node ${ENTRY_PATH$1} --help
|
|
2224
2450
|
`.trimStart();
|
|
2225
|
-
const main$
|
|
2451
|
+
const main$b = async () => {
|
|
2226
2452
|
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)))) {
|
|
2227
2453
|
return;
|
|
2228
2454
|
}
|
|
@@ -2230,21 +2456,21 @@ const main$a = async () => {
|
|
|
2230
2456
|
if (MODES.some((mode) => values[mode])) {
|
|
2231
2457
|
return;
|
|
2232
2458
|
}
|
|
2233
|
-
process.stdout.write(`@backtest-kit/cli ${"6.
|
|
2459
|
+
process.stdout.write(`@backtest-kit/cli ${"6.2.1"}\n`);
|
|
2234
2460
|
process.stdout.write("\n");
|
|
2235
2461
|
process.stdout.write(`Run with --help to see available commands.\n`);
|
|
2236
2462
|
process.stdout.write("\n");
|
|
2237
2463
|
process.stdout.write(HELP_TEXT$1);
|
|
2238
2464
|
process.exit(0);
|
|
2239
2465
|
};
|
|
2240
|
-
main$
|
|
2466
|
+
main$b();
|
|
2241
2467
|
|
|
2242
2468
|
const notifyShutdown = functoolsKit.singleshot(async () => {
|
|
2243
2469
|
console.log("Graceful shutdown initiated. Press Ctrl+C again to force quit.");
|
|
2244
2470
|
});
|
|
2245
2471
|
|
|
2246
|
-
const BEFORE_EXIT_FN$
|
|
2247
|
-
process.off("SIGINT", BEFORE_EXIT_FN$
|
|
2472
|
+
const BEFORE_EXIT_FN$5 = functoolsKit.singleshot(async () => {
|
|
2473
|
+
process.off("SIGINT", BEFORE_EXIT_FN$5);
|
|
2248
2474
|
const [running = null] = await BacktestKit.Backtest.list();
|
|
2249
2475
|
if (!running) {
|
|
2250
2476
|
return;
|
|
@@ -2260,6 +2486,35 @@ const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
|
|
|
2260
2486
|
frameName,
|
|
2261
2487
|
});
|
|
2262
2488
|
});
|
|
2489
|
+
const listenGracefulShutdown$5 = functoolsKit.singleshot(() => {
|
|
2490
|
+
process.on("SIGINT", BEFORE_EXIT_FN$5);
|
|
2491
|
+
});
|
|
2492
|
+
const main$a = async () => {
|
|
2493
|
+
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)))) {
|
|
2494
|
+
return;
|
|
2495
|
+
}
|
|
2496
|
+
const { values } = getArgs();
|
|
2497
|
+
if (!values.backtest) {
|
|
2498
|
+
return;
|
|
2499
|
+
}
|
|
2500
|
+
await cli.backtestMainService.connect();
|
|
2501
|
+
listenGracefulShutdown$5();
|
|
2502
|
+
};
|
|
2503
|
+
main$a();
|
|
2504
|
+
|
|
2505
|
+
const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
|
|
2506
|
+
process.off("SIGINT", BEFORE_EXIT_FN$4);
|
|
2507
|
+
const [running = null] = await BacktestKit.Walker.list();
|
|
2508
|
+
if (!running) {
|
|
2509
|
+
return;
|
|
2510
|
+
}
|
|
2511
|
+
notifyShutdown();
|
|
2512
|
+
const { walkerName, symbol, status } = running;
|
|
2513
|
+
if (status === "fulfilled") {
|
|
2514
|
+
return;
|
|
2515
|
+
}
|
|
2516
|
+
BacktestKit.Walker.stop(symbol, { walkerName });
|
|
2517
|
+
});
|
|
2263
2518
|
const listenGracefulShutdown$4 = functoolsKit.singleshot(() => {
|
|
2264
2519
|
process.on("SIGINT", BEFORE_EXIT_FN$4);
|
|
2265
2520
|
});
|
|
@@ -2268,11 +2523,11 @@ const main$9 = async () => {
|
|
|
2268
2523
|
return;
|
|
2269
2524
|
}
|
|
2270
2525
|
const { values } = getArgs();
|
|
2271
|
-
if (!values.
|
|
2526
|
+
if (!values.walker) {
|
|
2272
2527
|
return;
|
|
2273
2528
|
}
|
|
2274
|
-
await cli.backtestMainService.connect();
|
|
2275
2529
|
listenGracefulShutdown$4();
|
|
2530
|
+
await cli.walkerMainService.connect();
|
|
2276
2531
|
};
|
|
2277
2532
|
main$9();
|
|
2278
2533
|
|
|
@@ -2408,7 +2663,7 @@ const main$4 = async () => {
|
|
|
2408
2663
|
if (!values.pine) {
|
|
2409
2664
|
return;
|
|
2410
2665
|
}
|
|
2411
|
-
const entryPoint =
|
|
2666
|
+
const [entryPoint = null] = getPositionals();
|
|
2412
2667
|
if (!entryPoint) {
|
|
2413
2668
|
return;
|
|
2414
2669
|
}
|
|
@@ -2602,6 +2857,20 @@ function runScript(scriptPath, cwd) {
|
|
|
2602
2857
|
child.on("error", reject);
|
|
2603
2858
|
});
|
|
2604
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
|
+
}
|
|
2605
2874
|
const main$2 = async () => {
|
|
2606
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)))) {
|
|
2607
2876
|
return;
|
|
@@ -2622,109 +2891,130 @@ const main$2 = async () => {
|
|
|
2622
2891
|
await copyDir(templatePath, projectPath, { PROJECT_NAME: projectName });
|
|
2623
2892
|
console.log(`Fetching docs...`);
|
|
2624
2893
|
await runScript(path.join(projectPath, "scripts/fetch_docs.mjs"), projectPath);
|
|
2894
|
+
console.log(`Installing dependencies...`);
|
|
2895
|
+
await runNpmInstall(projectPath);
|
|
2625
2896
|
console.log(`Done! Project created at ${projectPath}`);
|
|
2626
2897
|
process.exit(0);
|
|
2627
2898
|
};
|
|
2628
2899
|
main$2();
|
|
2629
2900
|
|
|
2630
2901
|
const ENTRY_PATH = "./node_modules/@backtest-kit/cli/build/index.mjs";
|
|
2631
|
-
const HELP_TEXT = `
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
--
|
|
2640
|
-
--
|
|
2641
|
-
--
|
|
2642
|
-
--
|
|
2643
|
-
--
|
|
2644
|
-
--
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
--
|
|
2650
|
-
--
|
|
2651
|
-
--
|
|
2652
|
-
--
|
|
2653
|
-
--
|
|
2654
|
-
--
|
|
2655
|
-
--
|
|
2656
|
-
--
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
--
|
|
2662
|
-
--
|
|
2663
|
-
--
|
|
2664
|
-
--
|
|
2665
|
-
--
|
|
2666
|
-
--
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
--
|
|
2678
|
-
--
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
--
|
|
2687
|
-
--
|
|
2688
|
-
--
|
|
2689
|
-
--
|
|
2690
|
-
--
|
|
2691
|
-
--
|
|
2692
|
-
--
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2902
|
+
const HELP_TEXT = `
|
|
2903
|
+
Usage:
|
|
2904
|
+
node index.mjs --<mode> [flags] [entry-point]
|
|
2905
|
+
|
|
2906
|
+
Modes:
|
|
2907
|
+
|
|
2908
|
+
--backtest <entry> Run strategy against historical candle data
|
|
2909
|
+
--walker <entry...> Run Walker A/B strategy comparison across multiple strategies
|
|
2910
|
+
--paper <entry> Paper trading (live prices, no real orders)
|
|
2911
|
+
--live <entry> Live trading with real orders
|
|
2912
|
+
--pine <entry> Execute a local .pine indicator file
|
|
2913
|
+
--dump Fetch and save raw OHLCV candles
|
|
2914
|
+
--init Scaffold a new project in the current directory
|
|
2915
|
+
--help Print this help message
|
|
2916
|
+
|
|
2917
|
+
Backtest flags:
|
|
2918
|
+
|
|
2919
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
2920
|
+
--strategy <string> Strategy name from addStrategySchema (default: first registered)
|
|
2921
|
+
--exchange <string> Exchange name from addExchangeSchema (default: first registered)
|
|
2922
|
+
--frame <string> Frame name from addFrameSchema (default: first registered)
|
|
2923
|
+
--cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
|
|
2924
|
+
--noCache Skip candle cache warming before the run
|
|
2925
|
+
--verbose Log every candle fetch to stdout
|
|
2926
|
+
--ui Start web dashboard at http://localhost:60050
|
|
2927
|
+
--telegram Send trade notifications to Telegram
|
|
2928
|
+
|
|
2929
|
+
Walker flags (--walker):
|
|
2930
|
+
|
|
2931
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
2932
|
+
--cacheInterval <string> Comma-separated intervals to pre-cache (default: "1m, 15m, 30m, 4h")
|
|
2933
|
+
--noCache Skip candle cache warming before the run
|
|
2934
|
+
--verbose Log every candle fetch to stdout
|
|
2935
|
+
--output <string> Output file base name (default: walker_{SYMBOL}_{TIMESTAMP})
|
|
2936
|
+
--json Save results as JSON to ./dump/<output>.json
|
|
2937
|
+
--markdown Save report as Markdown to ./dump/<output>.md
|
|
2938
|
+
|
|
2939
|
+
Each positional argument is a strategy entry point. All strategy files are loaded without
|
|
2940
|
+
changing process.cwd() — .env is read from the working directory only.
|
|
2941
|
+
addWalkerSchema is called automatically using the registered exchange and frame.
|
|
2942
|
+
After comparison completes the report is printed to stdout (or saved if --json/--markdown).
|
|
2943
|
+
|
|
2944
|
+
Module file ./modules/walker.module is loaded automatically if it exists.
|
|
2945
|
+
|
|
2946
|
+
Paper / Live flags:
|
|
2947
|
+
|
|
2948
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
2949
|
+
--strategy <string> Strategy name (default: first registered)
|
|
2950
|
+
--exchange <string> Exchange name (default: first registered)
|
|
2951
|
+
--verbose Log every candle fetch to stdout
|
|
2952
|
+
--ui Start web dashboard
|
|
2953
|
+
--telegram Send Telegram notifications
|
|
2954
|
+
|
|
2955
|
+
PineScript flags (--pine):
|
|
2956
|
+
|
|
2957
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
2958
|
+
--timeframe <string> Candle interval (default: 15m)
|
|
2959
|
+
--limit <string> Number of candles to fetch (default: 250)
|
|
2960
|
+
--when <string> End date — ISO 8601 or Unix ms (default: now)
|
|
2961
|
+
--exchange <string> Exchange name (default: first registered)
|
|
2962
|
+
--output <string> Output file base name without extension
|
|
2963
|
+
--json Save output as JSON array to <pine-dir>/dump/<output>.json
|
|
2964
|
+
--jsonl Save output as JSONL to <pine-dir>/dump/<output>.jsonl
|
|
2965
|
+
--markdown Save output as Markdown table to <pine-dir>/dump/<output>.md
|
|
2966
|
+
|
|
2967
|
+
Only plot() calls with display=display.data_window produce output columns.
|
|
2968
|
+
Module file ./modules/pine.module is loaded automatically if it exists.
|
|
2969
|
+
|
|
2970
|
+
Candle dump flags (--dump):
|
|
2971
|
+
|
|
2972
|
+
--symbol <string> Trading pair (default: BTCUSDT)
|
|
2973
|
+
--timeframe <string> Candle interval (default: 15m)
|
|
2974
|
+
--limit <string> Number of candles (default: 250)
|
|
2975
|
+
--when <string> End date — ISO 8601 or Unix ms (default: now)
|
|
2976
|
+
--exchange <string> Exchange name (default: first registered)
|
|
2977
|
+
--output <string> Output file base name (default: {SYMBOL}_{LIMIT}_{TIMEFRAME}_{TIMESTAMP})
|
|
2978
|
+
--json Save as JSON array to ./dump/<output>.json
|
|
2979
|
+
--jsonl Save as JSONL to ./dump/<output>.jsonl
|
|
2980
|
+
|
|
2981
|
+
Module file ./modules/dump.module is loaded automatically if it exists.
|
|
2982
|
+
|
|
2983
|
+
Init flags (--init):
|
|
2984
|
+
|
|
2985
|
+
--output <string> Target directory name (default: backtest-kit-project)
|
|
2986
|
+
|
|
2987
|
+
Scaffolds a project and runs scripts/fetch_docs.mjs to download library docs.
|
|
2988
|
+
|
|
2989
|
+
Module hooks (loaded automatically by each mode):
|
|
2990
|
+
|
|
2991
|
+
modules/backtest.module --backtest Broker adapter for backtest
|
|
2992
|
+
modules/walker.module --walker Broker adapter for walker comparison
|
|
2993
|
+
modules/paper.module --paper Broker adapter for paper trading
|
|
2994
|
+
modules/live.module --live Broker adapter for live trading
|
|
2995
|
+
modules/pine.module --pine Exchange schema for PineScript runs
|
|
2996
|
+
modules/dump.module --dump Exchange schema for candle dumps
|
|
2997
|
+
|
|
2998
|
+
Extensions .ts, .mjs, .cjs are tried automatically. Missing module = soft warning.
|
|
2999
|
+
|
|
3000
|
+
Environment variables:
|
|
3001
|
+
|
|
3002
|
+
CC_TELEGRAM_TOKEN Telegram bot token (required for --telegram)
|
|
3003
|
+
CC_TELEGRAM_CHANNEL Telegram channel or chat ID (required for --telegram)
|
|
3004
|
+
CC_WWWROOT_HOST UI server bind address (default: 0.0.0.0)
|
|
3005
|
+
CC_WWWROOT_PORT UI server port (default: 60050)
|
|
3006
|
+
|
|
3007
|
+
Examples:
|
|
3008
|
+
|
|
3009
|
+
node ${ENTRY_PATH} --backtest ./content/feb_2026.strategy.ts
|
|
3010
|
+
node ${ENTRY_PATH} --backtest --symbol BTCUSDT --noCache --ui ./content/feb_2026.strategy.ts
|
|
3011
|
+
node ${ENTRY_PATH} --walker ./content/feb_2026_v1.strategy.ts ./content/feb_2026_v2.strategy.ts ./content/feb_2026_v3.strategy.ts
|
|
3012
|
+
node ${ENTRY_PATH} --walker --symbol BTCUSDT --noCache --markdown ./content/feb_2026_v1.ts ./content/feb_2026_v2.ts
|
|
3013
|
+
node ${ENTRY_PATH} --paper --symbol ETHUSDT ./content/feb_2026.strategy.ts
|
|
3014
|
+
node ${ENTRY_PATH} --live --ui --telegram ./content/feb_2026.strategy.ts
|
|
3015
|
+
node ${ENTRY_PATH} --pine ./math/feb_2026.pine --timeframe 15m --limit 500 --jsonl
|
|
3016
|
+
node ${ENTRY_PATH} --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl
|
|
3017
|
+
node ${ENTRY_PATH} --init --output my-trading-bot
|
|
2728
3018
|
`.trimStart();
|
|
2729
3019
|
const main$1 = async () => {
|
|
2730
3020
|
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)))) {
|
|
@@ -2734,6 +3024,7 @@ const main$1 = async () => {
|
|
|
2734
3024
|
if (!values.help) {
|
|
2735
3025
|
return;
|
|
2736
3026
|
}
|
|
3027
|
+
process.stdout.write(`@backtest-kit/cli ${"6.2.1"}\n\n`);
|
|
2737
3028
|
process.stdout.write(HELP_TEXT);
|
|
2738
3029
|
process.exit(0);
|
|
2739
3030
|
};
|
|
@@ -2747,7 +3038,7 @@ const main = async () => {
|
|
|
2747
3038
|
if (!values.version) {
|
|
2748
3039
|
return;
|
|
2749
3040
|
}
|
|
2750
|
-
process.stdout.write(`@backtest-kit/cli ${"6.
|
|
3041
|
+
process.stdout.write(`@backtest-kit/cli ${"6.2.1"}\n`);
|
|
2751
3042
|
process.exit(0);
|
|
2752
3043
|
};
|
|
2753
3044
|
main();
|
|
@@ -2769,7 +3060,7 @@ async function run(mode, args) {
|
|
|
2769
3060
|
}
|
|
2770
3061
|
if (mode === "backtest") {
|
|
2771
3062
|
await cli.backtestMainService.run(args);
|
|
2772
|
-
listenGracefulShutdown$
|
|
3063
|
+
listenGracefulShutdown$5();
|
|
2773
3064
|
return;
|
|
2774
3065
|
}
|
|
2775
3066
|
if (mode === "paper") {
|