@backtest-kit/cli 7.3.1 → 7.5.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.cjs CHANGED
@@ -570,6 +570,22 @@ const getArgs = functoolsKit.singleshot(() => {
570
570
  type: "boolean",
571
571
  default: false,
572
572
  },
573
+ pnldebug: {
574
+ type: "boolean",
575
+ default: false,
576
+ },
577
+ priceopen: {
578
+ type: "string",
579
+ default: "",
580
+ },
581
+ direction: {
582
+ type: "string",
583
+ default: "",
584
+ },
585
+ minutes: {
586
+ type: "string",
587
+ default: "",
588
+ },
573
589
  init: {
574
590
  type: "boolean",
575
591
  default: false,
@@ -716,11 +732,11 @@ class SetupUtils {
716
732
  BacktestKit.Markdown.enable();
717
733
  BacktestKit.Report.enable();
718
734
  BacktestKit.Dump.enable();
735
+ BacktestKit.State.enable();
719
736
  BacktestKit.Memory.enable();
720
737
  }
721
738
  {
722
739
  BacktestKit.Dump.useMarkdown();
723
- BacktestKit.Memory.usePersist();
724
740
  }
725
741
  {
726
742
  BacktestKit.StorageLive.usePersist();
@@ -734,6 +750,18 @@ class SetupUtils {
734
750
  BacktestKit.NotificationLive.usePersist();
735
751
  BacktestKit.NotificationBacktest.useMemory();
736
752
  }
753
+ {
754
+ BacktestKit.RecentLive.usePersist();
755
+ BacktestKit.RecentBacktest.useMemory();
756
+ }
757
+ {
758
+ BacktestKit.MemoryLive.usePersist();
759
+ BacktestKit.MemoryBacktest.useLocal();
760
+ }
761
+ {
762
+ BacktestKit.StateLive.usePersist();
763
+ BacktestKit.StateBacktest.useLocal();
764
+ }
737
765
  {
738
766
  BacktestKit.Markdown.useDummy();
739
767
  BacktestKit.Log.useJsonl();
@@ -776,6 +804,7 @@ class SetupUtils {
776
804
  BacktestKit.PersistIntervalAdapter.clear();
777
805
  BacktestKit.PersistMemoryAdapter.clear();
778
806
  BacktestKit.PersistRecentAdapter.clear();
807
+ BacktestKit.PersistStateAdapter.clear();
779
808
  }
780
809
  {
781
810
  BacktestKit.Dump.clear();
@@ -1929,6 +1958,9 @@ class TelegramLogicService {
1929
1958
  event,
1930
1959
  });
1931
1960
  const markdown = await this.telegramTemplateService.getTrailingTakeMarkdown(event);
1961
+ if (!markdown) {
1962
+ return;
1963
+ }
1932
1964
  await this.telegramWebService.publishNotify({
1933
1965
  symbol: event.symbol,
1934
1966
  markdown,
@@ -1939,6 +1971,9 @@ class TelegramLogicService {
1939
1971
  event,
1940
1972
  });
1941
1973
  const markdown = await this.telegramTemplateService.getTrailingStopMarkdown(event);
1974
+ if (!markdown) {
1975
+ return;
1976
+ }
1942
1977
  await this.telegramWebService.publishNotify({
1943
1978
  symbol: event.symbol,
1944
1979
  markdown,
@@ -1949,6 +1984,9 @@ class TelegramLogicService {
1949
1984
  event,
1950
1985
  });
1951
1986
  const markdown = await this.telegramTemplateService.getBreakevenMarkdown(event);
1987
+ if (!markdown) {
1988
+ return;
1989
+ }
1952
1990
  await this.telegramWebService.publishNotify({
1953
1991
  symbol: event.symbol,
1954
1992
  markdown,
@@ -1959,6 +1997,9 @@ class TelegramLogicService {
1959
1997
  event,
1960
1998
  });
1961
1999
  const markdown = await this.telegramTemplateService.getPartialProfitMarkdown(event);
2000
+ if (!markdown) {
2001
+ return;
2002
+ }
1962
2003
  await this.telegramWebService.publishNotify({
1963
2004
  symbol: event.symbol,
1964
2005
  markdown,
@@ -1969,6 +2010,9 @@ class TelegramLogicService {
1969
2010
  event,
1970
2011
  });
1971
2012
  const markdown = await this.telegramTemplateService.getPartialLossMarkdown(event);
2013
+ if (!markdown) {
2014
+ return;
2015
+ }
1972
2016
  await this.telegramWebService.publishNotify({
1973
2017
  symbol: event.symbol,
1974
2018
  markdown,
@@ -1979,6 +2023,9 @@ class TelegramLogicService {
1979
2023
  event,
1980
2024
  });
1981
2025
  const markdown = await this.telegramTemplateService.getScheduledMarkdown(event);
2026
+ if (!markdown) {
2027
+ return;
2028
+ }
1982
2029
  await this.telegramWebService.publishNotify({
1983
2030
  symbol: event.symbol,
1984
2031
  markdown,
@@ -1989,6 +2036,9 @@ class TelegramLogicService {
1989
2036
  event,
1990
2037
  });
1991
2038
  const markdown = await this.telegramTemplateService.getCancelledMarkdown(event);
2039
+ if (!markdown) {
2040
+ return;
2041
+ }
1992
2042
  await this.telegramWebService.publishNotify({
1993
2043
  symbol: event.symbol,
1994
2044
  markdown,
@@ -1999,6 +2049,9 @@ class TelegramLogicService {
1999
2049
  event,
2000
2050
  });
2001
2051
  const markdown = await this.telegramTemplateService.getOpenedMarkdown(event);
2052
+ if (!markdown) {
2053
+ return;
2054
+ }
2002
2055
  await this.telegramWebService.publishNotify({
2003
2056
  symbol: event.symbol,
2004
2057
  markdown,
@@ -2009,6 +2062,9 @@ class TelegramLogicService {
2009
2062
  event,
2010
2063
  });
2011
2064
  const markdown = await this.telegramTemplateService.getClosedMarkdown(event);
2065
+ if (!markdown) {
2066
+ return;
2067
+ }
2012
2068
  await this.telegramWebService.publishNotify({
2013
2069
  symbol: event.symbol,
2014
2070
  markdown,
@@ -2019,6 +2075,9 @@ class TelegramLogicService {
2019
2075
  event,
2020
2076
  });
2021
2077
  const markdown = await this.telegramTemplateService.getRiskMarkdown(event);
2078
+ if (!markdown) {
2079
+ return;
2080
+ }
2022
2081
  await this.telegramWebService.publishNotify({
2023
2082
  symbol: event.symbol,
2024
2083
  markdown,
@@ -2029,6 +2088,9 @@ class TelegramLogicService {
2029
2088
  event,
2030
2089
  });
2031
2090
  const markdown = await this.telegramTemplateService.getAverageBuyMarkdown(event);
2091
+ if (!markdown) {
2092
+ return;
2093
+ }
2032
2094
  await this.telegramWebService.publishNotify({
2033
2095
  symbol: event.symbol,
2034
2096
  markdown,
@@ -2039,6 +2101,9 @@ class TelegramLogicService {
2039
2101
  event,
2040
2102
  });
2041
2103
  const markdown = await this.telegramTemplateService.getSignalOpenMarkdown(event);
2104
+ if (!markdown) {
2105
+ return;
2106
+ }
2042
2107
  await this.telegramWebService.publishNotify({
2043
2108
  symbol: event.symbol,
2044
2109
  markdown,
@@ -2049,6 +2114,9 @@ class TelegramLogicService {
2049
2114
  event,
2050
2115
  });
2051
2116
  const markdown = await this.telegramTemplateService.getSignalCloseMarkdown(event);
2117
+ if (!markdown) {
2118
+ return;
2119
+ }
2052
2120
  await this.telegramWebService.publishNotify({
2053
2121
  symbol: event.symbol,
2054
2122
  markdown,
@@ -2059,6 +2127,9 @@ class TelegramLogicService {
2059
2127
  event,
2060
2128
  });
2061
2129
  const markdown = await this.telegramTemplateService.getSignalInfoMarkdown(event);
2130
+ if (!markdown) {
2131
+ return;
2132
+ }
2062
2133
  await this.telegramWebService.publishNotify({
2063
2134
  symbol: event.symbol,
2064
2135
  markdown,
@@ -2069,6 +2140,9 @@ class TelegramLogicService {
2069
2140
  event,
2070
2141
  });
2071
2142
  const markdown = await this.telegramTemplateService.getCancelScheduledMarkdown(event);
2143
+ if (!markdown) {
2144
+ return;
2145
+ }
2072
2146
  await this.telegramWebService.publishNotify({
2073
2147
  symbol: event.symbol,
2074
2148
  markdown,
@@ -2079,6 +2153,9 @@ class TelegramLogicService {
2079
2153
  event,
2080
2154
  });
2081
2155
  const markdown = await this.telegramTemplateService.getClosePendingMarkdown(event);
2156
+ if (!markdown) {
2157
+ return;
2158
+ }
2082
2159
  await this.telegramWebService.publishNotify({
2083
2160
  symbol: event.symbol,
2084
2161
  markdown,
@@ -2180,104 +2257,182 @@ const RENDER_TEMPLATE_FN = async (fileName, event, self) => {
2180
2257
  const template = await READ_TEMPLATE_FN(fileName, self);
2181
2258
  return Mustache.render(template, event);
2182
2259
  };
2260
+ const GET_TELEGRAM_CONFIG_FN = async (self) => {
2261
+ const exports = await self.configConnectionService.loadConfig("telegram.config");
2262
+ if (!exports) {
2263
+ return null;
2264
+ }
2265
+ return "default" in exports
2266
+ ? exports.default
2267
+ : exports;
2268
+ };
2183
2269
  class TelegramTemplateService {
2184
2270
  constructor() {
2185
2271
  this.loggerService = inject(TYPES.loggerService);
2186
2272
  this.resolveService = inject(TYPES.resolveService);
2273
+ this.configConnectionService = inject(TYPES.configConnectionService);
2274
+ this.getTelegramAdapter = functoolsKit.singleshot(async () => {
2275
+ this.loggerService.log("telegramTemplateService getTelegramAdapter");
2276
+ return await GET_TELEGRAM_CONFIG_FN(this);
2277
+ });
2187
2278
  this.getTrailingTakeMarkdown = async (event) => {
2188
2279
  this.loggerService.log("telegramTemplateService getTrailingTakeMarkdown", {
2189
2280
  event,
2190
2281
  });
2282
+ const adapter = await this.getTelegramAdapter();
2283
+ if (adapter?.getTrailingTakeMarkdown) {
2284
+ return await adapter.getTrailingTakeMarkdown(event);
2285
+ }
2191
2286
  return await RENDER_TEMPLATE_FN("trailing-take.mustache", event, this);
2192
2287
  };
2193
2288
  this.getTrailingStopMarkdown = async (event) => {
2194
2289
  this.loggerService.log("telegramTemplateService getTrailingStopMarkdown", {
2195
2290
  event,
2196
2291
  });
2292
+ const adapter = await this.getTelegramAdapter();
2293
+ if (adapter?.getTrailingStopMarkdown) {
2294
+ return await adapter.getTrailingStopMarkdown(event);
2295
+ }
2197
2296
  return await RENDER_TEMPLATE_FN("trailing-stop.mustache", event, this);
2198
2297
  };
2199
2298
  this.getBreakevenMarkdown = async (event) => {
2200
2299
  this.loggerService.log("telegramTemplateService getBreakevenMarkdown", {
2201
2300
  event,
2202
2301
  });
2302
+ const adapter = await this.getTelegramAdapter();
2303
+ if (adapter?.getBreakevenMarkdown) {
2304
+ return await adapter.getBreakevenMarkdown(event);
2305
+ }
2203
2306
  return await RENDER_TEMPLATE_FN("breakeven.mustache", event, this);
2204
2307
  };
2205
2308
  this.getPartialProfitMarkdown = async (event) => {
2206
2309
  this.loggerService.log("telegramTemplateService getPartialProfitMarkdown", {
2207
2310
  event,
2208
2311
  });
2312
+ const adapter = await this.getTelegramAdapter();
2313
+ if (adapter?.getPartialProfitMarkdown) {
2314
+ return await adapter.getPartialProfitMarkdown(event);
2315
+ }
2209
2316
  return await RENDER_TEMPLATE_FN("partial-profit.mustache", event, this);
2210
2317
  };
2211
2318
  this.getPartialLossMarkdown = async (event) => {
2212
2319
  this.loggerService.log("telegramTemplateService getPartialLossMarkdown", {
2213
2320
  event,
2214
2321
  });
2322
+ const adapter = await this.getTelegramAdapter();
2323
+ if (adapter?.getPartialLossMarkdown) {
2324
+ return await adapter.getPartialLossMarkdown(event);
2325
+ }
2215
2326
  return await RENDER_TEMPLATE_FN("partial-loss.mustache", event, this);
2216
2327
  };
2217
2328
  this.getScheduledMarkdown = async (event) => {
2218
2329
  this.loggerService.log("telegramTemplateService getScheduledMarkdown", {
2219
2330
  event,
2220
2331
  });
2332
+ const adapter = await this.getTelegramAdapter();
2333
+ if (adapter?.getScheduledMarkdown) {
2334
+ return await adapter.getScheduledMarkdown(event);
2335
+ }
2221
2336
  return await RENDER_TEMPLATE_FN("scheduled.mustache", event, this);
2222
2337
  };
2223
2338
  this.getCancelledMarkdown = async (event) => {
2224
2339
  this.loggerService.log("telegramTemplateService getCancelledMarkdown", {
2225
2340
  event,
2226
2341
  });
2342
+ const adapter = await this.getTelegramAdapter();
2343
+ if (adapter?.getCancelledMarkdown) {
2344
+ return await adapter.getCancelledMarkdown(event);
2345
+ }
2227
2346
  return await RENDER_TEMPLATE_FN("cancelled.mustache", event, this);
2228
2347
  };
2229
2348
  this.getOpenedMarkdown = async (event) => {
2230
2349
  this.loggerService.log("telegramTemplateService getOpenedMarkdown", {
2231
2350
  event,
2232
2351
  });
2352
+ const adapter = await this.getTelegramAdapter();
2353
+ if (adapter?.getOpenedMarkdown) {
2354
+ return await adapter.getOpenedMarkdown(event);
2355
+ }
2233
2356
  return await RENDER_TEMPLATE_FN("opened.mustache", event, this);
2234
2357
  };
2235
2358
  this.getClosedMarkdown = async (event) => {
2236
2359
  this.loggerService.log("telegramTemplateService getClosedMarkdown", {
2237
2360
  event,
2238
2361
  });
2362
+ const adapter = await this.getTelegramAdapter();
2363
+ if (adapter?.getClosedMarkdown) {
2364
+ return await adapter.getClosedMarkdown(event);
2365
+ }
2239
2366
  return await RENDER_TEMPLATE_FN("closed.mustache", event, this);
2240
2367
  };
2241
2368
  this.getRiskMarkdown = async (event) => {
2242
2369
  this.loggerService.log("telegramTemplateService getRiskMarkdown", {
2243
2370
  event,
2244
2371
  });
2372
+ const adapter = await this.getTelegramAdapter();
2373
+ if (adapter?.getRiskMarkdown) {
2374
+ return await adapter.getRiskMarkdown(event);
2375
+ }
2245
2376
  return await RENDER_TEMPLATE_FN("risk.mustache", event, this);
2246
2377
  };
2247
2378
  this.getAverageBuyMarkdown = async (event) => {
2248
2379
  this.loggerService.log("telegramTemplateService getAverageBuyMarkdown", {
2249
2380
  event,
2250
2381
  });
2382
+ const adapter = await this.getTelegramAdapter();
2383
+ if (adapter?.getAverageBuyMarkdown) {
2384
+ return await adapter.getAverageBuyMarkdown(event);
2385
+ }
2251
2386
  return await RENDER_TEMPLATE_FN("average-buy.mustache", event, this);
2252
2387
  };
2253
2388
  this.getSignalOpenMarkdown = async (event) => {
2254
2389
  this.loggerService.log("telegramTemplateService getSignalOpenMarkdown", {
2255
2390
  event,
2256
2391
  });
2392
+ const adapter = await this.getTelegramAdapter();
2393
+ if (adapter?.getSignalOpenMarkdown) {
2394
+ return await adapter.getSignalOpenMarkdown(event);
2395
+ }
2257
2396
  return await RENDER_TEMPLATE_FN("signal-open.mustache", event, this);
2258
2397
  };
2259
2398
  this.getSignalCloseMarkdown = async (event) => {
2260
2399
  this.loggerService.log("telegramTemplateService getSignalCloseMarkdown", {
2261
2400
  event,
2262
2401
  });
2402
+ const adapter = await this.getTelegramAdapter();
2403
+ if (adapter?.getSignalCloseMarkdown) {
2404
+ return await adapter.getSignalCloseMarkdown(event);
2405
+ }
2263
2406
  return await RENDER_TEMPLATE_FN("signal-close.mustache", event, this);
2264
2407
  };
2265
2408
  this.getCancelScheduledMarkdown = async (event) => {
2266
2409
  this.loggerService.log("telegramTemplateService getCancelScheduledMarkdown", {
2267
2410
  event,
2268
2411
  });
2412
+ const adapter = await this.getTelegramAdapter();
2413
+ if (adapter?.getCancelScheduledMarkdown) {
2414
+ return await adapter.getCancelScheduledMarkdown(event);
2415
+ }
2269
2416
  return await RENDER_TEMPLATE_FN("cancel-scheduled.mustache", event, this);
2270
2417
  };
2271
2418
  this.getClosePendingMarkdown = async (event) => {
2272
2419
  this.loggerService.log("telegramTemplateService getClosePendingMarkdown", {
2273
2420
  event,
2274
2421
  });
2422
+ const adapter = await this.getTelegramAdapter();
2423
+ if (adapter?.getClosePendingMarkdown) {
2424
+ return await adapter.getClosePendingMarkdown(event);
2425
+ }
2275
2426
  return await RENDER_TEMPLATE_FN("close-pending.mustache", event, this);
2276
2427
  };
2277
2428
  this.getSignalInfoMarkdown = async (event) => {
2278
2429
  this.loggerService.log("telegramTemplateService getSignalInfoMarkdown", {
2279
2430
  event,
2280
2431
  });
2432
+ const adapter = await this.getTelegramAdapter();
2433
+ if (adapter?.getSignalInfoMarkdown) {
2434
+ return await adapter.getSignalInfoMarkdown(event);
2435
+ }
2281
2436
  return await RENDER_TEMPLATE_FN("signal-info.mustache", event, this);
2282
2437
  };
2283
2438
  }
@@ -2839,14 +2994,14 @@ const cli = {
2839
2994
  };
2840
2995
  init();
2841
2996
 
2842
- const MODES = ["backtest", "walker", "paper", "live", "pine", "editor", "dump", "flush", "init", "help", "version"];
2997
+ const MODES = ["backtest", "walker", "paper", "live", "pine", "editor", "dump", "pnldebug", "flush", "init", "help", "version"];
2843
2998
  const ENTRY_PATH$1 = "./node_modules/@backtest-kit/cli/build/index.mjs";
2844
2999
  const HELP_TEXT$1 = `
2845
3000
  Example:
2846
3001
 
2847
3002
  node ${ENTRY_PATH$1} --help
2848
3003
  `.trimStart();
2849
- const main$d = async () => {
3004
+ const main$e = async () => {
2850
3005
  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)))) {
2851
3006
  return;
2852
3007
  }
@@ -2854,14 +3009,14 @@ const main$d = async () => {
2854
3009
  if (MODES.some((mode) => values[mode])) {
2855
3010
  return;
2856
3011
  }
2857
- process.stdout.write(`@backtest-kit/cli ${"7.3.1"}\n`);
3012
+ process.stdout.write(`@backtest-kit/cli ${"7.5.0"}\n`);
2858
3013
  process.stdout.write("\n");
2859
3014
  process.stdout.write(`Run with --help to see available commands.\n`);
2860
3015
  process.stdout.write("\n");
2861
3016
  process.stdout.write(HELP_TEXT$1);
2862
3017
  process.exit(0);
2863
3018
  };
2864
- main$d();
3019
+ main$e();
2865
3020
 
2866
3021
  const notifyShutdown = functoolsKit.singleshot(async () => {
2867
3022
  console.log("Graceful shutdown initiated. Press Ctrl+C again to force quit.");
@@ -2877,7 +3032,7 @@ const flush = async (entryPoint) => {
2877
3032
  console.log(`Removed: ${target}`);
2878
3033
  }
2879
3034
  };
2880
- const main$c = async () => {
3035
+ const main$d = async () => {
2881
3036
  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)))) {
2882
3037
  return;
2883
3038
  }
@@ -2894,7 +3049,7 @@ const main$c = async () => {
2894
3049
  }
2895
3050
  process.exit(0);
2896
3051
  };
2897
- main$c();
3052
+ main$d();
2898
3053
 
2899
3054
  const BEFORE_EXIT_FN$5 = functoolsKit.singleshot(async () => {
2900
3055
  process.off("SIGINT", BEFORE_EXIT_FN$5);
@@ -2916,7 +3071,7 @@ const BEFORE_EXIT_FN$5 = functoolsKit.singleshot(async () => {
2916
3071
  const listenGracefulShutdown$5 = functoolsKit.singleshot(() => {
2917
3072
  process.on("SIGINT", BEFORE_EXIT_FN$5);
2918
3073
  });
2919
- const main$b = async () => {
3074
+ const main$c = async () => {
2920
3075
  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)))) {
2921
3076
  return;
2922
3077
  }
@@ -2931,7 +3086,7 @@ const main$b = async () => {
2931
3086
  await cli.backtestMainService.connect();
2932
3087
  listenGracefulShutdown$5();
2933
3088
  };
2934
- main$b();
3089
+ main$c();
2935
3090
 
2936
3091
  const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
2937
3092
  process.off("SIGINT", BEFORE_EXIT_FN$4);
@@ -2949,7 +3104,7 @@ const BEFORE_EXIT_FN$4 = functoolsKit.singleshot(async () => {
2949
3104
  const listenGracefulShutdown$4 = functoolsKit.singleshot(() => {
2950
3105
  process.on("SIGINT", BEFORE_EXIT_FN$4);
2951
3106
  });
2952
- const main$a = async () => {
3107
+ const main$b = async () => {
2953
3108
  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)))) {
2954
3109
  return;
2955
3110
  }
@@ -2965,7 +3120,7 @@ const main$a = async () => {
2965
3120
  listenGracefulShutdown$4();
2966
3121
  await cli.walkerMainService.connect();
2967
3122
  };
2968
- main$a();
3123
+ main$b();
2969
3124
 
2970
3125
  const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
2971
3126
  process.off("SIGINT", BEFORE_EXIT_FN$3);
@@ -2986,7 +3141,7 @@ const BEFORE_EXIT_FN$3 = functoolsKit.singleshot(async () => {
2986
3141
  const listenGracefulShutdown$3 = functoolsKit.singleshot(() => {
2987
3142
  process.on("SIGINT", BEFORE_EXIT_FN$3);
2988
3143
  });
2989
- const main$9 = async () => {
3144
+ const main$a = async () => {
2990
3145
  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)))) {
2991
3146
  return;
2992
3147
  }
@@ -2997,7 +3152,7 @@ const main$9 = async () => {
2997
3152
  cli.paperMainService.connect();
2998
3153
  listenGracefulShutdown$3();
2999
3154
  };
3000
- main$9();
3155
+ main$a();
3001
3156
 
3002
3157
  const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
3003
3158
  process.off("SIGINT", BEFORE_EXIT_FN$2);
@@ -3018,7 +3173,7 @@ const BEFORE_EXIT_FN$2 = functoolsKit.singleshot(async () => {
3018
3173
  const listenGracefulShutdown$2 = functoolsKit.singleshot(() => {
3019
3174
  process.on("SIGINT", BEFORE_EXIT_FN$2);
3020
3175
  });
3021
- const main$8 = async () => {
3176
+ const main$9 = async () => {
3022
3177
  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)))) {
3023
3178
  return;
3024
3179
  }
@@ -3029,7 +3184,7 @@ const main$8 = async () => {
3029
3184
  await cli.liveMainService.connect();
3030
3185
  listenGracefulShutdown$2();
3031
3186
  };
3032
- main$8();
3187
+ main$9();
3033
3188
 
3034
3189
  const BEFORE_EXIT_FN$1 = functoolsKit.singleshot(async () => {
3035
3190
  process.off("SIGINT", BEFORE_EXIT_FN$1);
@@ -3039,7 +3194,7 @@ const BEFORE_EXIT_FN$1 = functoolsKit.singleshot(async () => {
3039
3194
  const listenGracefulShutdown$1 = functoolsKit.singleshot(() => {
3040
3195
  process.on("SIGINT", BEFORE_EXIT_FN$1);
3041
3196
  });
3042
- const main$7 = async () => {
3197
+ const main$8 = async () => {
3043
3198
  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)))) {
3044
3199
  return;
3045
3200
  }
@@ -3049,7 +3204,7 @@ const main$7 = async () => {
3049
3204
  }
3050
3205
  listenGracefulShutdown$1();
3051
3206
  };
3052
- main$7();
3207
+ main$8();
3053
3208
 
3054
3209
  const BEFORE_EXIT_FN = functoolsKit.singleshot(async () => {
3055
3210
  process.off("SIGINT", BEFORE_EXIT_FN);
@@ -3059,7 +3214,7 @@ const BEFORE_EXIT_FN = functoolsKit.singleshot(async () => {
3059
3214
  const listenGracefulShutdown = functoolsKit.singleshot(() => {
3060
3215
  process.on("SIGINT", BEFORE_EXIT_FN);
3061
3216
  });
3062
- const main$6 = async () => {
3217
+ const main$7 = async () => {
3063
3218
  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)))) {
3064
3219
  return;
3065
3220
  }
@@ -3069,7 +3224,7 @@ const main$6 = async () => {
3069
3224
  }
3070
3225
  listenGracefulShutdown();
3071
3226
  };
3072
- main$6();
3227
+ main$7();
3073
3228
 
3074
3229
  const EXTRACT_ROWS_FN = (plots, schema) => {
3075
3230
  const keys = Object.keys(schema);
@@ -3091,7 +3246,7 @@ const EXTRACT_ROWS_FN = (plots, schema) => {
3091
3246
  }
3092
3247
  return rows;
3093
3248
  };
3094
- const main$5 = async () => {
3249
+ const main$6 = async () => {
3095
3250
  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)))) {
3096
3251
  return;
3097
3252
  }
@@ -3171,9 +3326,9 @@ const main$5 = async () => {
3171
3326
  console.log(await BacktestKitPinets.toMarkdown(signalId, plots, signalSchema));
3172
3327
  process.exit(0);
3173
3328
  };
3174
- main$5();
3329
+ main$6();
3175
3330
 
3176
- const main$4 = async () => {
3331
+ const main$5 = async () => {
3177
3332
  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)))) {
3178
3333
  return;
3179
3334
  }
@@ -3208,9 +3363,9 @@ const main$4 = async () => {
3208
3363
  };
3209
3364
  process.on("SIGINT", beforeExit);
3210
3365
  };
3211
- main$4();
3366
+ main$5();
3212
3367
 
3213
- const main$3 = async () => {
3368
+ const main$4 = async () => {
3214
3369
  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)))) {
3215
3370
  return;
3216
3371
  }
@@ -3271,6 +3426,102 @@ const main$3 = async () => {
3271
3426
  console.log(JSON.stringify(candles, null, 2));
3272
3427
  process.exit(0);
3273
3428
  };
3429
+ main$4();
3430
+
3431
+ const main$3 = async () => {
3432
+ 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)))) {
3433
+ return;
3434
+ }
3435
+ const { values } = getArgs();
3436
+ if (!values.pnldebug) {
3437
+ return;
3438
+ }
3439
+ await cli.moduleConnectionService.loadModule("./pnldebug.module");
3440
+ {
3441
+ await cli.exchangeSchemaService.addSchema();
3442
+ await cli.symbolSchemaService.addSchema();
3443
+ }
3444
+ const [defaultExchangeName = null] = await BacktestKit.listExchangeSchema();
3445
+ const exchangeName = values.exchange || defaultExchangeName?.exchangeName;
3446
+ const symbol = values.symbol || "BTCUSDT";
3447
+ const priceOpenStr = values.priceopen;
3448
+ if (!priceOpenStr) {
3449
+ console.error("Error: --priceopen is required");
3450
+ process.exit(1);
3451
+ }
3452
+ const priceOpen = parseFloat(priceOpenStr);
3453
+ if (isNaN(priceOpen)) {
3454
+ console.error(`Error: --priceopen must be a number, got: ${priceOpenStr}`);
3455
+ process.exit(1);
3456
+ }
3457
+ const direction = (values.direction || "long").toLowerCase();
3458
+ if (direction !== "long" && direction !== "short") {
3459
+ console.error(`Error: --direction must be 'long' or 'short', got: ${direction}`);
3460
+ process.exit(1);
3461
+ }
3462
+ const whenStr = values.when || Date.now().toString();
3463
+ const whenStamp = Date.parse(whenStr);
3464
+ const when = isNaN(whenStamp) ? new Date() : new Date(whenStamp);
3465
+ const timestamp = BacktestKit.alignToInterval(when, "1m").getTime();
3466
+ const minutesStr = values.minutes || "60";
3467
+ const minutesNum = parseInt(minutesStr);
3468
+ const minutes = isNaN(minutesNum) ? 60 : minutesNum;
3469
+ const candles = await BacktestKit.Exchange.getRawCandles(symbol, "1m", { exchangeName }, minutes, undefined, timestamp);
3470
+ if (candles.length === 0) {
3471
+ console.error("Error: no candles returned for the given parameters");
3472
+ process.exit(1);
3473
+ }
3474
+ let peak = 0;
3475
+ let drawdown = 0;
3476
+ const rows = candles.map((c, i) => {
3477
+ const pnl = direction === "short"
3478
+ ? (priceOpen - c.close) / priceOpen * 100
3479
+ : (c.close - priceOpen) / priceOpen * 100;
3480
+ if (pnl > peak)
3481
+ peak = pnl;
3482
+ if (pnl < drawdown)
3483
+ drawdown = pnl;
3484
+ return { min: i + 1, timestamp: c.timestamp, close: c.close, pnl, peak, drawdown };
3485
+ });
3486
+ const dumpName = values.output || `${symbol}_${direction}_${priceOpen}_${timestamp}`;
3487
+ const dumpDir = path.join(process.cwd(), "dump");
3488
+ if (values.json) {
3489
+ const filePath = path.resolve(dumpDir, `${dumpName}.json`);
3490
+ await fs$1.mkdir(dumpDir, { recursive: true });
3491
+ await fs$1.writeFile(filePath, JSON.stringify(rows, null, 2), "utf-8");
3492
+ console.log(`Saved: ${filePath}`);
3493
+ process.exit(0);
3494
+ }
3495
+ if (values.jsonl) {
3496
+ const filePath = path.resolve(dumpDir, `${dumpName}.jsonl`);
3497
+ await fs$1.mkdir(dumpDir, { recursive: true });
3498
+ await fs$1.writeFile(filePath, rows.map((r) => JSON.stringify(r)).join("\n"), "utf-8");
3499
+ console.log(`Saved: ${filePath}`);
3500
+ process.exit(0);
3501
+ }
3502
+ if (values.markdown) {
3503
+ const header = `| min | timestamp | close | pnl% | peak% | drawdown% |\n| --- | --- | --- | --- | --- | --- |`;
3504
+ const mdRows = rows.map((r) => `| ${r.min} | ${new Date(r.timestamp).toISOString()} | ${r.close.toFixed(2)} | ${(r.pnl >= 0 ? "+" : "") + r.pnl.toFixed(2)}% | +${r.peak.toFixed(2)}% | ${r.drawdown.toFixed(2)}% |`);
3505
+ const filePath = path.resolve(dumpDir, `${dumpName}.md`);
3506
+ await fs$1.mkdir(dumpDir, { recursive: true });
3507
+ await fs$1.writeFile(filePath, [header, ...mdRows].join("\n"), "utf-8");
3508
+ console.log(`Saved: ${filePath}`);
3509
+ process.exit(0);
3510
+ }
3511
+ console.log(`Symbol: ${symbol} | Direction: ${direction} | PriceOpen: ${priceOpen} | From: ${new Date(timestamp).toISOString()} | Minutes: ${minutes}\n`);
3512
+ console.log(`${"min".padStart(5)} | ${"timestamp".padEnd(24)} | ${"close".padStart(12)} | ${"pnl%".padStart(8)} | ${"peak%".padStart(8)} | ${"drawdown%".padStart(10)}`);
3513
+ console.log("-".repeat(83));
3514
+ for (const r of rows) {
3515
+ const min = String(r.min).padStart(5);
3516
+ const ts = new Date(r.timestamp).toISOString().padEnd(24);
3517
+ const close = r.close.toFixed(2).padStart(12);
3518
+ const pnlStr = (r.pnl >= 0 ? "+" : "") + r.pnl.toFixed(2) + "%";
3519
+ const peakStr = "+" + r.peak.toFixed(2) + "%";
3520
+ const drawdownStr = r.drawdown.toFixed(2) + "%";
3521
+ console.log(`${min} | ${ts} | ${close} | ${pnlStr.padStart(8)} | ${peakStr.padStart(8)} | ${drawdownStr.padStart(10)}`);
3522
+ }
3523
+ process.exit(0);
3524
+ };
3274
3525
  main$3();
3275
3526
 
3276
3527
  const __filename$1 = url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
@@ -3389,6 +3640,7 @@ Modes:
3389
3640
  --pine <entry> Execute a local .pine indicator file
3390
3641
  --editor Open the Pine Script visual editor in the browser
3391
3642
  --dump Fetch and save raw OHLCV candles
3643
+ --pnldebug Simulate PnL per minute for a given entry price and direction
3392
3644
  --flush <entry...> Delete report/log/markdown/agent folders from strategy dump dir
3393
3645
  --init Scaffold a new project in the current directory
3394
3646
  --help Print this help message
@@ -3461,6 +3713,21 @@ Candle dump flags (--dump):
3461
3713
 
3462
3714
  Module file ./modules/dump.module is loaded automatically if it exists.
3463
3715
 
3716
+ PnL debug flags (--pnldebug):
3717
+
3718
+ --symbol <string> Trading pair (default: BTCUSDT)
3719
+ --priceopen <number> Entry price (required)
3720
+ --direction <string> Position direction: long or short (default: long)
3721
+ --when <string> Start timestamp — ISO 8601 or Unix ms (default: now)
3722
+ --minutes <string> Number of 1m candles to simulate (default: 60)
3723
+ --exchange <string> Exchange name (default: first registered)
3724
+ --output <string> Output file base name (default: {SYMBOL}_{DIRECTION}_{PRICEOPEN}_{TIMESTAMP})
3725
+ --json Save as JSON array to ./dump/<output>.json
3726
+ --jsonl Save as JSONL to ./dump/<output>.jsonl
3727
+ --markdown Save as Markdown table to ./dump/<output>.md
3728
+
3729
+ Module file ./modules/pnldebug.module is loaded automatically if it exists.
3730
+
3464
3731
  Flush flags (--flush):
3465
3732
 
3466
3733
  One or more positional entry points. For each entry point the following
@@ -3483,6 +3750,7 @@ Module hooks (loaded automatically by each mode):
3483
3750
  modules/pine.module --pine Exchange schema for PineScript runs
3484
3751
  modules/editor.module --editor Exchange schema for the visual Pine editor
3485
3752
  modules/dump.module --dump Exchange schema for candle dumps
3753
+ modules/pnldebug.module --pnldebug Exchange schema for PnL debug runs
3486
3754
 
3487
3755
  --flush has no associated module. It only removes dump subdirectories.
3488
3756
 
@@ -3506,6 +3774,8 @@ Examples:
3506
3774
  node ${ENTRY_PATH} --pine ./math/feb_2026.pine --timeframe 15m --limit 500 --jsonl
3507
3775
  node ${ENTRY_PATH} --editor
3508
3776
  node ${ENTRY_PATH} --dump --symbol BTCUSDT --timeframe 15m --limit 500 --jsonl
3777
+ node ${ENTRY_PATH} --pnldebug --symbol BTCUSDT --priceopen 64069.50 --direction short --when "2025-02-25" --minutes 120
3778
+ node ${ENTRY_PATH} --pnldebug --priceopen 67956.73 --direction long --when 1772064000000 --minutes 60 --markdown
3509
3779
  node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts
3510
3780
  node ${ENTRY_PATH} --flush ./content/feb_2026.strategy/feb_2026.strategy.ts ./content/feb_2026.strategy/feb_2026.test.ts
3511
3781
  node ${ENTRY_PATH} --init --output my-trading-bot
@@ -3518,7 +3788,7 @@ const main$1 = async () => {
3518
3788
  if (!values.help) {
3519
3789
  return;
3520
3790
  }
3521
- process.stdout.write(`@backtest-kit/cli ${"7.3.1"}\n\n`);
3791
+ process.stdout.write(`@backtest-kit/cli ${"7.5.0"}\n\n`);
3522
3792
  process.stdout.write(HELP_TEXT);
3523
3793
  process.exit(0);
3524
3794
  };
@@ -3532,7 +3802,7 @@ const main = async () => {
3532
3802
  if (!values.version) {
3533
3803
  return;
3534
3804
  }
3535
- process.stdout.write(`@backtest-kit/cli ${"7.3.1"}\n`);
3805
+ process.stdout.write(`@backtest-kit/cli ${"7.5.0"}\n`);
3536
3806
  process.exit(0);
3537
3807
  };
3538
3808
  main();