backtest-kit 1.4.14 → 1.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
@@ -1700,6 +1700,53 @@ var emitters = /*#__PURE__*/Object.freeze({
1700
1700
  walkerStopSubject: walkerStopSubject
1701
1701
  });
1702
1702
 
1703
+ /**
1704
+ * Converts markdown content to plain text with minimal formatting
1705
+ * @param content - Markdown string to convert
1706
+ * @returns Plain text representation
1707
+ */
1708
+ const toPlainString = (content) => {
1709
+ if (!content) {
1710
+ return "";
1711
+ }
1712
+ let text = content;
1713
+ // Remove code blocks
1714
+ text = text.replace(/```[\s\S]*?```/g, "");
1715
+ text = text.replace(/`([^`]+)`/g, "$1");
1716
+ // Remove images
1717
+ text = text.replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1");
1718
+ // Convert links to text only (keep link text, remove URL)
1719
+ text = text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
1720
+ // Remove headers (convert to plain text)
1721
+ text = text.replace(/^#{1,6}\s+(.+)$/gm, "$1");
1722
+ // Remove bold and italic markers
1723
+ text = text.replace(/\*\*\*(.+?)\*\*\*/g, "$1");
1724
+ text = text.replace(/\*\*(.+?)\*\*/g, "$1");
1725
+ text = text.replace(/\*(.+?)\*/g, "$1");
1726
+ text = text.replace(/___(.+?)___/g, "$1");
1727
+ text = text.replace(/__(.+?)__/g, "$1");
1728
+ text = text.replace(/_(.+?)_/g, "$1");
1729
+ // Remove strikethrough
1730
+ text = text.replace(/~~(.+?)~~/g, "$1");
1731
+ // Convert lists to plain text with bullets
1732
+ text = text.replace(/^\s*[-*+]\s+/gm, "• ");
1733
+ text = text.replace(/^\s*\d+\.\s+/gm, "• ");
1734
+ // Remove blockquotes
1735
+ text = text.replace(/^\s*>\s+/gm, "");
1736
+ // Remove horizontal rules
1737
+ text = text.replace(/^(\*{3,}|-{3,}|_{3,})$/gm, "");
1738
+ // Remove HTML tags
1739
+ text = text.replace(/<[^>]+>/g, "");
1740
+ // Remove excessive whitespace and normalize line breaks
1741
+ text = text.replace(/\n[\s\n]*\n/g, "\n");
1742
+ text = text.replace(/[ \t]+/g, " ");
1743
+ // Remove all newline characters
1744
+ text = text.replace(/\n/g, " ");
1745
+ // Remove excessive spaces after newline removal
1746
+ text = text.replace(/\s+/g, " ");
1747
+ return text.trim();
1748
+ };
1749
+
1703
1750
  const INTERVAL_MINUTES$1 = {
1704
1751
  "1m": 1,
1705
1752
  "3m": 3,
@@ -1912,7 +1959,7 @@ const GET_SIGNAL_FN = functoolsKit.trycatch(async (self) => {
1912
1959
  id: functoolsKit.randomString(),
1913
1960
  priceOpen: signal.priceOpen, // Используем priceOpen из сигнала
1914
1961
  position: signal.position,
1915
- note: signal.note,
1962
+ note: toPlainString(signal.note),
1916
1963
  priceTakeProfit: signal.priceTakeProfit,
1917
1964
  priceStopLoss: signal.priceStopLoss,
1918
1965
  minuteEstimatedTime: signal.minuteEstimatedTime,
@@ -1932,7 +1979,7 @@ const GET_SIGNAL_FN = functoolsKit.trycatch(async (self) => {
1932
1979
  id: functoolsKit.randomString(),
1933
1980
  priceOpen: signal.priceOpen,
1934
1981
  position: signal.position,
1935
- note: signal.note,
1982
+ note: toPlainString(signal.note),
1936
1983
  priceTakeProfit: signal.priceTakeProfit,
1937
1984
  priceStopLoss: signal.priceStopLoss,
1938
1985
  minuteEstimatedTime: signal.minuteEstimatedTime,
@@ -1951,6 +1998,7 @@ const GET_SIGNAL_FN = functoolsKit.trycatch(async (self) => {
1951
1998
  id: functoolsKit.randomString(),
1952
1999
  priceOpen: currentPrice,
1953
2000
  ...signal,
2001
+ note: toPlainString(signal.note),
1954
2002
  symbol: self.params.execution.context.symbol,
1955
2003
  exchangeName: self.params.method.context.exchangeName,
1956
2004
  strategyName: self.params.method.context.strategyName,
@@ -2279,7 +2327,7 @@ const CLOSE_PENDING_SIGNAL_FN = async (self, signal, currentPrice, closeReason)
2279
2327
  self.params.callbacks.onClose(self.params.execution.context.symbol, signal, currentPrice, self.params.execution.context.backtest);
2280
2328
  }
2281
2329
  // КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
2282
- await self.params.partial.clear(self.params.execution.context.symbol, signal, currentPrice);
2330
+ await self.params.partial.clear(self.params.execution.context.symbol, signal, currentPrice, self.params.execution.context.backtest);
2283
2331
  await self.params.risk.removeSignal(self.params.execution.context.symbol, {
2284
2332
  strategyName: self.params.method.context.strategyName,
2285
2333
  riskName: self.params.riskName,
@@ -2302,31 +2350,50 @@ const CLOSE_PENDING_SIGNAL_FN = async (self, signal, currentPrice, closeReason)
2302
2350
  return result;
2303
2351
  };
2304
2352
  const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
2305
- // Calculate revenue percentage for partial fill/loss callbacks
2353
+ // Calculate percentage of path to TP/SL for partial fill/loss callbacks
2306
2354
  {
2307
- let revenuePercent = 0;
2308
2355
  if (signal.position === "long") {
2309
- // For long: positive if current > open, negative if current < open
2310
- revenuePercent = ((currentPrice - signal.priceOpen) / signal.priceOpen) * 100;
2356
+ // For long: calculate progress towards TP or SL
2357
+ const currentDistance = currentPrice - signal.priceOpen;
2358
+ if (currentDistance > 0) {
2359
+ // Moving towards TP
2360
+ const tpDistance = signal.priceTakeProfit - signal.priceOpen;
2361
+ const progressPercent = (currentDistance / tpDistance) * 100;
2362
+ await self.params.partial.profit(self.params.execution.context.symbol, signal, currentPrice, Math.min(progressPercent, 100), self.params.execution.context.backtest, self.params.execution.context.when);
2363
+ if (self.params.callbacks?.onPartialProfit) {
2364
+ self.params.callbacks.onPartialProfit(self.params.execution.context.symbol, signal, currentPrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
2365
+ }
2366
+ }
2367
+ else if (currentDistance < 0) {
2368
+ // Moving towards SL
2369
+ const slDistance = signal.priceOpen - signal.priceStopLoss;
2370
+ const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
2371
+ await self.params.partial.loss(self.params.execution.context.symbol, signal, currentPrice, Math.min(progressPercent, 100), self.params.execution.context.backtest, self.params.execution.context.when);
2372
+ if (self.params.callbacks?.onPartialLoss) {
2373
+ self.params.callbacks.onPartialLoss(self.params.execution.context.symbol, signal, currentPrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
2374
+ }
2375
+ }
2311
2376
  }
2312
2377
  else if (signal.position === "short") {
2313
- // For short: positive if current < open, negative if current > open
2314
- revenuePercent = ((signal.priceOpen - currentPrice) / signal.priceOpen) * 100;
2315
- }
2316
- // Call onPartialProfit if revenue is positive (but not reached TP yet)
2317
- if (revenuePercent > 0) {
2318
- // КРИТИЧНО: Вызываем ClientPartial для отслеживания уровней
2319
- await self.params.partial.profit(self.params.execution.context.symbol, signal, currentPrice, revenuePercent, self.params.execution.context.backtest, self.params.execution.context.when);
2320
- if (self.params.callbacks?.onPartialProfit) {
2321
- self.params.callbacks.onPartialProfit(self.params.execution.context.symbol, signal, currentPrice, revenuePercent, self.params.execution.context.backtest);
2378
+ // For short: calculate progress towards TP or SL
2379
+ const currentDistance = signal.priceOpen - currentPrice;
2380
+ if (currentDistance > 0) {
2381
+ // Moving towards TP
2382
+ const tpDistance = signal.priceOpen - signal.priceTakeProfit;
2383
+ const progressPercent = (currentDistance / tpDistance) * 100;
2384
+ await self.params.partial.profit(self.params.execution.context.symbol, signal, currentPrice, Math.min(progressPercent, 100), self.params.execution.context.backtest, self.params.execution.context.when);
2385
+ if (self.params.callbacks?.onPartialProfit) {
2386
+ self.params.callbacks.onPartialProfit(self.params.execution.context.symbol, signal, currentPrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
2387
+ }
2322
2388
  }
2323
- }
2324
- // Call onPartialLoss if revenue is negative (but not hit SL yet)
2325
- if (revenuePercent < 0) {
2326
- // КРИТИЧНО: Вызываем ClientPartial для отслеживания уровней
2327
- await self.params.partial.loss(self.params.execution.context.symbol, signal, currentPrice, revenuePercent, self.params.execution.context.backtest, self.params.execution.context.when);
2328
- if (self.params.callbacks?.onPartialLoss) {
2329
- self.params.callbacks.onPartialLoss(self.params.execution.context.symbol, signal, currentPrice, revenuePercent, self.params.execution.context.backtest);
2389
+ if (currentDistance < 0) {
2390
+ // Moving towards SL
2391
+ const slDistance = signal.priceStopLoss - signal.priceOpen;
2392
+ const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
2393
+ await self.params.partial.loss(self.params.execution.context.symbol, signal, currentPrice, Math.min(progressPercent, 100), self.params.execution.context.backtest, self.params.execution.context.when);
2394
+ if (self.params.callbacks?.onPartialLoss) {
2395
+ self.params.callbacks.onPartialLoss(self.params.execution.context.symbol, signal, currentPrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
2396
+ }
2330
2397
  }
2331
2398
  }
2332
2399
  }
@@ -2458,7 +2525,7 @@ const CLOSE_PENDING_SIGNAL_IN_BACKTEST_FN = async (self, signal, averagePrice, c
2458
2525
  self.params.callbacks.onClose(self.params.execution.context.symbol, signal, averagePrice, self.params.execution.context.backtest);
2459
2526
  }
2460
2527
  // КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
2461
- await self.params.partial.clear(self.params.execution.context.symbol, signal, averagePrice);
2528
+ await self.params.partial.clear(self.params.execution.context.symbol, signal, averagePrice, self.params.execution.context.backtest);
2462
2529
  await self.params.risk.removeSignal(self.params.execution.context.symbol, {
2463
2530
  strategyName: self.params.method.context.strategyName,
2464
2531
  riskName: self.params.riskName,
@@ -2601,29 +2668,50 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
2601
2668
  return await CLOSE_PENDING_SIGNAL_IN_BACKTEST_FN(self, signal, closePrice, closeReason, currentCandleTimestamp);
2602
2669
  }
2603
2670
  // Call onPartialProfit/onPartialLoss callbacks during backtest candle processing
2604
- // Calculate revenue percentage
2671
+ // Calculate percentage of path to TP/SL
2605
2672
  {
2606
- let revenuePercent = 0;
2607
2673
  if (signal.position === "long") {
2608
- revenuePercent = ((averagePrice - signal.priceOpen) / signal.priceOpen) * 100;
2674
+ // For long: calculate progress towards TP or SL
2675
+ const currentDistance = averagePrice - signal.priceOpen;
2676
+ if (currentDistance > 0) {
2677
+ // Moving towards TP
2678
+ const tpDistance = signal.priceTakeProfit - signal.priceOpen;
2679
+ const progressPercent = (currentDistance / tpDistance) * 100;
2680
+ await self.params.partial.profit(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest, self.params.execution.context.when);
2681
+ if (self.params.callbacks?.onPartialProfit) {
2682
+ self.params.callbacks.onPartialProfit(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
2683
+ }
2684
+ }
2685
+ else if (currentDistance < 0) {
2686
+ // Moving towards SL
2687
+ const slDistance = signal.priceOpen - signal.priceStopLoss;
2688
+ const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
2689
+ await self.params.partial.loss(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest, self.params.execution.context.when);
2690
+ if (self.params.callbacks?.onPartialLoss) {
2691
+ self.params.callbacks.onPartialLoss(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
2692
+ }
2693
+ }
2609
2694
  }
2610
2695
  else if (signal.position === "short") {
2611
- revenuePercent = ((signal.priceOpen - averagePrice) / signal.priceOpen) * 100;
2612
- }
2613
- // Call onPartialProfit if revenue is positive (but not reached TP yet)
2614
- if (revenuePercent > 0) {
2615
- // КРИТИЧНО: Вызываем ClientPartial для отслеживания уровней
2616
- await self.params.partial.profit(self.params.execution.context.symbol, signal, averagePrice, revenuePercent, self.params.execution.context.backtest, self.params.execution.context.when);
2617
- if (self.params.callbacks?.onPartialProfit) {
2618
- self.params.callbacks.onPartialProfit(self.params.execution.context.symbol, signal, averagePrice, revenuePercent, self.params.execution.context.backtest);
2696
+ // For short: calculate progress towards TP or SL
2697
+ const currentDistance = signal.priceOpen - averagePrice;
2698
+ if (currentDistance > 0) {
2699
+ // Moving towards TP
2700
+ const tpDistance = signal.priceOpen - signal.priceTakeProfit;
2701
+ const progressPercent = (currentDistance / tpDistance) * 100;
2702
+ await self.params.partial.profit(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest, self.params.execution.context.when);
2703
+ if (self.params.callbacks?.onPartialProfit) {
2704
+ self.params.callbacks.onPartialProfit(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
2705
+ }
2619
2706
  }
2620
- }
2621
- // Call onPartialLoss if revenue is negative (but not hit SL yet)
2622
- if (revenuePercent < 0) {
2623
- // КРИТИЧНО: Вызываем ClientPartial для отслеживания уровней
2624
- await self.params.partial.loss(self.params.execution.context.symbol, signal, averagePrice, revenuePercent, self.params.execution.context.backtest, self.params.execution.context.when);
2625
- if (self.params.callbacks?.onPartialLoss) {
2626
- self.params.callbacks.onPartialLoss(self.params.execution.context.symbol, signal, averagePrice, revenuePercent, self.params.execution.context.backtest);
2707
+ if (currentDistance < 0) {
2708
+ // Moving towards SL
2709
+ const slDistance = signal.priceStopLoss - signal.priceOpen;
2710
+ const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
2711
+ await self.params.partial.loss(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest, self.params.execution.context.when);
2712
+ if (self.params.callbacks?.onPartialLoss) {
2713
+ self.params.callbacks.onPartialLoss(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
2714
+ }
2627
2715
  }
2628
2716
  }
2629
2717
  }
@@ -5613,7 +5701,7 @@ const columns$4 = [
5613
5701
  {
5614
5702
  key: "note",
5615
5703
  label: "Note",
5616
- format: (data) => data.signal.note ?? "N/A",
5704
+ format: (data) => toPlainString(data.signal.note ?? "N/A"),
5617
5705
  },
5618
5706
  {
5619
5707
  key: "openPrice",
@@ -6025,7 +6113,7 @@ const columns$3 = [
6025
6113
  {
6026
6114
  key: "note",
6027
6115
  label: "Note",
6028
- format: (data) => data.note ?? "N/A",
6116
+ format: (data) => toPlainString(data.note ?? "N/A"),
6029
6117
  },
6030
6118
  {
6031
6119
  key: "currentPrice",
@@ -6552,7 +6640,7 @@ const columns$2 = [
6552
6640
  {
6553
6641
  key: "note",
6554
6642
  label: "Note",
6555
- format: (data) => data.note ?? "N/A",
6643
+ format: (data) => toPlainString(data.note ?? "N/A"),
6556
6644
  },
6557
6645
  {
6558
6646
  key: "currentPrice",
@@ -7111,7 +7199,7 @@ class PerformanceStorage {
7111
7199
  * console.log("Bottlenecks:", stats.metricStats);
7112
7200
  *
7113
7201
  * // Save report to disk
7114
- * await Performance.dump("my-strategy");
7202
+ * await Performance.dump("BTCUSDT", "my-strategy");
7115
7203
  * ```
