@backtest-kit/cli 7.1.0 → 7.3.0

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.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import * as BacktestKit from 'backtest-kit';
3
- import { Log, listExchangeSchema, addExchangeSchema, roundTicks, listFrameSchema, addFrameSchema, listenDoneLive, listenDoneBacktest, shutdown, listenSignal, listStrategySchema, overrideExchangeSchema, Backtest, Session, Cache, Interval, alignToInterval, addWalkerSchema, overrideWalkerSchema, Walker, listenDoneWalker, Live, getCandles, checkCandles, warmCandles, listenRisk, listenStrategyCommit, listenSync, Notification, Recent, Storage, Markdown, Report, Dump, Memory, StorageLive, StorageBacktest, RecentLive, RecentBacktest, NotificationLive, NotificationBacktest, MarkdownWriter, ReportWriter, PersistSignalAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistPartialAdapter, PersistBreakevenAdapter, PersistCandleAdapter, PersistStorageAdapter, PersistNotificationAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistIntervalAdapter, PersistMemoryAdapter, PersistRecentAdapter, setConfig, Exchange } from 'backtest-kit';
4
- import { getErrorMessage, errorData, singleshot, str, BehaviorSubject, compose, createAwaiter, execpool, queued, sleep, randomString, TIMEOUT_SYMBOL, typo, retry, trycatch, memoize } from 'functools-kit';
3
+ import { setConfig, Log, listExchangeSchema, addExchangeSchema, roundTicks, listFrameSchema, addFrameSchema, listenDoneLive, listenDoneBacktest, shutdown, listenSignal, Notification, Recent, Storage, Markdown, Report, Dump, Memory, StorageLive, StorageBacktest, RecentLive, RecentBacktest, NotificationLive, NotificationBacktest, MarkdownWriter, ReportWriter, PersistSignalAdapter, PersistRiskAdapter, PersistScheduleAdapter, PersistPartialAdapter, PersistBreakevenAdapter, PersistCandleAdapter, PersistStorageAdapter, PersistNotificationAdapter, PersistLogAdapter, PersistMeasureAdapter, PersistIntervalAdapter, PersistMemoryAdapter, PersistRecentAdapter, listStrategySchema, overrideExchangeSchema, Backtest, Session, 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, BehaviorSubject, compose, createAwaiter, execpool, queued, sleep, randomString, TIMEOUT_SYMBOL, typo, retry, trycatch, memoize, isObject } from 'functools-kit';
5
5
  import fs, { constants } from 'fs';
6
6
  import * as stackTrace from 'stack-trace';
7
7
  import path, { join, resolve, dirname, basename, extname } from 'path';
@@ -12,7 +12,7 @@ import { fileURLToPath } from 'url';
12
12
  import ccxt from 'ccxt';
13
13
  import { parseArgs } from 'util';
14
14
  import * as BacktestKitUi from '@backtest-kit/ui';
15
- import { serve } from '@backtest-kit/ui';
15
+ import { lib, serve } from '@backtest-kit/ui';
16
16
  import QuickChart from 'quickchart-js';
17
17
  import { Telegraf, Input } from 'telegraf';
18
18
  import imageSize from 'image-size';
@@ -63,6 +63,38 @@ import { spawn } from 'child_process';
63
63
  });
64
64
  }
65
65
 
66
+ setConfig({
67
+ CC_MAX_NOTIFICATIONS: 5000,
68
+ CC_MAX_SIGNALS: 750,
69
+ });
70
+ setConfig({
71
+ CC_ENABLE_DCA_EVERYWHERE: true,
72
+ CC_ENABLE_PPPL_EVERYWHERE: true,
73
+ CC_ENABLE_TRAILING_EVERYWHERE: true,
74
+ });
75
+ setConfig({
76
+ CC_MAX_SIGNAL_GENERATION_SECONDS: 15 * 60,
77
+ });
78
+ setConfig({
79
+ CC_MAX_BACKTEST_MARKDOWN_ROWS: 1000,
80
+ CC_MAX_BREAKEVEN_MARKDOWN_ROWS: 1000,
81
+ CC_MAX_HEATMAP_MARKDOWN_ROWS: 1000,
82
+ CC_MAX_HIGHEST_PROFIT_MARKDOWN_ROWS: 1000,
83
+ CC_MAX_LIVE_MARKDOWN_ROWS: 1000,
84
+ CC_MAX_PARTIAL_MARKDOWN_ROWS: 1000,
85
+ CC_MAX_RISK_MARKDOWN_ROWS: 1000,
86
+ CC_MAX_SCHEDULE_MARKDOWN_ROWS: 1000,
87
+ CC_MAX_STRATEGY_MARKDOWN_ROWS: 1000,
88
+ CC_MAX_SYNC_MARKDOWN_ROWS: 1000,
89
+ CC_MAX_PERFORMANCE_MARKDOWN_ROWS: 1000,
90
+ });
91
+ setConfig({
92
+ CC_MAX_SIGNAL_LIFETIME_MINUTES: Infinity,
93
+ });
94
+ setConfig({
95
+ CC_WALKER_MARKDOWN_TOP_N: 10,
96
+ });
97
+
66
98
  const ERROR_HANDLER_INSTALLED = Symbol.for("error-handler-installed");
