@backtest-kit/cli 7.3.1 → 7.4.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/README.md CHANGED
@@ -509,6 +509,54 @@ Sends formatted HTML messages with 1m / 15m / 1h price charts to your Telegram c
509
509
 
510
510
  Requires `CC_TELEGRAM_TOKEN` and `CC_TELEGRAM_CHANNEL` in your environment.
511
511
 
512
+ #### Telegram Message Adapter (`telegram.config`)
513
+
514
+ By default messages are rendered from Mustache templates (`template/*.mustache`). To override rendering programmatically, create a `config/telegram.config` file and export an object with any subset of `get*Markdown` methods. Each method receives the event payload and must return a `Promise<string>` with the Markdown message body.
515
+
516
+ Resolution order is the same as other configs (strategy dir → project root → package default).
517
+
518
+ ```ts
519
+ // config/telegram.config.ts
520
+ import {
521
+ IStrategyTickResultOpened,
522
+ IStrategyTickResultClosed,
523
+ RiskContract,
524
+ } from "backtest-kit";
525
+
526
+ export default {
527
+ async getOpenedMarkdown(event: IStrategyTickResultOpened): Promise<string> {
528
+ return `**Opened** ${event.symbol} at ${event.priceOpen}`;
529
+ },
530
+ async getClosedMarkdown(event: IStrategyTickResultClosed): Promise<string> {
531
+ return `**Closed** ${event.symbol} at ${event.priceClosed}`;
532
+ },
533
+ async getRiskMarkdown(event: RiskContract): Promise<string> {
534
+ return `**Risk rejected** ${event.symbol}`;
535
+ },
536
+ };
537
+ ```
538
+
539
+ All methods are optional — unimplemented ones fall back to the Mustache template.
540
+
541
+ | Method | Event type |
542
+ |--------|------------|
543
+ | `getOpenedMarkdown` | `IStrategyTickResultOpened` |
544
+ | `getClosedMarkdown` | `IStrategyTickResultClosed` |
545
+ | `getScheduledMarkdown` | `IStrategyTickResultScheduled` |
546
+ | `getCancelledMarkdown` | `IStrategyTickResultCancelled` |
547
+ | `getRiskMarkdown` | `RiskContract` |
548
+ | `getPartialProfitMarkdown` | `PartialProfitCommit` |
549
+ | `getPartialLossMarkdown` | `PartialLossCommit` |
550
+ | `getBreakevenMarkdown` | `BreakevenCommit` |
551
+ | `getTrailingTakeMarkdown` | `TrailingTakeCommit` |
552
+ | `getTrailingStopMarkdown` | `TrailingStopCommit` |
553
+ | `getAverageBuyMarkdown` | `AverageBuyCommit` |
554
+ | `getSignalOpenMarkdown` | `SignalOpenContract` |
555
+ | `getSignalCloseMarkdown` | `SignalCloseContract` |
556
+ | `getCancelScheduledMarkdown` | `CancelScheduledCommit` |
557
+ | `getClosePendingMarkdown` | `ClosePendingCommit` |
558
+ | `getSignalInfoMarkdown` | `SignalInfoContract` |
559
+
512
560
  ## 🧩 Module Hooks (Broker Adapter)
513
561
 
514
562
  The CLI supports **mode-specific module files** that are loaded as side-effect imports before the strategy starts. Each file is expected to call `Broker.useBrokerAdapter()` from `backtest-kit` to register a broker adapter.
package/build/index.cjs CHANGED
@@ -1929,6 +1929,9 @@ class TelegramLogicService {
1929
1929
  event,
1930
1930
  });
1931
1931
  const markdown = await this.telegramTemplateService.getTrailingTakeMarkdown(event);