7116
7204
  */
7117
7205
  class PerformanceMarkdownService {
@@ -8764,6 +8852,8 @@ class OptimizerTemplateService {
8764
8852
  interval,
8765
8853
  prompt,
8766
8854
  });
8855
+ // Convert prompt to plain text first
8856
+ const plainPrompt = toPlainString(prompt);
8767
8857
  // Escape special characters to prevent code injection
8768
8858
  const escapedStrategyName = String(strategyName)
8769
8859
  .replace(/\\/g, '\\\\')
@@ -8771,7 +8861,7 @@ class OptimizerTemplateService {
8771
8861
  const escapedInterval = String(interval)
8772
8862
  .replace(/\\/g, '\\\\')
8773
8863
  .replace(/"/g, '\\"');
8774
- const escapedPrompt = String(prompt)
8864
+ const escapedPrompt = String(plainPrompt)
8775
8865
  .replace(/\\/g, '\\\\')
8776
8866
  .replace(/`/g, '\\`')
8777
8867
  .replace(/\$/g, '\\$');
@@ -8997,7 +9087,7 @@ class OptimizerTemplateService {
8997
9087
  ``,
8998
9088
  `listenDoneBacktest((event) => {`,
8999
9089
  ` console.log("Backtest completed:", event.symbol);`,
9000
- ` Backtest.dump(event.strategyName);`,
9090
+ ` Backtest.dump(event.symbol, event.strategyName);`,
9001
9091
  `});`,
9002
9092
  ``,
9003
9093
  `listenError((error) => {`,
@@ -9952,7 +10042,7 @@ const HANDLE_PROFIT_FN = async (symbol, data, currentPrice, revenuePercent, back
9952
10042
  }
9953
10043
  }
9954
10044
  if (shouldPersist) {
9955
- await self._persistState(symbol);
10045
+ await self._persistState(symbol, backtest);
9956
10046
  }
9957
10047
  };
9958
10048
  /**
@@ -9999,7 +10089,7 @@ const HANDLE_LOSS_FN = async (symbol, data, currentPrice, lossPercent, backtest,
9999
10089
  }
10000
10090
  }
10001
10091
  if (shouldPersist) {
10002
- await self._persistState(symbol);
10092
+ await self._persistState(symbol, backtest);
10003
10093
  }
10004
10094
  };
10005
10095
  /**
@@ -10135,7 +10225,10 @@ class ClientPartial {
10135
10225
  * @param symbol - Trading pair symbol
10136
10226
  * @returns Promise that resolves when persistence is complete
10137
10227
  */
10138
- async _persistState(symbol) {
10228
+ async _persistState(symbol, backtest) {
10229
+ if (backtest) {
10230
+ return;
10231
+ }
10139
10232
  this.params.logger.debug("ClientPartial persistState", { symbol });
10140
10233
  if (this._states === NEED_FETCH) {
10141
10234
  throw new Error("ClientPartial not initialized. Call waitForInit() before using.");
@@ -10258,7 +10351,7 @@ class ClientPartial {
10258
10351
  * // Cleanup: PartialConnectionService.getPartial.clear(signal.id)
10259
10352
  * ```
10260
10353
  */
10261
- async clear(symbol, data, priceClose) {
10354
+ async clear(symbol, data, priceClose, backtest) {
10262
10355
  this.params.logger.log("ClientPartial clear", {
10263
10356
  symbol,
10264
10357
  data,
@@ -10268,7 +10361,7 @@ class ClientPartial {
10268
10361
  throw new Error("ClientPartial not initialized. Call waitForInit() before using.");
10269
10362
  }
10270
10363
  this._states.delete(data.id);
10271
- await this._persistState(symbol);
10364
+ await this._persistState(symbol, backtest);
10272
10365
  }
10273
10366
  }
10274
10367
 
@@ -10439,7 +10532,7 @@ class PartialConnectionService {
10439
10532
  * @param priceClose - Final closing price
10440
10533
  * @returns Promise that resolves when clear is complete
10441
10534
  */
10442
- this.clear = async (symbol, data, priceClose) => {
10535
+ this.clear = async (symbol, data, priceClose, backtest) => {
10443
10536
  this.loggerService.log("partialConnectionService profit", {
10444
10537
  symbol,
10445
10538
  data,
@@ -10447,7 +10540,7 @@ class PartialConnectionService {
10447
10540
  });
10448
10541
  const partial = this.getPartial(data.id);
10449
10542
  await partial.waitForInit(symbol);
10450
- await partial.clear(symbol, data, priceClose);
10543
+ await partial.clear(symbol, data, priceClose, backtest);
10451
10544
  this.getPartial.clear(data.id);
10452
10545
  };
10453
10546
  }
@@ -10654,7 +10747,7 @@ class PartialMarkdownService {
10654
10747
  * Memoized function to get or create ReportStorage for a symbol-strategy pair.
10655
10748
  * Each symbol-strategy combination gets its own isolated storage instance.
10656
10749
  */
10657
- this.getStorage = functoolsKit.memoize(([symbol, strategyName]) => JSON.stringify([symbol, strategyName]), () => new ReportStorage());
10750
+ this.getStorage = functoolsKit.memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, () => new ReportStorage());
10658
10751
  /**
10659
10752
  * Processes profit events and accumulates them.
10660
10753
  * Should be called from partialProfitSubject subscription.
@@ -10791,7 +10884,7 @@ class PartialMarkdownService {
10791
10884
  ctx,
10792
10885
  });
10793
10886
  if (ctx) {
10794
- const key = JSON.stringify([ctx.symbol, ctx.strategyName]);
10887
+ const key = `${ctx.symbol}:${ctx.strategyName}`;
10795
10888
  this.getStorage.clear(key);
10796
10889
  }
10797
10890
  else {
@@ -10916,13 +11009,14 @@ class PartialGlobalService {
10916
11009
  * @param priceClose - Final closing price
10917
11010
  * @returns Promise that resolves when clear is complete
10918
11011
  */
10919
- this.clear = async (symbol, data, priceClose) => {
11012
+ this.clear = async (symbol, data, priceClose, backtest) => {
10920
11013
  this.loggerService.log("partialGlobalService profit", {
10921
11014
  symbol,
10922
11015
  data,
10923
11016
  priceClose,
11017
+ backtest,
10924
11018
  });
10925
- return await this.partialConnectionService.clear(symbol, data, priceClose);
11019
+ return await this.partialConnectionService.clear(symbol, data, priceClose, backtest);
10926
11020
  };
10927
11021
  }
10928
11022
  }
@@ -13378,7 +13472,7 @@ const SCHEDULE_METHOD_NAME_DUMP = "ScheduleUtils.dump";
13378
13472
  * console.log(`Average wait time: ${stats.avgWaitTime} minutes`);
13379
13473
  *
13380
13474
  * // Generate and save report
13381
- * await Schedule.dump("my-strategy");
13475
+ * await Schedule.dump("BTCUSDT", "my-strategy");
13382
13476
  * ```
13383
13477
  */
13384
13478
  class ScheduleUtils {
@@ -13490,7 +13584,7 @@ const Schedule = new ScheduleUtils();
13490
13584
  * .slice(0, 5));
13491
13585
  *
13492
13586
  * // Generate and save report
13493
- * await Performance.dump("my-strategy");
13587
+ * await Performance.dump("BTCUSDT", "my-strategy");
13494
13588
  * ```
13495
13589
  */
13496
13590
  class Performance {
package/build/index.mjs CHANGED
@@ -1698,6 +1698,53 @@ var emitters = /*#__PURE__*/Object.freeze({
1698
1698
  walkerStopSubject: walkerStopSubject
1699
1699
  });
1700
1700
 
1701
+ /**
1702
+ * Converts markdown content to plain text with minimal formatting
1703
+ * @param content - Markdown string to convert
1704
+ * @returns Plain text representation
1705
+ */
1706
+ const toPlainString = (content) => {
1707
+ if (!content) {
1708
+ return "";
1709
+ }
1710
+ let text = content;
1711
+ // Remove code blocks
1712
+ text = text.replace(/```[\s\S]*?```/g, "");
1713
+ text = text.replace(/`([^`]+)`/g, "$1");
1714
+ // Remove images
1715
+ text = text.replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1");
1716
+ // Convert links to text only (keep link text, remove URL)
1717
+ text = text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
1718
+ // Remove headers (convert to plain text)
1719
+ text = text.replace(/^#{1,6}\s+(.+)$/gm, "$1");
1720
+ // Remove bold and italic markers
1721
+ text = text.replace(/\*\*\*(.+?)\*\*\*/g, "$1");
1722
+ text = text.replace(/\*\*(.+?)\*\*/g, "$1");
1723
+ text = text.replace(/\*(.+?)\*/g, "$1");
1724
+ text = text.replace(/___(.+?)___/g, "$1");
1725
+ text = text.replace(/__(.+?)__/g, "$1");
1726
+ text = text.replace(/_(.+?)_/g, "$1");
1727
+ // Remove strikethrough
1728
+ text = text.replace(/~~(.+?)~~/g, "$1");
1729
+ // Convert lists to plain text with bullets
1730
+ text = text.replace(/^\s*[-*+]\s+/gm, "• ");
1731
+ text = text.replace(/^\s*\d+\.\s+/gm, "• ");
1732
+ // Remove blockquotes
1733
+ text = text.replace(/^\s*>\s+/gm, "");
1734
+ // Remove horizontal rules
1735
+ text = text.replace(/^(\*{3,}|-{3,}|_{3,})$/gm, "");
1736
+ // Remove HTML tags
1737
+ text = text.replace(/<[^>]+>/g, "");
1738
+ // Remove excessive whitespace and normalize line breaks
1739
+ text = text.replace(/\n[\s\n]*\n/g, "\n");
1740
+ text = text.replace(/[ \t]+/g, " ");
1741
+ // Remove all newline characters
1742
+ text = text.replace(/\n/g, " ");
1743
+ // Remove excessive spaces after newline removal
1744
+ text = text.replace(/\s+/g, " ");
1745
+ return text.trim();
1746
+ };
1747
+
1701
1748
  const INTERVAL_MINUTES$1 = {
1702
1749
  "1m": 1,
1703
1750
  "3m": 3,
@@ -1910,7 +1957,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
1910
1957
  id: randomString(),
1911
1958
  priceOpen: signal.priceOpen, // Используем priceOpen из сигнала
1912
1959
  position: signal.position,
1913
- note: signal.note,
1960
+ note: toPlainString(signal.note),
1914
1961
  priceTakeProfit: signal.priceTakeProfit,
1915
1962
  priceStopLoss: signal.priceStopLoss,
1916
1963
  minuteEstimatedTime: signal.minuteEstimatedTime,
@@ -1930,7 +1977,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
1930
1977
  id: randomString(),
1931
1978
  priceOpen: signal.priceOpen,
1932
1979
  position: signal.position,
1933
- note: signal.note,
1980
+ note: toPlainString(signal.note),
1934
1981
  priceTakeProfit: signal.priceTakeProfit,
1935
1982
  priceStopLoss: signal.priceStopLoss,
1936
1983
  minuteEstimatedTime: signal.minuteEstimatedTime,
@@ -1949,6 +1996,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
1949
1996
  id: randomString(),
1950
1997
  priceOpen: currentPrice,
1951
1998
  ...signal,
1999
+ note: toPlainString(signal.note),
1952
2000
  symbol: self.params.execution.context.symbol,
1953
2001
  exchangeName: self.params.method.context.exchangeName,
1954
2002
  strategyName: self.params.method.context.strategyName,
@@ -2277,7 +2325,7 @@ const CLOSE_PENDING_SIGNAL_FN = async (self, signal, currentPrice, closeReason)
2277
2325
  self.params.callbacks.onClose(self.params.execution.context.symbol, signal, currentPrice, self.params.execution.context.backtest);
2278
2326
  }
2279
2327
  // КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
2280
- await self.params.partial.clear(self.params.execution.context.symbol, signal, currentPrice);
2328
+ await self.params.partial.clear(self.params.execution.context.symbol, signal, currentPrice, self.params.execution.context.backtest);
2281
2329
  await self.params.risk.removeSignal(self.params.execution.context.symbol, {
2282
2330
  strategyName: self.params.method.context.strategyName,
2283
2331
  riskName: self.params.riskName,
@@ -2300,31 +2348,50 @@ const CLOSE_PENDING_SIGNAL_FN = async (self, signal, currentPrice, closeReason)
2300
2348
  return result;
2301
2349
  };
2302
2350
  const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
2303
- // Calculate revenue percentage for partial fill/loss callbacks
2351
+ // Calculate percentage of path to TP/SL for partial fill/loss callbacks
2304
2352
  {
2305
- let revenuePercent = 0;
2306
2353
  if (signal.position === "long") {
2307
- // For long: positive if current > open, negative if current < open
2308
- revenuePercent = ((currentPrice - signal.priceOpen) / signal.priceOpen) * 100;
2354
+ // For long: calculate progress towards TP or SL
2355
+ const currentDistance = currentPrice - signal.priceOpen;
2356
+ if (currentDistance > 0) {
2357
+ // Moving towards TP
2358
+ const tpDistance = signal.priceTakeProfit - signal.priceOpen;
2359
+ const progressPercent = (currentDistance / tpDistance) * 100;
2360
+ await self.params.partial.profit(self.params.execution.context.symbol, signal, currentPrice, Math.min(progressPercent, 100), self.params.execution.context.backtest, self.params.execution.context.when);
2361
+ if (self.params.callbacks?.onPartialProfit) {
2362
+ self.params.callbacks.onPartialProfit(self.params.execution.context.symbol, signal, currentPrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
2363
+ }
2364
+ }
2365
+ else if (currentDistance < 0) {
2366
+ // Moving towards SL
2367
+ const slDistance = signal.priceOpen - signal.priceStopLoss;
2368
+ const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
2369
+ await self.params.partial.loss(self.params.execution.context.symbol, signal, currentPrice, Math.min(progressPercent, 100), self.params.execution.context.backtest, self.params.execution.context.when);
2370
+ if (self.params.callbacks?.onPartialLoss) {
2371
+ self.params.callbacks.onPartialLoss(self.params.execution.context.symbol, signal, currentPrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
2372
+ }
2373
+ }
2309
2374
  }
2310
2375
  else if (signal.position === "short") {
2311
- // For short: positive if current < open, negative if current > open
2312
- revenuePercent = ((signal.priceOpen - currentPrice) / signal.priceOpen) * 100;
2313
- }
2314
- // Call onPartialProfit if revenue is positive (but not reached TP yet)
2315
- if (revenuePercent > 0) {
2316
- // КРИТИЧНО: Вызываем ClientPartial для отслеживания уровней
2317
- await self.params.partial.profit(self.params.execution.context.symbol, signal, currentPrice, revenuePercent, self.params.execution.context.backtest, self.params.execution.context.when);
2318
- if (self.params.callbacks?.onPartialProfit) {
2319
- self.params.callbacks.onPartialProfit(self.params.execution.context.symbol, signal, currentPrice, revenuePercent, self.params.execution.context.backtest);
2376
+ // For short: calculate progress towards TP or SL
2377
+ const currentDistance = signal.priceOpen - currentPrice;
2378
+ if (currentDistance > 0) {
2379
+ // Moving towards TP
2380
+ const tpDistance = signal.priceOpen - signal.priceTakeProfit;
2381
+ const progressPercent = (currentDistance / tpDistance) * 100;
2382
+ await self.params.partial.profit(self.params.execution.context.symbol, signal, currentPrice, Math.min(progressPercent, 100), self.params.execution.context.backtest, self.params.execution.context.when);
2383
+ if (self.params.callbacks?.onPartialProfit) {
2384
+ self.params.callbacks.onPartialProfit(self.params.execution.context.symbol, signal, currentPrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
2385
+ }
2320
2386
  }
2321
- }
2322
- // Call onPartialLoss if revenue is negative (but not hit SL yet)
2323
- if (revenuePercent < 0) {
2324
- // КРИТИЧНО: Вызываем ClientPartial для отслеживания уровней
2325
- await self.params.partial.loss(self.params.execution.context.symbol, signal, currentPrice, revenuePercent, self.params.execution.context.backtest, self.params.execution.context.when);
2326
- if (self.params.callbacks?.onPartialLoss) {
2327
- self.params.callbacks.onPartialLoss(self.params.execution.context.symbol, signal, currentPrice, revenuePercent, self.params.execution.context.backtest);
2387
+ if (currentDistance < 0) {
2388
+ // Moving towards SL
2389
+ const slDistance = signal.priceStopLoss - signal.priceOpen;
2390
+ const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
2391
+ await self.params.partial.loss(self.params.execution.context.symbol, signal, currentPrice, Math.min(progressPercent, 100), self.params.execution.context.backtest, self.params.execution.context.when);
2392
+ if (self.params.callbacks?.onPartialLoss) {
2393
+ self.params.callbacks.onPartialLoss(self.params.execution.context.symbol, signal, currentPrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
2394
+ }
2328
2395
  }
2329
2396
  }
2330
2397
  }
@@ -2456,7 +2523,7 @@ const CLOSE_PENDING_SIGNAL_IN_BACKTEST_FN = async (self, signal, averagePrice, c
2456
2523
  self.params.callbacks.onClose(self.params.execution.context.symbol, signal, averagePrice, self.params.execution.context.backtest);
2457
2524
  }
2458
2525
  // КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
2459
- await self.params.partial.clear(self.params.execution.context.symbol, signal, averagePrice);
2526
+ await self.params.partial.clear(self.params.execution.context.symbol, signal, averagePrice, self.params.execution.context.backtest);
2460
2527
  await self.params.risk.removeSignal(self.params.execution.context.symbol, {
2461
2528
  strategyName: self.params.method.context.strategyName,
2462
2529
  riskName: self.params.riskName,
@@ -2599,29 +2666,50 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
2599
2666
  return await CLOSE_PENDING_SIGNAL_IN_BACKTEST_FN(self, signal, closePrice, closeReason, currentCandleTimestamp);
2600
2667
  }
2601
2668
  // Call onPartialProfit/onPartialLoss callbacks during backtest candle processing
2602
- // Calculate revenue percentage
2669
+ // Calculate percentage of path to TP/SL
2603
2670
  {
2604
- let revenuePercent = 0;
2605
2671
  if (signal.position === "long") {
2606
- revenuePercent = ((averagePrice - signal.priceOpen) / signal.priceOpen) * 100;
2672
+ // For long: calculate progress towards TP or SL
2673
+ const currentDistance = averagePrice - signal.priceOpen;
2674
+ if (currentDistance > 0) {
2675
+ // Moving towards TP
2676
+ const tpDistance = signal.priceTakeProfit - signal.priceOpen;
2677
+ const progressPercent = (currentDistance / tpDistance) * 100;
2678
+ await self.params.partial.profit(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest, self.params.execution.context.when);
2679
+ if (self.params.callbacks?.onPartialProfit) {
2680
+ self.params.callbacks.onPartialProfit(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
2681
+ }
2682
+ }
2683
+ else if (currentDistance < 0) {
2684
+ // Moving towards SL
2685
+ const slDistance = signal.priceOpen - signal.priceStopLoss;
2686
+ const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
2687
+ await self.params.partial.loss(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest, self.params.execution.context.when);
2688
+ if (self.params.callbacks?.onPartialLoss) {
2689
+ self.params.callbacks.onPartialLoss(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
2690
+ }
2691
+ }
2607
2692
  }
2608
2693
  else if (signal.position === "short") {
2609
- revenuePercent = ((signal.priceOpen - averagePrice) / signal.priceOpen) * 100;
2610
- }
2611
- // Call onPartialProfit if revenue is positive (but not reached TP yet)
2612
- if (revenuePercent > 0) {
2613
- // КРИТИЧНО: Вызываем ClientPartial для отслеживания уровней
2614
- await self.params.partial.profit(self.params.execution.context.symbol, signal, averagePrice, revenuePercent, self.params.execution.context.backtest, self.params.execution.context.when);
2615
- if (self.params.callbacks?.onPartialProfit) {
2616
- self.params.callbacks.onPartialProfit(self.params.execution.context.symbol, signal, averagePrice, revenuePercent, self.params.execution.context.backtest);
2694
+ // For short: calculate progress towards TP or SL
2695
+ const currentDistance = signal.priceOpen - averagePrice;
2696
+ if (currentDistance > 0) {
2697
+ // Moving towards TP
2698
+ const tpDistance = signal.priceOpen - signal.priceTakeProfit;
2699
+ const progressPercent = (currentDistance / tpDistance) * 100;
2700
+ await self.params.partial.profit(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest, self.params.execution.context.when);
2701
+ if (self.params.callbacks?.onPartialProfit) {
2702
+ self.params.callbacks.onPartialProfit(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
2703
+ }
2617
2704
  }
2618
- }
2619
- // Call onPartialLoss if revenue is negative (but not hit SL yet)
2620
- if (revenuePercent < 0) {
2621
- // КРИТИЧНО: Вызываем ClientPartial для отслеживания уровней
2622
- await self.params.partial.loss(self.params.execution.context.symbol, signal, averagePrice, revenuePercent, self.params.execution.context.backtest, self.params.execution.context.when);
2623
- if (self.params.callbacks?.onPartialLoss) {
2624
- self.params.callbacks.onPartialLoss(self.params.execution.context.symbol, signal, averagePrice, revenuePercent, self.params.execution.context.backtest);
2705
+ if (currentDistance < 0) {
2706
+ // Moving towards SL
2707
+ const slDistance = signal.priceStopLoss - signal.priceOpen;
2708
+ const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
2709
+ await self.params.partial.loss(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest, self.params.execution.context.when);
2710
+ if (self.params.callbacks?.onPartialLoss) {
2711
+ self.params.callbacks.onPartialLoss(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
2712
+ }
2625
2713
  }
2626
2714
  }
2627
2715
  }
@@ -5611,7 +5699,7 @@ const columns$4 = [
5611
5699
  {
5612
5700
  key: "note",
5613
5701
  label: "Note",
5614
- format: (data) => data.signal.note ?? "N/A",
5702
+ format: (data) => toPlainString(data.signal.note ?? "N/A"),
5615
5703
  },
5616
5704
  {
5617
5705
  key: "openPrice",
@@ -6023,7 +6111,7 @@ const columns$3 = [
6023
6111
  {
6024
6112
  key: "note",
6025
6113
  label: "Note",
6026
- format: (data) => data.note ?? "N/A",
6114
+ format: (data) => toPlainString(data.note ?? "N/A"),
6027
6115
  },
6028
6116
  {
6029
6117
  key: "currentPrice",
@@ -6550,7 +6638,7 @@ const columns$2 = [
6550
6638
  {
6551
6639
  key: "note",
6552
6640
  label: "Note",
6553
- format: (data) => data.note ?? "N/A",
6641
+ format: (data) => toPlainString(data.note ?? "N/A"),
6554
6642
  },
6555
6643
  {
6556
6644
  key: "currentPrice",
@@ -7109,7 +7197,7 @@ class PerformanceStorage {
7109
7197
  * console.log("Bottlenecks:", stats.metricStats);
7110
7198
  *
7111
7199
  * // Save report to disk
7112
- * await Performance.dump("my-strategy");
7200
+ * await Performance.dump("BTCUSDT", "my-strategy");
7113
7201
  * ```
7114
7202
  */
7115
7203
  class PerformanceMarkdownService {
@@ -8762,6 +8850,8 @@ class OptimizerTemplateService {
8762
8850
  interval,
8763
8851
  prompt,
8764
8852
  });
8853
+ // Convert prompt to plain text first
8854
+ const plainPrompt = toPlainString(prompt);
8765
8855
  // Escape special characters to prevent code injection
8766
8856
  const escapedStrategyName = String(strategyName)
8767
8857
  .replace(/\\/g, '\\\\')
@@ -8769,7 +8859,7 @@ class OptimizerTemplateService {
8769
8859
  const escapedInterval = String(interval)
8770
8860
  .replace(/\\/g, '\\\\')
8771
8861
  .replace(/"/g, '\\"');
8772
- const escapedPrompt = String(prompt)
8862
+ const escapedPrompt = String(plainPrompt)
8773
8863
  .replace(/\\/g, '\\\\')
8774
8864
  .replace(/`/g, '\\`')
8775
8865
  .replace(/\$/g, '\\$');
@@ -8995,7 +9085,7 @@ class OptimizerTemplateService {
8995
9085
  ``,
8996
9086
  `listenDoneBacktest((event) => {`,
8997
9087
  ` console.log("Backtest completed:", event.symbol);`,
8998
- ` Backtest.dump(event.strategyName);`,
9088
+ ` Backtest.dump(event.symbol, event.strategyName);`,
8999
9089
  `});`,
9000
9090
  ``,
9001
9091
  `listenError((error) => {`,
@@ -9950,7 +10040,7 @@ const HANDLE_PROFIT_FN = async (symbol, data, currentPrice, revenuePercent, back
9950
10040
  }
9951
10041
  }
9952
10042
  if (shouldPersist) {
9953
- await self._persistState(symbol);
10043
+ await self._persistState(symbol, backtest);
9954
10044
  }
9955
10045
  };
9956
10046
  /**
@@ -9997,7 +10087,7 @@ const HANDLE_LOSS_FN = async (symbol, data, currentPrice, lossPercent, backtest,
9997
10087
  }
9998
10088
  }
9999
10089
  if (shouldPersist) {
10000
- await self._persistState(symbol);
10090
+ await self._persistState(symbol, backtest);
10001
10091
  }
10002
10092
  };
10003
10093
  /**
@@ -10133,7 +10223,10 @@ class ClientPartial {
10133
10223
  * @param symbol - Trading pair symbol
10134
10224
  * @returns Promise that resolves when persistence is complete
10135
10225
  */
10136
- async _persistState(symbol) {
10226
+ async _persistState(symbol, backtest) {
10227
+ if (backtest) {
10228
+ return;
10229
+ }
10137
10230
  this.params.logger.debug("ClientPartial persistState", { symbol });
10138
10231
  if (this._states === NEED_FETCH) {
10139
10232
  throw new Error("ClientPartial not initialized. Call waitForInit() before using.");
@@ -10256,7 +10349,7 @@ class ClientPartial {
10256
10349
  * // Cleanup: PartialConnectionService.getPartial.clear(signal.id)
10257
10350
  * ```
10258
10351
  */
10259
- async clear(symbol, data, priceClose) {
10352
+ async clear(symbol, data, priceClose, backtest) {
10260
10353
  this.params.logger.log("ClientPartial clear", {
10261
10354
  symbol,
10262
10355
  data,
@@ -10266,7 +10359,7 @@ class ClientPartial {
10266
10359
  throw new Error("ClientPartial not initialized. Call waitForInit() before using.");
10267
10360
  }
10268
10361
  this._states.delete(data.id);
10269
- await this._persistState(symbol);
10362
+ await this._persistState(symbol, backtest);
10270
10363
  }
10271
10364
  }
10272
10365
 
@@ -10437,7 +10530,7 @@ class PartialConnectionService {
10437
10530
  * @param priceClose - Final closing price
10438
10531
  * @returns Promise that resolves when clear is complete
10439
10532
  */
10440
- this.clear = async (symbol, data, priceClose) => {
10533
+ this.clear = async (symbol, data, priceClose, backtest) => {
10441
10534
  this.loggerService.log("partialConnectionService profit", {
10442
10535
  symbol,
10443
10536
  data,
@@ -10445,7 +10538,7 @@ class PartialConnectionService {
10445
10538
  });
10446
10539
  const partial = this.getPartial(data.id);
10447
10540
  await partial.waitForInit(symbol);
10448
- await partial.clear(symbol, data, priceClose);
10541
+ await partial.clear(symbol, data, priceClose, backtest);
10449
10542
  this.getPartial.clear(data.id);
10450
10543
  };
10451
10544
  }
@@ -10652,7 +10745,7 @@ class PartialMarkdownService {
10652
10745
  * Memoized function to get or create ReportStorage for a symbol-strategy pair.
10653
10746
  * Each symbol-strategy combination gets its own isolated storage instance.
10654
10747
  */
10655
- this.getStorage = memoize(([symbol, strategyName]) => JSON.stringify([symbol, strategyName]), () => new ReportStorage());
10748
+ this.getStorage = memoize(([symbol, strategyName]) => `${symbol}:${strategyName}`, () => new ReportStorage());
10656
10749
  /**
10657
10750
  * Processes profit events and accumulates them.
10658
10751
  * Should be called from partialProfitSubject subscription.
@@ -10789,7 +10882,7 @@ class PartialMarkdownService {
10789
10882
  ctx,
10790
10883
  });
10791
10884
  if (ctx) {
10792
- const key = JSON.stringify([ctx.symbol, ctx.strategyName]);
10885
+ const key = `${ctx.symbol}:${ctx.strategyName}`;
10793
10886
  this.getStorage.clear(key);
10794
10887
  }
10795
10888
  else {
@@ -10914,13 +11007,14 @@ class PartialGlobalService {
10914
11007
  * @param priceClose - Final closing price
10915
11008
  * @returns Promise that resolves when clear is complete
10916
11009
  */
10917
- this.clear = async (symbol, data, priceClose) => {
11010
+ this.clear = async (symbol, data, priceClose, backtest) => {
10918
11011
  this.loggerService.log("partialGlobalService profit", {
10919
11012
  symbol,
10920
11013
  data,
10921
11014
  priceClose,
11015
+ backtest,
10922
11016
  });
10923
- return await this.partialConnectionService.clear(symbol, data, priceClose);
11017
+ return await this.partialConnectionService.clear(symbol, data, priceClose, backtest);
10924
11018
  };
10925
11019
  }
10926
11020
  }
@@ -13376,7 +13470,7 @@ const SCHEDULE_METHOD_NAME_DUMP = "ScheduleUtils.dump";
13376
13470
  * console.log(`Average wait time: ${stats.avgWaitTime} minutes`);
13377
13471
  *
13378
13472
  * // Generate and save report
13379
- * await Schedule.dump("my-strategy");
13473
+ * await Schedule.dump("BTCUSDT", "my-strategy");
13380
13474
  * ```
13381
13475
  */
13382
13476
  class ScheduleUtils {
@@ -13488,7 +13582,7 @@ const Schedule = new ScheduleUtils();
13488
13582
  * .slice(0, 5));
13489
13583
  *
13490
13584
  * // Generate and save report
13491
- * await Performance.dump("my-strategy");
13585
+ * await Performance.dump("BTCUSDT", "my-strategy");
13492
13586
  * ```
13493
13587
  */
13494
13588
  class Performance {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backtest-kit",
3
- "version": "1.4.14",
3
+ "version": "1.5.0",
4
4
  "description": "A TypeScript library for trading system backtest",
5
5
  "author": {
6
6
  "name": "Petr Tripolsky",
package/types.d.ts CHANGED
@@ -723,7 +723,7 @@ interface IPartial {
723
723
  * // Memoized instance cleared from getPartial cache
724
724
  * ```
725
725
  */
726
- clear(symbol: string, data: ISignalRow, priceClose: number): Promise<void>;
726
+ clear(symbol: string, data: ISignalRow, priceClose: number, backtest: boolean): Promise<void>;
727
727
  }
728
728
 
729
729
  /**
@@ -4240,7 +4240,7 @@ interface PerformanceStatistics {
4240
4240
  * console.log("Bottlenecks:", stats.metricStats);
4241
4241
  *
4242
4242
  * // Save report to disk
4243
- * await Performance.dump("my-strategy");
4243
+ * await Performance.dump("BTCUSDT", "my-strategy");
4244
4244
  * ```
4245
4245
  */
4246
4246
  declare class PerformanceMarkdownService {
@@ -5402,7 +5402,7 @@ declare const Live: LiveUtils;
5402
5402
  * console.log(`Average wait time: ${stats.avgWaitTime} minutes`);
5403
5403
  *
5404
5404
  * // Generate and save report
5405
- * await Schedule.dump("my-strategy");
5405
+ * await Schedule.dump("BTCUSDT", "my-strategy");
5406
5406
  * ```
5407
5407
  */
5408
5408
  declare class ScheduleUtils {
@@ -5493,7 +5493,7 @@ declare const Schedule: ScheduleUtils;
5493
5493
  * .slice(0, 5));
5494
5494
  *
5495
5495
  * // Generate and save report
5496
- * await Performance.dump("my-strategy");
5496
+ * await Performance.dump("BTCUSDT", "my-strategy");
5497
5497
  * ```
5498
5498
  */
5499
5499
  declare class Performance {
@@ -8661,7 +8661,7 @@ declare class PartialConnectionService implements IPartial {
8661
8661
  * @param priceClose - Final closing price
8662
8662
  * @returns Promise that resolves when clear is complete
8663
8663
  */
8664
- clear: (symbol: string, data: ISignalRow, priceClose: number) => Promise<void>;
8664
+ clear: (symbol: string, data: ISignalRow, priceClose: number, backtest: boolean) => Promise<void>;
8665
8665
  }
8666
8666
 
8667
8667
  /**
@@ -8742,7 +8742,7 @@ declare class PartialGlobalService {
8742
8742
  * @param priceClose - Final closing price
8743
8743
  * @returns Promise that resolves when clear is complete
8744
8744
  */
8745
- clear: (symbol: string, data: ISignalRow, priceClose: number) => Promise<void>;
8745
+ clear: (symbol: string, data: ISignalRow, priceClose: number, backtest: boolean) => Promise<void>;
8746
8746
  }
8747
8747
 
8748
8748
  /**