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 +155 -61
- package/build/index.mjs +155 -61
- package/package.json +1 -1
- package/types.d.ts +6 -6
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
|
|
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:
|
|
2310
|
-
|
|
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:
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
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
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
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
|
|
2671
|
+
// Calculate percentage of path to TP/SL
|
|
2605
2672
|
{
|
|
2606
|
-
let revenuePercent = 0;
|
|
2607
2673
|
if (signal.position === "long") {
|
|
2608
|
-
|
|
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
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
self.params.callbacks
|
|
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
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
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(
|
|
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]) =>
|
|
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 =
|
|
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
|
|
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:
|
|
2308
|
-
|
|
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:
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
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
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
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
|
|
2669
|
+
// Calculate percentage of path to TP/SL
|
|
2603
2670
|
{
|
|
2604
|
-
let revenuePercent = 0;
|
|
2605
2671
|
if (signal.position === "long") {
|
|
2606
|
-
|
|
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
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
self.params.callbacks
|
|
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
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
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(
|
|
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]) =>
|
|
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 =
|
|
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
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
|
/**
|