1932
+ if (!markdown) {
1933
+ return;
1934
+ }
1932
1935
  await this.telegramWebService.publishNotify({
1933
1936
  symbol: event.symbol,
1934
1937
  markdown,
@@ -1939,6 +1942,9 @@ class TelegramLogicService {
1939
1942
  event,
1940
1943
  });
1941
1944
  const markdown = await this.telegramTemplateService.getTrailingStopMarkdown(event);
1945
+ if (!markdown) {
1946
+ return;
1947
+ }
1942
1948
  await this.telegramWebService.publishNotify({
1943
1949
  symbol: event.symbol,
1944
1950
  markdown,
@@ -1949,6 +1955,9 @@ class TelegramLogicService {
1949
1955
  event,
1950
1956
  });
1951
1957
  const markdown = await this.telegramTemplateService.getBreakevenMarkdown(event);
1958
+ if (!markdown) {
1959
+ return;
1960
+ }
1952
1961
  await this.telegramWebService.publishNotify({
1953
1962
  symbol: event.symbol,
1954
1963
  markdown,
@@ -1959,6 +1968,9 @@ class TelegramLogicService {
1959
1968
  event,
1960
1969
  });
1961
1970
  const markdown = await this.telegramTemplateService.getPartialProfitMarkdown(event);
1971
+ if (!markdown) {
1972
+ return;
1973
+ }
1962
1974
  await this.telegramWebService.publishNotify({
1963
1975
  symbol: event.symbol,
1964
1976
  markdown,
@@ -1969,6 +1981,9 @@ class TelegramLogicService {
1969
1981
  event,
1970
1982
  });
1971
1983
  const markdown = await this.telegramTemplateService.getPartialLossMarkdown(event);
1984
+ if (!markdown) {
1985
+ return;
1986
+ }
1972
1987
  await this.telegramWebService.publishNotify({
1973
1988
  symbol: event.symbol,
1974
1989
  markdown,
@@ -1979,6 +1994,9 @@ class TelegramLogicService {
1979
1994
  event,
1980
1995
  });
1981
1996
  const markdown = await this.telegramTemplateService.getScheduledMarkdown(event);
1997
+ if (!markdown) {
1998
+ return;
1999
+ }
1982
2000
  await this.telegramWebService.publishNotify({
1983
2001
  symbol: event.symbol,
1984
2002
  markdown,
@@ -1989,6 +2007,9 @@ class TelegramLogicService {
1989
2007
  event,
1990
2008
  });
1991
2009
  const markdown = await this.telegramTemplateService.getCancelledMarkdown(event);
2010
+ if (!markdown) {
2011
+ return;
2012
+ }
1992
2013
  await this.telegramWebService.publishNotify({
1993
2014
  symbol: event.symbol,
1994
2015
  markdown,
@@ -1999,6 +2020,9 @@ class TelegramLogicService {
1999
2020
  event,
2000
2021
  });
2001
2022
  const markdown = await this.telegramTemplateService.getOpenedMarkdown(event);
2023
+ if (!markdown) {
2024
+ return;
2025
+ }
2002
2026
  await this.telegramWebService.publishNotify({
2003
2027
  symbol: event.symbol,
2004
2028
  markdown,
@@ -2009,6 +2033,9 @@ class TelegramLogicService {
2009
2033
  event,
2010
2034
  });
2011
2035
  const markdown = await this.telegramTemplateService.getClosedMarkdown(event);
2036
+ if (!markdown) {
2037
+ return;
2038
+ }
2012
2039
  await this.telegramWebService.publishNotify({
2013
2040
  symbol: event.symbol,
2014
2041
  markdown,
@@ -2019,6 +2046,9 @@ class TelegramLogicService {
2019
2046
  event,
2020
2047
  });
2021
2048
  const markdown = await this.telegramTemplateService.getRiskMarkdown(event);
2049
+ if (!markdown) {
2050
+ return;
2051
+ }
2022
2052
  await this.telegramWebService.publishNotify({
2023
2053
  symbol: event.symbol,
2024
2054
  markdown,
@@ -2029,6 +2059,9 @@ class TelegramLogicService {
2029
2059
  event,
2030
2060
  });
2031
2061
  const markdown = await this.telegramTemplateService.getAverageBuyMarkdown(event);
2062
+ if (!markdown) {
2063
+ return;
2064
+ }
2032
2065
  await this.telegramWebService.publishNotify({
2033
2066
  symbol: event.symbol,
2034
2067
  markdown,
@@ -2039,6 +2072,9 @@ class TelegramLogicService {
2039
2072
  event,
2040
2073
  });
2041
2074
  const markdown = await this.telegramTemplateService.getSignalOpenMarkdown(event);
2075
+ if (!markdown) {
2076
+ return;
2077
+ }
2042
2078
  await this.telegramWebService.publishNotify({
2043
2079
  symbol: event.symbol,
2044
2080
  markdown,
@@ -2049,6 +2085,9 @@ class TelegramLogicService {
2049
2085
  event,
2050
2086
  });
2051
2087
  const markdown = await this.telegramTemplateService.getSignalCloseMarkdown(event);
2088
+ if (!markdown) {
2089
+ return;
2090
+ }
2052
2091
  await this.telegramWebService.publishNotify({
2053
2092
  symbol: event.symbol,
2054
2093
  markdown,
@@ -2059,6 +2098,9 @@ class TelegramLogicService {
2059
2098
  event,
2060
2099
  });
2061
2100
  const markdown = await this.telegramTemplateService.getSignalInfoMarkdown(event);
2101
+ if (!markdown) {
2102
+ return;
2103
+ }
2062
2104
  await this.telegramWebService.publishNotify({
2063
2105
  symbol: event.symbol,
2064
2106
  markdown,
@@ -2069,6 +2111,9 @@ class TelegramLogicService {
2069
2111
  event,
2070
2112
  });
2071
2113
  const markdown = await this.telegramTemplateService.getCancelScheduledMarkdown(event);
2114
+ if (!markdown) {
2115
+ return;
2116
+ }
2072
2117
  await this.telegramWebService.publishNotify({
2073
2118
  symbol: event.symbol,
2074
2119
  markdown,
@@ -2079,6 +2124,9 @@ class TelegramLogicService {
2079
2124
  event,
2080
2125
  });
2081
2126
  const markdown = await this.telegramTemplateService.getClosePendingMarkdown(event);
2127
+ if (!markdown) {
2128
+ return;
2129
+ }
2082
2130
  await this.telegramWebService.publishNotify({
2083
2131
  symbol: event.symbol,
2084
2132
  markdown,
@@ -2180,104 +2228,182 @@ const RENDER_TEMPLATE_FN = async (fileName, event, self) => {
2180
2228
  const template = await READ_TEMPLATE_FN(fileName, self);
2181
2229
  return Mustache.render(template, event);
2182
2230
  };
2231
+ const GET_TELEGRAM_CONFIG_FN = async (self) => {
2232
+ const exports = await self.configConnectionService.loadConfig("telegram.config");
2233
+ if (!exports) {
2234
+ return null;
2235
+ }
2236
+ return "default" in exports
2237
+ ? exports.default
2238
+ : exports;
2239
+ };
2183
2240
  class TelegramTemplateService {
2184
2241
  constructor() {
2185
2242
  this.loggerService = inject(TYPES.loggerService);
2186
2243
  this.resolveService = inject(TYPES.resolveService);
2244
+ this.configConnectionService = inject(TYPES.configConnectionService);
2245
+ this.getTelegramAdapter = functoolsKit.singleshot(async () => {
2246
+ this.loggerService.log("telegramTemplateService getTelegramAdapter");
2247
+ return await GET_TELEGRAM_CONFIG_FN(this);
2248
+ });
2187
2249
  this.getTrailingTakeMarkdown = async (event) => {
2188
2250
  this.loggerService.log("telegramTemplateService getTrailingTakeMarkdown", {
2189
2251
  event,
2190
2252
  });
2253
+ const adapter = await this.getTelegramAdapter();
2254
+ if (adapter?.getTrailingTakeMarkdown) {
2255
+ return await adapter.getTrailingTakeMarkdown(event);
2256
+ }
2191
2257
  return await RENDER_TEMPLATE_FN("trailing-take.mustache", event, this);
2192
2258
  };
2193
2259
  this.getTrailingStopMarkdown = async (event) => {
2194
2260
  this.loggerService.log("telegramTemplateService getTrailingStopMarkdown", {
2195
2261
  event,
2196
2262
  });
2263
+ const adapter = await this.getTelegramAdapter();
2264
+ if (adapter?.getTrailingStopMarkdown) {
2265
+ return await adapter.getTrailingStopMarkdown(event);
2266
+ }
2197
2267
  return await RENDER_TEMPLATE_FN("trailing-stop.mustache", event, this);
2198
2268
  };
2199
2269
  this.getBreakevenMarkdown = async (event) => {
2200
2270
  this.loggerService.log("telegramTemplateService getBreakevenMarkdown", {
2201
2271
  event,
2202
2272
  });
2273
+ const adapter = await this.getTelegramAdapter();
2274
+ if (adapter?.getBreakevenMarkdown) {
2275
+ return await adapter.getBreakevenMarkdown(event);
2276
+ }
2203
2277
  return await RENDER_TEMPLATE_FN("breakeven.mustache", event, this);
2204
2278
  };
2205
2279
  this.getPartialProfitMarkdown = async (event) => {
2206
2280
  this.loggerService.log("telegramTemplateService getPartialProfitMarkdown", {
2207
2281
  event,
2208
2282
  });
2283
+ const adapter = await this.getTelegramAdapter();
2284
+ if (adapter?.getPartialProfitMarkdown) {
2285
+ return await adapter.getPartialProfitMarkdown(event);
2286
+ }
2209
2287
  return await RENDER_TEMPLATE_FN("partial-profit.mustache", event, this);
2210
2288
  };
2211
2289
  this.getPartialLossMarkdown = async (event) => {
2212
2290
  this.loggerService.log("telegramTemplateService getPartialLossMarkdown", {
2213
2291
  event,
2214
2292
  });
2293
+ const adapter = await this.getTelegramAdapter();
2294
+ if (adapter?.getPartialLossMarkdown) {
2295
+ return await adapter.getPartialLossMarkdown(event);
2296
+ }
2215
2297
  return await RENDER_TEMPLATE_FN("partial-loss.mustache", event, this);
2216
2298
  };
2217
2299
  this.getScheduledMarkdown = async (event) => {
2218
2300
  this.loggerService.log("telegramTemplateService getScheduledMarkdown", {
2219
2301
  event,
2220
2302
  });
2303
+ const adapter = await this.getTelegramAdapter();
2304
+ if (adapter?.getScheduledMarkdown) {
2305
+ return await adapter.getScheduledMarkdown(event);
2306
+ }
2221
2307
  return await RENDER_TEMPLATE_FN("scheduled.mustache", event, this);
2222
2308
  };
2223
2309
  this.getCancelledMarkdown = async (event) => {
2224
2310
  this.loggerService.log("telegramTemplateService getCancelledMarkdown", {
2225
2311
  event,
2226
2312
  });
2313
+ const adapter = await this.getTelegramAdapter();
2314
+ if (adapter?.getCancelledMarkdown) {
2315
+ return await adapter.getCancelledMarkdown(event);
2316
+ }
2227
2317
  return await RENDER_TEMPLATE_FN("cancelled.mustache", event, this);
2228
2318
  };
2229
2319
  this.getOpenedMarkdown = async (event) => {
2230
2320
  this.loggerService.log("telegramTemplateService getOpenedMarkdown", {
2231
2321
  event,
2232
2322
  });
2323
+ const adapter = await this.getTelegramAdapter();
2324
+ if (adapter?.getOpenedMarkdown) {
2325
+ return await adapter.getOpenedMarkdown(event);
2326
+ }
2233
2327
  return await RENDER_TEMPLATE_FN("opened.mustache", event, this);
2234
2328
  };
2235
2329
  this.getClosedMarkdown = async (event) => {
2236
2330
  this.loggerService.log("telegramTemplateService getClosedMarkdown", {
2237
2331
  event,
2238
2332
  });
2333
+ const adapter = await this.getTelegramAdapter();
2334
+ if (adapter?.getClosedMarkdown) {
2335
+ return await adapter.getClosedMarkdown(event);
2336
+ }
2239
2337
  return await RENDER_TEMPLATE_FN("closed.mustache", event, this);
2240
2338
  };
2241
2339
  this.getRiskMarkdown = async (event) => {
2242
2340
  this.loggerService.log("telegramTemplateService getRiskMarkdown", {
2243
2341
  event,
2244
2342
  });
2343
+ const adapter = await this.getTelegramAdapter();
2344
+ if (adapter?.getRiskMarkdown) {
2345
+ return await adapter.getRiskMarkdown(event);
2346
+ }
2245
2347
  return await RENDER_TEMPLATE_FN("risk.mustache", event, this);
2246
2348
  };
2247
2349
  this.getAverageBuyMarkdown = async (event) => {
2248
2350
  this.loggerService.log("telegramTemplateService getAverageBuyMarkdown", {
2249
2351
  event,
2250
2352
  });
2353
+ const adapter = await this.getTelegramAdapter();
2354
+ if (adapter?.getAverageBuyMarkdown) {
2355
+ return await adapter.getAverageBuyMarkdown(event);
2356
+ }
2251
2357
  return await RENDER_TEMPLATE_FN("average-buy.mustache", event, this);
2252
2358
  };
2253
2359
  this.getSignalOpenMarkdown = async (event) => {
2254
2360
  this.loggerService.log("telegramTemplateService getSignalOpenMarkdown", {
2255
2361
  event,
2256
2362
  });
2363
+ const adapter = await this.getTelegramAdapter();
2364
+ if (adapter?.getSignalOpenMarkdown) {
2365
+ return await adapter.getSignalOpenMarkdown(event);
2366
+ }
2257
2367
  return await RENDER_TEMPLATE_FN("signal-open.mustache", event, this);
2258
2368
  };
2259
2369
  this.getSignalCloseMarkdown = async (event) => {
2260
2370
  this.loggerService.log("telegramTemplateService getSignalCloseMarkdown", {
2261
2371
  event,
2262
2372
  });
2373
+ const adapter = await this.getTelegramAdapter();
2374
+ if (adapter?.getSignalCloseMarkdown) {
2375
+ return await adapter.getSignalCloseMarkdown(event);
2376
+ }
2263
2377
  return await RENDER_TEMPLATE_FN("signal-close.mustache", event, this);
2264
2378
  };
2265
2379
  this.getCancelScheduledMarkdown = async (event) => {
2266
2380
  this.loggerService.log("telegramTemplateService getCancelScheduledMarkdown", {
2267
2381
  event,
2268
2382
  });
2383
+ const adapter = await this.getTelegramAdapter();
2384
+ if (adapter?.getCancelScheduledMarkdown) {
2385
+ return await adapter.getCancelScheduledMarkdown(event);
2386
+ }
2269
2387
  return await RENDER_TEMPLATE_FN("cancel-scheduled.mustache", event, this);
2270
2388
  };
2271
2389
  this.getClosePendingMarkdown = async (event) => {
2272
2390
  this.loggerService.log("telegramTemplateService getClosePendingMarkdown", {
2273
2391
  event,
2274
2392
  });
2393
+ const adapter = await this.getTelegramAdapter();
2394
+ if (adapter?.getClosePendingMarkdown) {
2395
+ return await adapter.getClosePendingMarkdown(event);
2396
+ }
2275
2397
  return await RENDER_TEMPLATE_FN("close-pending.mustache", event, this);
2276
2398
  };
2277
2399
  this.getSignalInfoMarkdown = async (event) => {
2278
2400
  this.loggerService.log("telegramTemplateService getSignalInfoMarkdown", {
2279
2401
  event,
2280
2402
  });
2403
+ const adapter = await this.getTelegramAdapter();
2404
+ if (adapter?.getSignalInfoMarkdown) {
2405
+ return await adapter.getSignalInfoMarkdown(event);
2406
+ }
2281
2407
  return await RENDER_TEMPLATE_FN("signal-info.mustache", event, this);
2282
2408
  };
2283
2409
  }
@@ -2854,7 +2980,7 @@ const main$d = async () => {
2854
2980
  if (MODES.some((mode) => values[mode])) {
2855
2981
  return;
2856
2982
  }
2857
- process.stdout.write(`@backtest-kit/cli ${"7.3.1"}\n`);
2983
+ process.stdout.write(`@backtest-kit/cli ${"7.4.0"}\n`);
2858
2984
  process.stdout.write("\n");
2859
2985
  process.stdout.write(`Run with --help to see available commands.\n`);
2860
2986
  process.stdout.write("\n");
@@ -3518,7 +3644,7 @@ const main$1 = async () => {
3518
3644
  if (!values.help) {
3519
3645
  return;
3520
3646
  }
3521
- process.stdout.write(`@backtest-kit/cli ${"7.3.1"}\n\n`);
3647
+ process.stdout.write(`@backtest-kit/cli ${"7.4.0"}\n\n`);
3522
3648
  process.stdout.write(HELP_TEXT);
3523
3649
  process.exit(0);
3524
3650
  };
@@ -3532,7 +3658,7 @@ const main = async () => {
3532
3658
  if (!values.version) {
3533
3659
  return;
3534
3660
  }
3535
- process.stdout.write(`@backtest-kit/cli ${"7.3.1"}\n`);
3661
+ process.stdout.write(`@backtest-kit/cli ${"7.4.0"}\n`);
3536
3662
  process.exit(0);
3537
3663
  };
3538
3664
  main();
package/build/index.mjs CHANGED
@@ -1904,6 +1904,9 @@ class TelegramLogicService {
1904
1904
  event,
1905
1905
  });
1906
1906
  const markdown = await this.telegramTemplateService.getTrailingTakeMarkdown(event);
1907
+ if (!markdown) {
1908
+ return;
1909
+ }
1907
1910
  await this.telegramWebService.publishNotify({
1908
1911
  symbol: event.symbol,
1909
1912
  markdown,
@@ -1914,6 +1917,9 @@ class TelegramLogicService {
1914
1917
  event,
1915
1918
  });
1916
1919
  const markdown = await this.telegramTemplateService.getTrailingStopMarkdown(event);
1920
+ if (!markdown) {
1921
+ return;
1922
+ }
1917
1923
  await this.telegramWebService.publishNotify({
1918
1924
  symbol: event.symbol,
1919
1925
  markdown,
@@ -1924,6 +1930,9 @@ class TelegramLogicService {
1924
1930
  event,
1925
1931
  });
1926
1932
  const markdown = await this.telegramTemplateService.getBreakevenMarkdown(event);
1933
+ if (!markdown) {
1934
+ return;
1935
+ }
1927
1936
  await this.telegramWebService.publishNotify({
1928
1937
  symbol: event.symbol,
1929
1938
  markdown,
@@ -1934,6 +1943,9 @@ class TelegramLogicService {
1934
1943
  event,
1935
1944
  });
1936
1945
  const markdown = await this.telegramTemplateService.getPartialProfitMarkdown(event);
1946
+ if (!markdown) {
1947
+ return;
1948
+ }
1937
1949
  await this.telegramWebService.publishNotify({
1938
1950
  symbol: event.symbol,
1939
1951
  markdown,
@@ -1944,6 +1956,9 @@ class TelegramLogicService {
1944
1956
  event,
1945
1957
  });
1946
1958
  const markdown = await this.telegramTemplateService.getPartialLossMarkdown(event);
1959
+ if (!markdown) {
1960
+ return;
1961
+ }
1947
1962
  await this.telegramWebService.publishNotify({
1948
1963
  symbol: event.symbol,
1949
1964
  markdown,
@@ -1954,6 +1969,9 @@ class TelegramLogicService {
1954
1969
  event,
1955
1970
  });
1956
1971
  const markdown = await this.telegramTemplateService.getScheduledMarkdown(event);
1972
+ if (!markdown) {
1973
+ return;
1974
+ }
1957
1975
  await this.telegramWebService.publishNotify({
1958
1976
  symbol: event.symbol,
1959
1977
  markdown,
@@ -1964,6 +1982,9 @@ class TelegramLogicService {
1964
1982
  event,
1965
1983
  });
1966
1984
  const markdown = await this.telegramTemplateService.getCancelledMarkdown(event);
1985
+ if (!markdown) {
1986
+ return;
1987
+ }
1967
1988
  await this.telegramWebService.publishNotify({
1968
1989
  symbol: event.symbol,
1969
1990
  markdown,
@@ -1974,6 +1995,9 @@ class TelegramLogicService {
1974
1995
  event,
1975
1996
  });
1976
1997
  const markdown = await this.telegramTemplateService.getOpenedMarkdown(event);
1998
+ if (!markdown) {
1999
+ return;
2000
+ }
1977
2001
  await this.telegramWebService.publishNotify({
1978
2002
  symbol: event.symbol,
1979
2003
  markdown,
@@ -1984,6 +2008,9 @@ class TelegramLogicService {
1984
2008
  event,
1985
2009
  });
1986
2010
  const markdown = await this.telegramTemplateService.getClosedMarkdown(event);
2011
+ if (!markdown) {
2012
+ return;
2013
+ }
1987
2014
  await this.telegramWebService.publishNotify({
1988
2015
  symbol: event.symbol,
1989
2016
  markdown,
@@ -1994,6 +2021,9 @@ class TelegramLogicService {
1994
2021
  event,
1995
2022
  });
1996
2023
  const markdown = await this.telegramTemplateService.getRiskMarkdown(event);
2024
+ if (!markdown) {
2025
+ return;
2026
+ }
1997
2027
  await this.telegramWebService.publishNotify({
1998
2028
  symbol: event.symbol,
1999
2029
  markdown,
@@ -2004,6 +2034,9 @@ class TelegramLogicService {
2004
2034
  event,
2005
2035
  });
2006
2036
  const markdown = await this.telegramTemplateService.getAverageBuyMarkdown(event);
2037
+ if (!markdown) {
2038
+ return;
2039
+ }
2007
2040
  await this.telegramWebService.publishNotify({
2008
2041
  symbol: event.symbol,
2009
2042
  markdown,
@@ -2014,6 +2047,9 @@ class TelegramLogicService {
2014
2047
  event,
2015
2048
  });
2016
2049
  const markdown = await this.telegramTemplateService.getSignalOpenMarkdown(event);
2050
+ if (!markdown) {
2051
+ return;
2052
+ }
2017
2053
  await this.telegramWebService.publishNotify({
2018
2054
  symbol: event.symbol,
2019
2055
  markdown,
@@ -2024,6 +2060,9 @@ class TelegramLogicService {
2024
2060
  event,
2025
2061
  });
2026
2062
  const markdown = await this.telegramTemplateService.getSignalCloseMarkdown(event);
2063
+ if (!markdown) {
2064
+ return;
2065
+ }
2027
2066
  await this.telegramWebService.publishNotify({
2028
2067
  symbol: event.symbol,
2029
2068
  markdown,
@@ -2034,6 +2073,9 @@ class TelegramLogicService {
2034
2073
  event,
2035
2074
  });
2036
2075
  const markdown = await this.telegramTemplateService.getSignalInfoMarkdown(event);
2076
+ if (!markdown) {
2077
+ return;
2078
+ }
2037
2079
  await this.telegramWebService.publishNotify({
2038
2080
  symbol: event.symbol,
2039
2081
  markdown,
@@ -2044,6 +2086,9 @@ class TelegramLogicService {
2044
2086
  event,
2045
2087
  });
2046
2088
  const markdown = await this.telegramTemplateService.getCancelScheduledMarkdown(event);
2089
+ if (!markdown) {
2090
+ return;
2091
+ }
2047
2092
  await this.telegramWebService.publishNotify({
2048
2093
  symbol: event.symbol,
2049
2094
  markdown,
@@ -2054,6 +2099,9 @@ class TelegramLogicService {
2054
2099
  event,
2055
2100
  });
2056
2101
  const markdown = await this.telegramTemplateService.getClosePendingMarkdown(event);
2102
+ if (!markdown) {
2103
+ return;
2104
+ }
2057
2105
  await this.telegramWebService.publishNotify({
2058
2106
  symbol: event.symbol,
2059
2107
  markdown,
@@ -2155,104 +2203,182 @@ const RENDER_TEMPLATE_FN = async (fileName, event, self) => {
2155
2203
  const template = await READ_TEMPLATE_FN(fileName, self);
2156
2204
  return Mustache.render(template, event);
2157
2205
  };
2206
+ const GET_TELEGRAM_CONFIG_FN = async (self) => {
2207
+ const exports = await self.configConnectionService.loadConfig("telegram.config");
2208
+ if (!exports) {
2209
+ return null;
2210
+ }
2211
+ return "default" in exports
2212
+ ? exports.default
2213
+ : exports;
2214
+ };
2158
2215
  class TelegramTemplateService {
2159
2216
  constructor() {
2160
2217
  this.loggerService = inject(TYPES.loggerService);
2161
2218
  this.resolveService = inject(TYPES.resolveService);
2219
+ this.configConnectionService = inject(TYPES.configConnectionService);
2220
+ this.getTelegramAdapter = singleshot(async () => {
2221
+ this.loggerService.log("telegramTemplateService getTelegramAdapter");
2222
+ return await GET_TELEGRAM_CONFIG_FN(this);
2223
+ });
2162
2224
  this.getTrailingTakeMarkdown = async (event) => {
2163
2225
  this.loggerService.log("telegramTemplateService getTrailingTakeMarkdown", {
2164
2226
  event,
2165
2227
  });
2228
+ const adapter = await this.getTelegramAdapter();
2229
+ if (adapter?.getTrailingTakeMarkdown) {
2230
+ return await adapter.getTrailingTakeMarkdown(event);
2231
+ }
2166
2232
  return await RENDER_TEMPLATE_FN("trailing-take.mustache", event, this);
2167
2233
  };
2168
2234
  this.getTrailingStopMarkdown = async (event) => {
2169
2235
  this.loggerService.log("telegramTemplateService getTrailingStopMarkdown", {
2170
2236
  event,
2171
2237
  });
2238
+ const adapter = await this.getTelegramAdapter();
2239
+ if (adapter?.getTrailingStopMarkdown) {
2240
+ return await adapter.getTrailingStopMarkdown(event);
2241
+ }
2172
2242
  return await RENDER_TEMPLATE_FN("trailing-stop.mustache", event, this);
2173
2243
  };
2174
2244
  this.getBreakevenMarkdown = async (event) => {
2175
2245
  this.loggerService.log("telegramTemplateService getBreakevenMarkdown", {
2176
2246
  event,
2177
2247
  });
2248
+ const adapter = await this.getTelegramAdapter();
2249
+ if (adapter?.getBreakevenMarkdown) {
2250
+ return await adapter.getBreakevenMarkdown(event);
2251
+ }
2178
2252
  return await RENDER_TEMPLATE_FN("breakeven.mustache", event, this);
2179
2253
  };
2180
2254
  this.getPartialProfitMarkdown = async (event) => {
2181
2255
  this.loggerService.log("telegramTemplateService getPartialProfitMarkdown", {
2182
2256
  event,
2183
2257
  });
2258
+ const adapter = await this.getTelegramAdapter();
2259
+ if (adapter?.getPartialProfitMarkdown) {
2260
+ return await adapter.getPartialProfitMarkdown(event);
2261
+ }
2184
2262
  return await RENDER_TEMPLATE_FN("partial-profit.mustache", event, this);
2185
2263
  };
2186
2264
  this.getPartialLossMarkdown = async (event) => {
2187
2265
  this.loggerService.log("telegramTemplateService getPartialLossMarkdown", {
2188
2266
  event,
2189
2267
  });
2268
+ const adapter = await this.getTelegramAdapter();
2269
+ if (adapter?.getPartialLossMarkdown) {
2270
+ return await adapter.getPartialLossMarkdown(event);
2271
+ }
2190
2272
  return await RENDER_TEMPLATE_FN("partial-loss.mustache", event, this);
2191
2273
  };
2192
2274
  this.getScheduledMarkdown = async (event) => {
2193
2275
  this.loggerService.log("telegramTemplateService getScheduledMarkdown", {
2194
2276
  event,
2195
2277
  });
2278
+ const adapter = await this.getTelegramAdapter();
2279
+ if (adapter?.getScheduledMarkdown) {
2280
+ return await adapter.getScheduledMarkdown(event);
2281
+ }
2196
2282
  return await RENDER_TEMPLATE_FN("scheduled.mustache", event, this);
2197
2283
  };
2198
2284
  this.getCancelledMarkdown = async (event) => {
2199
2285
  this.loggerService.log("telegramTemplateService getCancelledMarkdown", {
2200
2286
  event,
2201
2287
  });
2288
+ const adapter = await this.getTelegramAdapter();
2289
+ if (adapter?.getCancelledMarkdown) {
2290
+ return await adapter.getCancelledMarkdown(event);
2291
+ }
2202
2292
  return await RENDER_TEMPLATE_FN("cancelled.mustache", event, this);
2203
2293
  };
2204
2294
  this.getOpenedMarkdown = async (event) => {
2205
2295
  this.loggerService.log("telegramTemplateService getOpenedMarkdown", {
2206
2296
  event,
2207
2297
  });
2298
+ const adapter = await this.getTelegramAdapter();
2299
+ if (adapter?.getOpenedMarkdown) {
2300
+ return await adapter.getOpenedMarkdown(event);
2301
+ }
2208
2302
  return await RENDER_TEMPLATE_FN("opened.mustache", event, this);
2209
2303
  };
2210
2304
  this.getClosedMarkdown = async (event) => {
2211
2305
  this.loggerService.log("telegramTemplateService getClosedMarkdown", {
2212
2306
  event,
2213
2307
  });
2308
+ const adapter = await this.getTelegramAdapter();
2309
+ if (adapter?.getClosedMarkdown) {
2310
+ return await adapter.getClosedMarkdown(event);
2311
+ }
2214
2312
  return await RENDER_TEMPLATE_FN("closed.mustache", event, this);
2215
2313
  };
2216
2314
  this.getRiskMarkdown = async (event) => {
2217
2315
  this.loggerService.log("telegramTemplateService getRiskMarkdown", {
2218
2316
  event,
2219
2317
  });
2318
+ const adapter = await this.getTelegramAdapter();
2319
+ if (adapter?.getRiskMarkdown) {
2320
+ return await adapter.getRiskMarkdown(event);
2321
+ }
2220
2322
  return await RENDER_TEMPLATE_FN("risk.mustache", event, this);
2221
2323
  };
2222
2324
  this.getAverageBuyMarkdown = async (event) => {
2223
2325
  this.loggerService.log("telegramTemplateService getAverageBuyMarkdown", {
2224
2326
  event,
2225
2327
  });
2328
+ const adapter = await this.getTelegramAdapter();
2329
+ if (adapter?.getAverageBuyMarkdown) {
2330
+ return await adapter.getAverageBuyMarkdown(event);
2331
+ }
2226
2332
  return await RENDER_TEMPLATE_FN("average-buy.mustache", event, this);
2227
2333
  };
2228
2334
  this.getSignalOpenMarkdown = async (event) => {
2229
2335
  this.loggerService.log("telegramTemplateService getSignalOpenMarkdown", {
2230
2336
  event,
2231
2337
  });
2338
+ const adapter = await this.getTelegramAdapter();
2339
+ if (adapter?.getSignalOpenMarkdown) {
2340
+ return await adapter.getSignalOpenMarkdown(event);
2341
+ }
2232
2342
  return await RENDER_TEMPLATE_FN("signal-open.mustache", event, this);
2233
2343
  };
2234
2344
  this.getSignalCloseMarkdown = async (event) => {
2235
2345
  this.loggerService.log("telegramTemplateService getSignalCloseMarkdown", {
2236
2346
  event,
2237
2347
  });
2348
+ const adapter = await this.getTelegramAdapter();
2349
+ if (adapter?.getSignalCloseMarkdown) {
2350
+ return await adapter.getSignalCloseMarkdown(event);
2351
+ }
2238
2352
  return await RENDER_TEMPLATE_FN("signal-close.mustache", event, this);
2239
2353
  };
2240
2354
  this.getCancelScheduledMarkdown = async (event) => {
2241
2355
  this.loggerService.log("telegramTemplateService getCancelScheduledMarkdown", {
2242
2356
  event,
2243
2357
  });
2358
+ const adapter = await this.getTelegramAdapter();
2359
+ if (adapter?.getCancelScheduledMarkdown) {
2360
+ return await adapter.getCancelScheduledMarkdown(event);
2361
+ }
2244
2362
  return await RENDER_TEMPLATE_FN("cancel-scheduled.mustache", event, this);
2245
2363
  };
2246
2364
  this.getClosePendingMarkdown = async (event) => {
2247
2365
  this.loggerService.log("telegramTemplateService getClosePendingMarkdown", {
2248
2366
  event,
2249
2367
  });
2368
+ const adapter = await this.getTelegramAdapter();
2369
+ if (adapter?.getClosePendingMarkdown) {
2370
+ return await adapter.getClosePendingMarkdown(event);
2371
+ }
2250
2372
  return await RENDER_TEMPLATE_FN("close-pending.mustache", event, this);
2251
2373
  };
2252
2374
  this.getSignalInfoMarkdown = async (event) => {
2253
2375
  this.loggerService.log("telegramTemplateService getSignalInfoMarkdown", {
2254
2376
  event,
2255
2377
  });
2378
+ const adapter = await this.getTelegramAdapter();
2379
+ if (adapter?.getSignalInfoMarkdown) {
2380
+ return await adapter.getSignalInfoMarkdown(event);
2381
+ }
2256
2382
  return await RENDER_TEMPLATE_FN("signal-info.mustache", event, this);
2257
2383
  };
2258
2384
  }
@@ -2825,7 +2951,7 @@ const main$d = async () => {
2825
2951
  if (MODES.some((mode) => values[mode])) {
2826
2952
  return;
2827
2953
  }
2828
- process.stdout.write(`@backtest-kit/cli ${"7.3.1"}\n`);
2954
+ process.stdout.write(`@backtest-kit/cli ${"7.4.0"}\n`);
2829
2955
  process.stdout.write("\n");
2830
2956
  process.stdout.write(`Run with --help to see available commands.\n`);
2831
2957
  process.stdout.write("\n");
@@ -3489,7 +3615,7 @@ const main$1 = async () => {
3489
3615
  if (!values.help) {
3490
3616
  return;
3491
3617
  }
3492
- process.stdout.write(`@backtest-kit/cli ${"7.3.1"}\n\n`);
3618
+ process.stdout.write(`@backtest-kit/cli ${"7.4.0"}\n\n`);
3493
3619
  process.stdout.write(HELP_TEXT);
3494
3620
  process.exit(0);
3495
3621
  };
@@ -3503,7 +3629,7 @@ const main = async () => {
3503
3629
  if (!values.version) {
3504
3630
  return;
3505
3631
  }
3506
- process.stdout.write(`@backtest-kit/cli ${"7.3.1"}\n`);
3632
+ process.stdout.write(`@backtest-kit/cli ${"7.4.0"}\n`);
3507
3633
  process.exit(0);
3508
3634
  };
3509
3635
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backtest-kit/cli",
3
- "version": "7.3.1",
3
+ "version": "7.4.0",
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",
@@ -62,11 +62,11 @@
62
62
  "devDependencies": {
63
63
  "@babel/plugin-transform-modules-umd": "7.27.1",
64
64
  "@babel/standalone": "7.29.1",
65
- "@backtest-kit/graph": "7.3.0",
66
- "@backtest-kit/ollama": "7.3.0",
67
- "@backtest-kit/pinets": "7.3.0",
68
- "@backtest-kit/signals": "7.3.0",
69
- "@backtest-kit/ui": "7.3.0",
65
+ "@backtest-kit/graph": "7.4.0",
66
+ "@backtest-kit/ollama": "7.4.0",
67
+ "@backtest-kit/pinets": "7.4.0",
68
+ "@backtest-kit/signals": "7.4.0",
69
+ "@backtest-kit/ui": "7.4.0",
70
70
  "@rollup/plugin-replace": "6.0.3",
71
71
  "@rollup/plugin-typescript": "11.1.6",
72
72
  "@types/image-size": "0.7.0",
@@ -74,7 +74,7 @@
74
74
  "@types/mustache": "4.2.6",
75
75
  "@types/node": "22.9.0",
76
76
  "@types/stack-trace": "0.0.33",
77
- "backtest-kit": "7.3.0",
77
+ "backtest-kit": "7.4.0",
78
78
  "glob": "11.0.1",
79
79
  "markdown-it": "14.1.1",
80
80
  "rimraf": "6.0.1",
@@ -89,12 +89,12 @@
89
89
  "peerDependencies": {
90
90
  "@babel/plugin-transform-modules-umd": "^7.27.1",
91
91
  "@babel/standalone": "^7.29.1",
92
- "@backtest-kit/graph": "^7.3.0",
93
- "@backtest-kit/ollama": "^7.3.0",
94
- "@backtest-kit/pinets": "^7.3.0",
95
- "@backtest-kit/signals": "^7.3.0",
96
- "@backtest-kit/ui": "^7.3.0",
97
- "backtest-kit": "^7.3.0",
92
+ "@backtest-kit/graph": "^7.4.0",
93
+ "@backtest-kit/ollama": "^7.4.0",
94
+ "@backtest-kit/pinets": "^7.4.0",
95
+ "@backtest-kit/signals": "^7.4.0",
96
+ "@backtest-kit/ui": "^7.4.0",
97
+ "backtest-kit": "^7.4.0",
98
98
  "markdown-it": "^14.1.1",
99
99
  "typescript": "^5.0.0"
100
100
  },
@@ -13,12 +13,12 @@
13
13
  "license": "ISC",
14
14
  "type": "commonjs",
15
15
  "dependencies": {
16
- "@backtest-kit/cli": "^7.3.1",
17
- "@backtest-kit/graph": "^7.3.0",
18
- "@backtest-kit/pinets": "^7.3.0",
19
- "@backtest-kit/ui": "^7.3.0",
16
+ "@backtest-kit/cli": "^7.4.0",
17
+ "@backtest-kit/graph": "^7.4.0",
18
+ "@backtest-kit/pinets": "^7.4.0",
19
+ "@backtest-kit/ui": "^7.4.0",
20
20
  "agent-swarm-kit": "^2.6.0",
21
- "backtest-kit": "^7.3.0",
21
+ "backtest-kit": "^7.4.0",
22
22
  "functools-kit": "^2.3.0",
23
23
  "garch": "^1.2.3",
24
24
  "get-moment-stamp": "^1.1.2",
package/types.d.ts CHANGED
@@ -317,9 +317,43 @@ declare class TelegramLogicService {
317
317
  connect: (() => () => void) & functools_kit.ISingleshotClearable<() => () => void>;
318
318
  }
319
319
 
320
- declare class TelegramTemplateService {
320
+ interface NotificationConfig {
321
+ signal: boolean;
322
+ risk: boolean;
323
+ info: boolean;
324
+ breakeven: boolean;
325
+ common_error: boolean;
326
+ critical_error: boolean;
327
+ validation_error: boolean;
328
+ partial_loss: boolean;
329
+ partial_profit: boolean;
330
+ signal_sync: boolean;
331
+ strategy_commit: boolean;
332
+ }
333
+ interface TelegramConfig {
334
+ getTrailingTakeMarkdown(event: TrailingTakeCommit): Promise<string>;
335
+ getTrailingStopMarkdown(event: TrailingStopCommit): Promise<string>;
336
+ getBreakevenMarkdown(event: BreakevenCommit): Promise<string>;
337
+ getPartialProfitMarkdown(event: PartialProfitCommit): Promise<string>;
338
+ getPartialLossMarkdown(event: PartialLossCommit): Promise<string>;
339
+ getScheduledMarkdown(event: IStrategyTickResultScheduled): Promise<string>;
340
+ getCancelledMarkdown(event: IStrategyTickResultCancelled): Promise<string>;
341
+ getOpenedMarkdown(event: IStrategyTickResultOpened): Promise<string>;
342
+ getClosedMarkdown(event: IStrategyTickResultClosed): Promise<string>;
343
+ getRiskMarkdown(event: RiskContract): Promise<string>;
344
+ getAverageBuyMarkdown(event: AverageBuyCommit): Promise<string>;
345
+ getSignalOpenMarkdown(event: SignalOpenContract): Promise<string>;
346
+ getSignalCloseMarkdown(event: SignalCloseContract): Promise<string>;
347
+ getCancelScheduledMarkdown(event: CancelScheduledCommit): Promise<string>;
348
+ getClosePendingMarkdown(event: ClosePendingCommit): Promise<string>;
349
+ getSignalInfoMarkdown(event: SignalInfoContract): Promise<string>;
350
+ }
351
+
352
+ declare class TelegramTemplateService implements TelegramConfig {
321
353
  readonly loggerService: LoggerService;
322
354
  readonly resolveService: ResolveService;
355
+ readonly configConnectionService: ConfigConnectionService;
356
+ private getTelegramAdapter;
323
357
  getTrailingTakeMarkdown: (event: TrailingTakeCommit) => Promise<string>;
324
358
  getTrailingStopMarkdown: (event: TrailingStopCommit) => Promise<string>;
325
359
  getBreakevenMarkdown: (event: BreakevenCommit) => Promise<string>;
@@ -345,20 +379,6 @@ declare class ModuleConnectionService {
345
379
  loadModule: (fileName: string) => Promise<boolean>;
346
380
  }
347
381
 
348
- interface NotificationConfig {
349
- signal: boolean;
350
- risk: boolean;
351
- info: boolean;
352
- breakeven: boolean;
353
- common_error: boolean;
354
- critical_error: boolean;
355
- validation_error: boolean;
356
- partial_loss: boolean;
357
- partial_profit: boolean;
358
- signal_sync: boolean;
359
- strategy_commit: boolean;
360
- }
361
-
362
382
  declare class ConfigService {
363
383
  readonly loggerService: LoggerService;
364
384
  readonly configConnectionService: ConfigConnectionService;