67
99
  function dumpStackTrace() {
68
100
  const trace = stackTrace.get();
@@ -158,12 +190,16 @@ const apiServices$1 = {
158
190
  const baseServices$1 = {
159
191
  errorService: Symbol('errorService'),
160
192
  loggerService: Symbol('loggerService'),
193
+ };
194
+ const coreServices$1 = {
161
195
  resolveService: Symbol('resolveService'),
162
196
  loaderService: Symbol('loaderService'),
197
+ configService: Symbol('configService'),
163
198
  babelService: Symbol('babelService'),
164
199
  };
165
200
  const connectionServices$1 = {
166
201
  moduleConnectionService: Symbol('moduleConnectionService'),
202
+ configConnectionService: Symbol('configConnectionService'),
167
203
  };
168
204
  const mainServices$1 = {
169
205
  backtestMainService: Symbol('backtestMainService'),
@@ -193,6 +229,7 @@ const templateServices$1 = {
193
229
  const TYPES = {
194
230
  ...apiServices$1,
195
231
  ...baseServices$1,
232
+ ...coreServices$1,
196
233
  ...connectionServices$1,
197
234
  ...mainServices$1,
198
235
  ...logicServices$1,
@@ -213,8 +250,10 @@ class ResolveService {
213
250
  this.loaderService = inject(TYPES.loaderService);
214
251
  this.DEFAULT_TEMPLATE_DIR = path.resolve(__dirname$1, '..', 'template');
215
252
  this.DEFAULT_MODULES_DIR = path.resolve(__dirname$1, '..', 'modules');
253
+ this.DEFAULT_CONFIG_DIR = path.resolve(__dirname$1, '..', 'config');
216
254
  this.OVERRIDE_TEMPLATE_DIR = path.resolve(process.cwd(), 'template');
217
255
  this.OVERRIDE_MODULES_DIR = path.resolve(process.cwd(), 'modules');
256
+ this.OVERRIDE_CONFIG_DIR = path.resolve(process.cwd(), 'config');
218
257
  this.PROJECT_ROOT_DIR = process.cwd();
219
258
  this.getIsLaunched = () => {
220
259
  this.loggerService.log("resolveService getIsLaunched");
@@ -391,6 +430,10 @@ const ALLOWED_EXTENSIONS = [
391
430
  const DISALLOWED_PATHS = [
392
431
  "node_modules",
393
432
  "@backtest-kit",
433
+ "build/index.mjs",
434
+ "build/index.js",
435
+ "build\\index.mjs",
436
+ "build\\index.js",
394
437
  ];
395
438
  const getArgs = singleshot(() => {
396
439
  const { values, positionals } = parseArgs({
@@ -632,6 +675,105 @@ const notifyVerbose = singleshot(() => {
632
675
  });
633
676
  });
634
677
 
678
+ class SetupUtils {
679
+ constructor() {
680
+ this.enable = singleshot(() => {
681
+ cli.loggerService.debug("SetupUtils enable");
682
+ {
683
+ const config = cli.configService.getNotificationConfig();
684
+ Notification.enable(config);
685
+ }
686
+ {
687
+ Recent.enable();
688
+ Storage.enable();
689
+ }
690
+ {
691
+ Markdown.enable();
692
+ Report.enable();
693
+ Dump.enable();
694
+ Memory.enable();
695
+ }
696
+ {
697
+ Dump.useMarkdown();
698
+ Memory.usePersist();
699
+ }
700
+ {
701
+ StorageLive.usePersist();
702
+ StorageBacktest.useMemory();
703
+ }
704
+ {
705
+ RecentLive.usePersist();
706
+ RecentBacktest.useMemory();
707
+ }
708
+ {
709
+ NotificationLive.usePersist();
710
+ NotificationBacktest.useMemory();
711
+ }
712
+ {
713
+ Markdown.useDummy();
714
+ Log.useJsonl();
715
+ }
716
+ });
717
+ this.clear = () => {
718
+ cli.loggerService.debug("SetupUtils clear");
719
+ if (!this.enable.hasValue()) {
720
+ return;
721
+ }
722
+ this.enable.clear();
723
+ {
724
+ Recent.disable();
725
+ Storage.disable();
726
+ Notification.disable();
727
+ }
728
+ {
729
+ Markdown.disable();
730
+ Report.disable();
731
+ Dump.disable();
732
+ Memory.disable();
733
+ }
734
+ {
735
+ Markdown.clear();
736
+ Report.clear();
737
+ MarkdownWriter.clear();
738
+ ReportWriter.clear();
739
+ }
740
+ {
741
+ PersistSignalAdapter.clear();
742
+ PersistRiskAdapter.clear();
743
+ PersistScheduleAdapter.clear();
744
+ PersistPartialAdapter.clear();
745
+ PersistBreakevenAdapter.clear();
746
+ PersistCandleAdapter.clear();
747
+ PersistStorageAdapter.clear();
748
+ PersistNotificationAdapter.clear();
749
+ PersistLogAdapter.clear();
750
+ PersistMeasureAdapter.clear();
751
+ PersistIntervalAdapter.clear();
752
+ PersistMemoryAdapter.clear();
753
+ PersistRecentAdapter.clear();
754
+ }
755
+ {
756
+ Dump.clear();
757
+ Log.clear();
758
+ Markdown.clear();
759
+ }
760
+ {
761
+ StorageLive.clear();
762
+ StorageBacktest.clear();
763
+ }
764
+ {
765
+ NotificationLive.clear();
766
+ NotificationBacktest.clear();
767
+ }
768
+ {
769
+ RecentLive.clear();
770
+ RecentBacktest.clear();
771
+ }
772
+ };
773
+ }
774
+ }
775
+ const Setup = new SetupUtils();
776
+
635
777
  const DEFAULT_CACHE_LIST$1 = ["1m", "15m", "30m", "1h", "4h"];
636
778
  const GET_CACHE_INTERVAL_LIST_FN$1 = () => {
637
779
  const { values } = getArgs();
@@ -646,6 +788,7 @@ class BacktestMainService {
646
788
  constructor() {
647
789
  this.loggerService = inject(TYPES.loggerService);
648
790
  this.resolveService = inject(TYPES.resolveService);
791
+ this.configService = inject(TYPES.configService);
649
792
  this.exchangeSchemaService = inject(TYPES.exchangeSchemaService);
650
793
  this.frameSchemaService = inject(TYPES.frameSchemaService);
651
794
  this.symbolSchemaService = inject(TYPES.symbolSchemaService);
@@ -657,6 +800,10 @@ class BacktestMainService {
657
800
  this.loggerService.log("backtestMainService run", {
658
801
  payload,
659
802
  });
803
+ {
804
+ await this.configService.waitForInit();
805
+ Setup.enable();
806
+ }
660
807
  {
661
808
  this.frontendProviderService.connect();
662
809
  this.telegramProviderService.connect();
@@ -759,12 +906,17 @@ class WalkerMainService {
759
906
  constructor() {
760
907
  this.loggerService = inject(TYPES.loggerService);
761
908
  this.resolveService = inject(TYPES.resolveService);
909
+ this.configService = inject(TYPES.configService);
762
910
  this.exchangeSchemaService = inject(TYPES.exchangeSchemaService);
763
911
  this.symbolSchemaService = inject(TYPES.symbolSchemaService);
764
912
  this.cacheLogicService = inject(TYPES.cacheLogicService);
765
913
  this.moduleConnectionService = inject(TYPES.moduleConnectionService);
766
914
  this.run = singleshot(async (payload) => {
767
915
  this.loggerService.log("walkerMainService run", { payload });
916
+ {
917
+ await this.configService.waitForInit();
918
+ Setup.enable();
919
+ }
768
920
  const strategyMap = new Map();
769
921
  const sessionMap = new Map();
770
922
  const cwd = process.cwd();
@@ -970,6 +1122,7 @@ class LiveMainService {
970
1122
  constructor() {
971
1123
  this.loggerService = inject(TYPES.loggerService);
972
1124
  this.resolveService = inject(TYPES.resolveService);
1125
+ this.configService = inject(TYPES.configService);
973
1126
  this.exchangeSchemaService = inject(TYPES.exchangeSchemaService);
974
1127
  this.symbolSchemaService = inject(TYPES.symbolSchemaService);
975
1128
  this.frontendProviderService = inject(TYPES.frontendProviderService);
@@ -979,6 +1132,10 @@ class LiveMainService {
979
1132
  this.loggerService.log("liveMainService run", {
980
1133
  payload,
981
1134
  });
1135
+ {
1136
+ await this.configService.waitForInit();
1137
+ Setup.enable();
1138
+ }
982
1139
  {
983
1140
  this.frontendProviderService.connect();
984
1141
  this.telegramProviderService.connect();
@@ -1047,6 +1204,7 @@ class PaperMainService {
1047
1204
  constructor() {
1048
1205
  this.loggerService = inject(TYPES.loggerService);
1049
1206
  this.resolveService = inject(TYPES.resolveService);
1207
+ this.configService = inject(TYPES.configService);
1050
1208
  this.exchangeSchemaService = inject(TYPES.exchangeSchemaService);
1051
1209
  this.symbolSchemaService = inject(TYPES.symbolSchemaService);
1052
1210
  this.frontendProviderService = inject(TYPES.frontendProviderService);
@@ -1054,6 +1212,10 @@ class PaperMainService {
1054
1212
  this.moduleConnectionService = inject(TYPES.moduleConnectionService);
1055
1213
  this.run = singleshot(async (payload) => {
1056
1214
  this.loggerService.log("paperMainService init");
1215
+ {
1216
+ await this.configService.waitForInit();
1217
+ Setup.enable();
1218
+ }
1057
1219
  {
1058
1220
  this.frontendProviderService.connect();
1059
1221
  this.telegramProviderService.connect();
@@ -1131,16 +1293,72 @@ const getEnv = singleshot(() => {
1131
1293
  };
1132
1294
  });
1133
1295
 
1296
+ const GET_SYMBOL_EXPORTS_FN = async (self) => {
1297
+ const exports = await self.configConnectionService.loadConfig("symbol.config");
1298
+ if (!exports) {
1299
+ return null;
1300
+ }
1301
+ return "default" in exports
1302
+ ? exports.default
1303
+ : exports;
1304
+ };
1305
+ const GET_SYMBOL_CONFIG_FN = async (self) => {
1306
+ const config = await GET_SYMBOL_EXPORTS_FN(self);
1307
+ if (!config) {
1308
+ throw new Error("FrontendProviderService getSymbolConfig `symbol.config` is not found");
1309
+ }
1310
+ if (Array.isArray(config)) {
1311
+ return config;
1312
+ }
1313
+ if ("symbol_list" in config) {
1314
+ return config.symbol_list;
1315
+ }
1316
+ throw new Error("FrontendProviderService getSymbolConfig `symbol.config` is not found");
1317
+ };
1318
+ const MAP_SYMBOL_CONFIG_FN = (config) => {
1319
+ const uniqueSymbols = new Set();
1320
+ const symbolList = config
1321
+ .filter((item) => {
1322
+ if (uniqueSymbols.has(item.symbol)) {
1323
+ return false;
1324
+ }
1325
+ uniqueSymbols.add(item.symbol);
1326
+ return true;
1327
+ })
1328
+ .map(({ priority, displayName, symbol, logo, icon, ...other }, idx) => ({
1329
+ symbol,
1330
+ icon,
1331
+ logo: logo ?? icon,
1332
+ priority: priority ?? idx,
1333
+ displayName: displayName ?? symbol,
1334
+ index: idx,
1335
+ ...other,
1336
+ }));
1337
+ symbolList.sort(({ priority: a_p, index: a_x }, { priority: b_p, index: b_x }) => b_p - a_p || a_x - b_x);
1338
+ return symbolList;
1339
+ };
1134
1340
  class FrontendProviderService {
1135
1341
  constructor() {
1136
1342
  this.loggerService = inject(TYPES.loggerService);
1137
1343
  this.resolveService = inject(TYPES.resolveService);
1344
+ this.configConnectionService = inject(TYPES.configConnectionService);
1138
1345
  this.enable = singleshot(() => {
1139
1346
  this.loggerService.log("frontendProviderService enable");
1140
1347
  const { CC_WWWROOT_HOST, CC_WWWROOT_PORT } = getEnv();
1141
- const unServer = serve(CC_WWWROOT_HOST, CC_WWWROOT_PORT, this.resolveService.PROJECT_ROOT_DIR);
1348
+ let unServer;
1349
+ const init = async () => {
1350
+ {
1351
+ const config = await GET_SYMBOL_CONFIG_FN(this);
1352
+ if (config) {
1353
+ const symbolList = MAP_SYMBOL_CONFIG_FN(config);
1354
+ lib.symbolConnectionService.getSymbolList.setValue(Promise.resolve(symbolList));
1355
+ }
1356
+ }
1357
+ unServer = serve(CC_WWWROOT_HOST, CC_WWWROOT_PORT, this.resolveService.PROJECT_ROOT_DIR);
1358
+ };
1359
+ init();
1142
1360
  return () => {
1143
- unServer();
1361
+ unServer && unServer();
1144
1362
  this.enable.clear();
1145
1363
  };
1146
1364
  });
@@ -1811,6 +2029,16 @@ class TelegramLogicService {
1811
2029
  markdown,
1812
2030
  });
1813
2031
  });
2032
+ this.notifySignalInfo = trycatch(async (event) => {
2033
+ this.loggerService.log("telegramLogicService notifySignalInfo", {
2034
+ event,
2035
+ });
2036
+ const markdown = await this.telegramTemplateService.getSignalInfoMarkdown(event);
2037
+ await this.telegramWebService.publishNotify({
2038
+ symbol: event.symbol,
2039
+ markdown,
2040
+ });
2041
+ });
1814
2042
  this.notifyCancelScheduled = async (event) => {
1815
2043
  this.loggerService.log("telegramLogicService notifyCancelScheduled", {
1816
2044
  event,
@@ -1898,8 +2126,11 @@ class TelegramLogicService {
1898
2126
  return;
1899
2127
  }
1900
2128
  });
2129
+ const unSignalNotify = listenSignalNotify(async (event) => {
2130
+ await this.notifySignalInfo(event);
2131
+ });
1901
2132
  const unConnect = () => this.connect.clear();
1902
- const unListen = compose(() => unRisk(), () => unSignal(), () => unCommit(), () => unSync(), () => unConnect());
2133
+ const unListen = compose(() => unRisk(), () => unSignal(), () => unCommit(), () => unSync(), () => unSignalNotify(), () => unConnect());
1903
2134
  return () => {
1904
2135
  STOP_BOT_FN();
1905
2136
  unListen();
@@ -2018,6 +2249,12 @@ class TelegramTemplateService {
2018
2249
  });
2019
2250
  return await RENDER_TEMPLATE_FN("close-pending.mustache", event, this);
2020
2251
  };
2252
+ this.getSignalInfoMarkdown = async (event) => {
2253
+ this.loggerService.log("telegramTemplateService getSignalInfoMarkdown", {
2254
+ event,
2255
+ });
2256
+ return await RENDER_TEMPLATE_FN("signal-info.mustache", event, this);
2257
+ };
2021
2258
  }
2022
2259
  }
2023
2260
 
@@ -2058,7 +2295,7 @@ class ModuleConnectionService {
2058
2295
  this.resolveService = inject(TYPES.resolveService);
2059
2296
  this.loaderService = inject(TYPES.loaderService);
2060
2297
  this.loadModule = async (fileName) => {
2061
- this.loggerService.log("moduleConnectionService getInstance", {
2298
+ this.loggerService.log("moduleConnectionService loadModule", {
2062
2299
  fileName,
2063
2300
  });
2064
2301
  return await LOAD_MODULE_MODULE_FN(fileName, this);
@@ -2105,6 +2342,19 @@ class BabelService {
2105
2342
  }
2106
2343
  }
2107
2344
 
2345
+ /**
2346
+ * This file is used to define any aliases for imports in the client code. This is useful for mocking modules during testing or for providing alternative implementations of certain modules.
2347
+ * For example, if we want to mock the "pinets" module during testing, we can add an entry to the IMPORT_ALIAS object that points to our mock implementation. Then, when the client code tries to import "pinets", it will receive our mock implementation instead of the actual "pinets" module.
2348
+ * This allows us to isolate the client code from external dependencies and test it more effectively.
2349
+ * Note that the keys in the IMPORT_ALIAS object should match the module names used in the client code, and the values should be the corresponding mock implementations or alternative modules.
2350
+ * @example
2351
+ * // To mock the "pinets" module, we can add the following entry to the IMPORT_ALIAS object:
2352
+ * Object.assign(IMPORT_ALIAS, {
2353
+ * "pinets": require("pinets/dist/pinets.min.browser.js"),
2354
+ * });
2355
+ */
2356
+ const IMPORT_ALIAS = {};
2357
+
2108
2358
  const USE_ESMODULE_DEFAULT = false;
2109
2359
  const IMPORT_PATHS_EXCLUDE = new Set(["dump", "logs", "modules", "node_modules"]);
2110
2360
  const TRANSPILE_FN = memoize(([path]) => `${path}`, (path, code, self, require) => {
@@ -2220,6 +2470,8 @@ const CREATE_BASE_REQUIRE_FN = (self, seen) => {
2220
2470
  return new Proxy(baseRequire, {
2221
2471
  apply(_target, _this, args) {
2222
2472
  const id = args[0];
2473
+ if (IMPORT_ALIAS[id])
2474
+ return IMPORT_ALIAS[id];
2223
2475
  if (id === "backtest-kit")
2224
2476
  return globalThis.BacktestKit;
2225
2477
  if (id === "@backtest-kit/cli")
@@ -2323,6 +2575,26 @@ globalThis.BacktestKitOllama = BacktestKitOllama;
2323
2575
  globalThis.BacktestKitPinets = BacktestKitPinets;
2324
2576
  globalThis.BacktestKitSignals = BacktestKitSignals;
2325
2577
 
2578
+ const GET_ALIAS_EXPORTS_FN = (self) => {
2579
+ const instance = self.getInstance(self.resolveService.OVERRIDE_CONFIG_DIR);
2580
+ if (!instance.check("alias.module")) {
2581
+ return null;
2582
+ }
2583
+ const exports = instance.import("alias.module");
2584
+ return "default" in exports
2585
+ ? exports.default
2586
+ : exports;
2587
+ };
2588
+ const INIT_ALIAS_FN = (self) => {
2589
+ const alias = GET_ALIAS_EXPORTS_FN(self);
2590
+ if (!alias) {
2591
+ return;
2592
+ }
2593
+ if (!isObject(alias)) {
2594
+ return;
2595
+ }
2596
+ Object.assign(IMPORT_ALIAS, alias);
2597
+ };
2326
2598
  class LoaderService {
2327
2599
  constructor() {
2328
2600
  this.babelService = inject(TYPES.babelService);
@@ -2350,6 +2622,90 @@ class LoaderService {
2350
2622
  const instance = this.getInstance(basePath);
2351
2623
  return instance.check(filePath);
2352
2624
  };
2625
+ this.init = singleshot(() => {
2626
+ this.loggerService.log("loaderService init");
2627
+ INIT_ALIAS_FN(this);
2628
+ });
2629
+ }
2630
+ }
2631
+
2632
+ const GET_CONFIG_VARIANTS_FN = (fileName, self) => {
2633
+ const result = [];
2634
+ result.push({
2635
+ filePath: path.join(process.cwd(), "config", fileName),
2636
+ baseDir: path.join(process.cwd(), "config")
2637
+ });
2638
+ result.push({
2639
+ filePath: path.join(self.resolveService.OVERRIDE_CONFIG_DIR, fileName),
2640
+ baseDir: self.resolveService.OVERRIDE_CONFIG_DIR,
2641
+ });
2642
+ result.push({
2643
+ filePath: path.join(self.resolveService.DEFAULT_CONFIG_DIR, fileName),
2644
+ baseDir: self.resolveService.DEFAULT_CONFIG_DIR,
2645
+ });
2646
+ return result;
2647
+ };
2648
+ const LOAD_CONFIG_CONFIG_FN = async (fileName, self) => {
2649
+ for (const { filePath, baseDir } of GET_CONFIG_VARIANTS_FN(fileName, self)) {
2650
+ try {
2651
+ if (await self.loaderService.check(filePath, baseDir)) {
2652
+ return self.loaderService.import(filePath, baseDir);
2653
+ }
2654
+ }
2655
+ catch {
2656
+ console.warn(`Module module import failed filePath=${filePath} baseDir=${baseDir}`);
2657
+ process.exit(-1);
2658
+ }
2659
+ }
2660
+ return null;
2661
+ };
2662
+ class ConfigConnectionService {
2663
+ constructor() {
2664
+ this.loggerService = inject(TYPES.loggerService);
2665
+ this.resolveService = inject(TYPES.resolveService);
2666
+ this.loaderService = inject(TYPES.loaderService);
2667
+ this.loadConfig = (fileName) => {
2668
+ this.loggerService.log("configConnectionService loadConfig", {
2669
+ fileName,
2670
+ });
2671
+ return LOAD_CONFIG_CONFIG_FN(fileName, this);
2672
+ };
2673
+ }
2674
+ }
2675
+
2676
+ const GET_NOTIFICATION_EXPORTS_FN = async (self) => {
2677
+ const exports = await self.configConnectionService.loadConfig("notification.config");
2678
+ if (!exports) {
2679
+ return null;
2680
+ }
2681
+ return "default" in exports
2682
+ ? exports.default
2683
+ : exports;
2684
+ };
2685
+ const GET_NOTIFICATION_CONFIG_FN = async (self) => {
2686
+ const config = await GET_NOTIFICATION_EXPORTS_FN(self);
2687
+ if (!config) {
2688
+ throw new Error("ConfigService getNotificationConfig `notification.config` is not found");
2689
+ }
2690
+ if ("notification_config" in config) {
2691
+ return config.notification_config;
2692
+ }
2693
+ return config;
2694
+ };
2695
+ class ConfigService {
2696
+ constructor() {
2697
+ this.loggerService = inject(TYPES.loggerService);
2698
+ this.configConnectionService = inject(TYPES.configConnectionService);
2699
+ this.getNotificationConfig = singleshot(() => {
2700
+ throw new Error("ConfigService getNotificationConfig waitForInit is not called");
2701
+ });
2702
+ this.waitForInit = singleshot(async () => {
2703
+ this.loggerService.log("configService waitForInit");
2704
+ {
2705
+ const config = await GET_NOTIFICATION_CONFIG_FN(this);
2706
+ this.getNotificationConfig.setValue(config);
2707
+ }
2708
+ });
2353
2709
  }
2354
2710
  }
2355
2711
 
@@ -2360,12 +2716,16 @@ class LoaderService {
2360
2716
  {
2361
2717
  provide(TYPES.errorService, () => new ErrorService());
2362
2718
  provide(TYPES.loggerService, () => new LoggerService());
2719
+ }
2720
+ {
2363
2721
  provide(TYPES.resolveService, () => new ResolveService());
2364
2722
  provide(TYPES.loaderService, () => new LoaderService());
2723
+ provide(TYPES.configService, () => new ConfigService());
2365
2724
  provide(TYPES.babelService, () => new BabelService());
2366
2725
  }
2367
2726
  {
2368
2727
  provide(TYPES.moduleConnectionService, () => new ModuleConnectionService());
2728
+ provide(TYPES.configConnectionService, () => new ConfigConnectionService());
2369
2729
  }
2370
2730
  {
2371
2731
  provide(TYPES.backtestMainService, () => new BacktestMainService());
@@ -2400,12 +2760,16 @@ const apiServices = {
2400
2760
  const baseServices = {
2401
2761
  errorService: inject(TYPES.errorService),
2402
2762
  loggerService: inject(TYPES.loggerService),
2763
+ };
2764
+ const coreServices = {
2403
2765
  resolveService: inject(TYPES.resolveService),
2404
2766
  loaderService: inject(TYPES.loaderService),
2767
+ configService: inject(TYPES.configService),
2405
2768
  babelService: inject(TYPES.babelService),
2406
2769
  };
2407
2770
  const connectionServices = {
2408
2771
  moduleConnectionService: inject(TYPES.moduleConnectionService),
2772
+ configConnectionService: inject(TYPES.configConnectionService),
2409
2773
  };
2410
2774
  const mainServices = {
2411
2775
  backtestMainService: inject(TYPES.backtestMainService),
@@ -2435,6 +2799,7 @@ const templateServices = {
2435
2799
  const cli = {
2436
2800
  ...apiServices,
2437
2801
  ...baseServices,
2802
+ ...coreServices,
2438
2803
  ...connectionServices,
2439
2804
  ...mainServices,
2440
2805
  ...logicServices,
@@ -2445,148 +2810,6 @@ const cli = {
2445
2810
  };
2446
2811
  init();
2447
2812
 
2448
- const NOTIFICATION_CONFIG = {
2449
- signal: true,
2450
- risk: true,
2451
- info: true,
2452
- breakeven: true,
2453
- common_error: true,
2454
- critical_error: true,
2455
- validation_error: true,
2456
- partial_loss: false,
2457
- partial_profit: false,
2458
- signal_sync: false,
2459
- strategy_commit: true,
2460
- };
2461
- class SetupUtils {
2462
- constructor() {
2463
- this.enable = singleshot(() => {
2464
- cli.loggerService.debug("SetupUtils enable");
2465
- Notification.enable(NOTIFICATION_CONFIG);
2466
- {
2467
- Recent.enable();
2468
- Storage.enable();
2469
- }
2470
- {
2471
- Markdown.enable();
2472
- Report.enable();
2473
- Dump.enable();
2474
- Memory.enable();
2475
- }
2476
- {
2477
- Dump.useMarkdown();
2478
- Memory.usePersist();
2479
- }
2480
- {
2481
- StorageLive.usePersist();
2482
- StorageBacktest.useMemory();
2483
- }
2484
- {
2485
- RecentLive.usePersist();
2486
- RecentBacktest.useMemory();
2487
- }
2488
- {
2489
- NotificationLive.usePersist();
2490
- NotificationBacktest.useMemory();
2491
- }
2492
- {
2493
- Markdown.useDummy();
2494
- Log.useJsonl();
2495
- }
2496
- });
2497
- this.clear = () => {
2498
- cli.loggerService.debug("SetupUtils clear");
2499
- if (!this.enable.hasValue()) {
2500
- return;
2501
- }
2502
- this.enable.clear();
2503
- {
2504
- Recent.disable();
2505
- Storage.disable();
2506
- Notification.disable();
2507
- }
2508
- {
2509
- Markdown.disable();
2510
- Report.disable();
2511
- Dump.disable();
2512
- Memory.disable();
2513
- }
2514
- {
2515
- Markdown.clear();
2516
- Report.clear();
2517
- MarkdownWriter.clear();
2518
- ReportWriter.clear();
2519
- }
2520
- {
2521
- PersistSignalAdapter.clear();
2522
- PersistRiskAdapter.clear();
2523
- PersistScheduleAdapter.clear();
2524
- PersistPartialAdapter.clear();
2525
- PersistBreakevenAdapter.clear();
2526
- PersistCandleAdapter.clear();
2527
- PersistStorageAdapter.clear();
2528
- PersistNotificationAdapter.clear();
2529
- PersistLogAdapter.clear();
2530
- PersistMeasureAdapter.clear();
2531
- PersistIntervalAdapter.clear();
2532
- PersistMemoryAdapter.clear();
2533
- PersistRecentAdapter.clear();
2534
- }
2535
- {
2536
- Dump.clear();
2537
- Log.clear();
2538
- Markdown.clear();
2539
- }
2540
- {
2541
- StorageLive.clear();
2542
- StorageBacktest.clear();
2543
- }
2544
- {
2545
- NotificationLive.clear();
2546
- NotificationBacktest.clear();
2547
- }
2548
- {
2549
- RecentLive.clear();
2550
- RecentBacktest.clear();
2551
- }
2552
- };
2553
- }
2554
- }
2555
- const Setup = new SetupUtils();
2556
-
2557
- Setup.enable();
2558
- setConfig({
2559
- CC_MAX_NOTIFICATIONS: 5000,
2560
- CC_MAX_SIGNALS: 750,
2561
- });
2562
- setConfig({
2563
- CC_ENABLE_DCA_EVERYWHERE: true,
2564
- CC_ENABLE_PPPL_EVERYWHERE: true,
2565
- CC_ENABLE_TRAILING_EVERYWHERE: true,
2566
- });
2567
- setConfig({
2568
- CC_MAX_SIGNAL_GENERATION_SECONDS: 15 * 60,
2569
- });
2570
- setConfig({
2571
- CC_MAX_BACKTEST_MARKDOWN_ROWS: 1000,
2572
- CC_MAX_BREAKEVEN_MARKDOWN_ROWS: 1000,
2573
- CC_MAX_HEATMAP_MARKDOWN_ROWS: 1000,
2574
- CC_MAX_HIGHEST_PROFIT_MARKDOWN_ROWS: 1000,
2575
- CC_MAX_LIVE_MARKDOWN_ROWS: 1000,
2576
- CC_MAX_PARTIAL_MARKDOWN_ROWS: 1000,
2577
- CC_MAX_RISK_MARKDOWN_ROWS: 1000,
2578
- CC_MAX_SCHEDULE_MARKDOWN_ROWS: 1000,
2579
- CC_MAX_STRATEGY_MARKDOWN_ROWS: 1000,
2580
- CC_MAX_SYNC_MARKDOWN_ROWS: 1000,
2581
- CC_MAX_PERFORMANCE_MARKDOWN_ROWS: 1000,
2582
- });
2583
- setConfig({
2584
- CC_MAX_SIGNAL_LIFETIME_MINUTES: Infinity,
2585
- });
2586
- setConfig({
2587
- CC_WALKER_MARKDOWN_TOP_N: 10,
2588
- });
2589
-
2590
2813
  const MODES = ["backtest", "walker", "paper", "live", "pine", "editor", "dump", "flush", "init", "help", "version"];
2591
2814
  const ENTRY_PATH$1 = "./node_modules/@backtest-kit/cli/build/index.mjs";
2592
2815
  const HELP_TEXT$1 = `
@@ -2602,7 +2825,7 @@ const main$d = async () => {
2602
2825
  if (MODES.some((mode) => values[mode])) {
2603
2826
  return;
2604
2827
  }
2605
- process.stdout.write(`@backtest-kit/cli ${"7.1.0"}\n`);
2828
+ process.stdout.write(`@backtest-kit/cli ${"7.3.0"}\n`);
2606
2829
  process.stdout.write("\n");
2607
2830
  process.stdout.write(`Run with --help to see available commands.\n`);
2608
2831
  process.stdout.write("\n");
@@ -2933,6 +3156,10 @@ const main$4 = async () => {
2933
3156
  console.warn("--editor and --pine are mutually exclusive. Use one at a time.");
2934
3157
  process.exit(1);
2935
3158
  }
3159
+ {
3160
+ await cli.configService.waitForInit();
3161
+ Setup.enable();
3162
+ }
2936
3163
  await cli.moduleConnectionService.loadModule("./editor.module");
2937
3164
  {
2938
3165
  await cli.exchangeSchemaService.addSchema();
@@ -3262,7 +3489,7 @@ const main$1 = async () => {
3262
3489
  if (!values.help) {
3263
3490
  return;
3264
3491
  }
3265
- process.stdout.write(`@backtest-kit/cli ${"7.1.0"}\n\n`);
3492
+ process.stdout.write(`@backtest-kit/cli ${"7.3.0"}\n\n`);
3266
3493
  process.stdout.write(HELP_TEXT);
3267
3494
  process.exit(0);
3268
3495
  };
@@ -3276,7 +3503,7 @@ const main = async () => {
3276
3503
  if (!values.version) {
3277
3504
  return;
3278
3505
  }
3279
- process.stdout.write(`@backtest-kit/cli ${"7.1.0"}\n`);
3506
+ process.stdout.write(`@backtest-kit/cli ${"7.3.0"}\n`);
3280
3507
  process.exit(0);
3281
3508
  };
3282
3509
  main();