backtest-kit 1.4.15 → 1.5.1
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 +329 -77
- package/build/index.mjs +329 -77
- package/package.json +1 -1
- package/types.d.ts +20 -3
package/build/index.mjs
CHANGED
|
@@ -36,6 +36,15 @@ const GLOBAL_CONFIG = {
|
|
|
36
36
|
* Default: 1440 minutes (1 day)
|
|
37
37
|
*/
|
|
38
38
|
CC_MAX_SIGNAL_LIFETIME_MINUTES: 1440,
|
|
39
|
+
/**
|
|
40
|
+
* Maximum time allowed for signal generation (in seconds).
|
|
41
|
+
* Prevents long-running or stuck signal generation routines from blocking
|
|
42
|
+
* execution or consuming resources indefinitely. If generation exceeds this
|
|
43
|
+
* threshold the attempt should be aborted, logged and optionally retried.
|
|
44
|
+
*
|
|
45
|
+
* Default: 180 seconds (3 minutes)
|
|
46
|
+
*/
|
|
47
|
+
CC_MAX_SIGNAL_GENERATION_SECONDS: 180,
|
|
39
48
|
/**
|
|
40
49
|
* Number of retries for getCandles function
|
|
41
50
|
* Default: 3 retries
|
|
@@ -1698,6 +1707,53 @@ var emitters = /*#__PURE__*/Object.freeze({
|
|
|
1698
1707
|
walkerStopSubject: walkerStopSubject
|
|
1699
1708
|
});
|
|
1700
1709
|
|
|
1710
|
+
/**
|
|
1711
|
+
* Converts markdown content to plain text with minimal formatting
|
|
1712
|
+
* @param content - Markdown string to convert
|
|
1713
|
+
* @returns Plain text representation
|
|
1714
|
+
*/
|
|
1715
|
+
const toPlainString = (content) => {
|
|
1716
|
+
if (!content) {
|
|
1717
|
+
return "";
|
|
1718
|
+
}
|
|
1719
|
+
let text = content;
|
|
1720
|
+
// Remove code blocks
|
|
1721
|
+
text = text.replace(/```[\s\S]*?```/g, "");
|
|
1722
|
+
text = text.replace(/`([^`]+)`/g, "$1");
|
|
1723
|
+
// Remove images
|
|
1724
|
+
text = text.replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1");
|
|
1725
|
+
// Convert links to text only (keep link text, remove URL)
|
|
1726
|
+
text = text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
|
|
1727
|
+
// Remove headers (convert to plain text)
|
|
1728
|
+
text = text.replace(/^#{1,6}\s+(.+)$/gm, "$1");
|
|
1729
|
+
// Remove bold and italic markers
|
|
1730
|
+
text = text.replace(/\*\*\*(.+?)\*\*\*/g, "$1");
|
|
1731
|
+
text = text.replace(/\*\*(.+?)\*\*/g, "$1");
|
|
1732
|
+
text = text.replace(/\*(.+?)\*/g, "$1");
|
|
1733
|
+
text = text.replace(/___(.+?)___/g, "$1");
|
|
1734
|
+
text = text.replace(/__(.+?)__/g, "$1");
|
|
1735
|
+
text = text.replace(/_(.+?)_/g, "$1");
|
|
1736
|
+
// Remove strikethrough
|
|
1737
|
+
text = text.replace(/~~(.+?)~~/g, "$1");
|
|
1738
|
+
// Convert lists to plain text with bullets
|
|
1739
|
+
text = text.replace(/^\s*[-*+]\s+/gm, "• ");
|
|
1740
|
+
text = text.replace(/^\s*\d+\.\s+/gm, "• ");
|
|
1741
|
+
// Remove blockquotes
|
|
1742
|
+
text = text.replace(/^\s*>\s+/gm, "");
|
|
1743
|
+
// Remove horizontal rules
|
|
1744
|
+
text = text.replace(/^(\*{3,}|-{3,}|_{3,})$/gm, "");
|
|
1745
|
+
// Remove HTML tags
|
|
1746
|
+
text = text.replace(/<[^>]+>/g, "");
|
|
1747
|
+
// Remove excessive whitespace and normalize line breaks
|
|
1748
|
+
text = text.replace(/\n[\s\n]*\n/g, "\n");
|
|
1749
|
+
text = text.replace(/[ \t]+/g, " ");
|
|
1750
|
+
// Remove all newline characters
|
|
1751
|
+
text = text.replace(/\n/g, " ");
|
|
1752
|
+
// Remove excessive spaces after newline removal
|
|
1753
|
+
text = text.replace(/\s+/g, " ");
|
|
1754
|
+
return text.trim();
|
|
1755
|
+
};
|
|
1756
|
+
|
|
1701
1757
|
const INTERVAL_MINUTES$1 = {
|
|
1702
1758
|
"1m": 1,
|
|
1703
1759
|
"3m": 3,
|
|
@@ -1706,6 +1762,7 @@ const INTERVAL_MINUTES$1 = {
|
|
|
1706
1762
|
"30m": 30,
|
|
1707
1763
|
"1h": 60,
|
|
1708
1764
|
};
|
|
1765
|
+
const TIMEOUT_SYMBOL = Symbol('timeout');
|
|
1709
1766
|
const VALIDATE_SIGNAL_FN = (signal, currentPrice, isScheduled) => {
|
|
1710
1767
|
const errors = [];
|
|
1711
1768
|
// ПРОВЕРКА ОБЯЗАТЕЛЬНЫХ ПОЛЕЙ ISignalRow
|
|
@@ -1889,7 +1946,14 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
1889
1946
|
}))) {
|
|
1890
1947
|
return null;
|
|
1891
1948
|
}
|
|
1892
|
-
const
|
|
1949
|
+
const timeoutMs = GLOBAL_CONFIG.CC_MAX_SIGNAL_GENERATION_SECONDS * 1000;
|
|
1950
|
+
const signal = await Promise.race([
|
|
1951
|
+
self.params.getSignal(self.params.execution.context.symbol, self.params.execution.context.when),
|
|
1952
|
+
sleep(timeoutMs).then(() => TIMEOUT_SYMBOL),
|
|
1953
|
+
]);
|
|
1954
|
+
if (typeof signal === "symbol") {
|
|
1955
|
+
throw new Error(`Timeout for ${self.params.method.context.strategyName} symbol=${self.params.execution.context.symbol}`);
|
|
1956
|
+
}
|
|
1893
1957
|
if (!signal) {
|
|
1894
1958
|
return null;
|
|
1895
1959
|
}
|
|
@@ -1910,7 +1974,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
1910
1974
|
id: randomString(),
|
|
1911
1975
|
priceOpen: signal.priceOpen, // Используем priceOpen из сигнала
|
|
1912
1976
|
position: signal.position,
|
|
1913
|
-
note: signal.note,
|
|
1977
|
+
note: toPlainString(signal.note),
|
|
1914
1978
|
priceTakeProfit: signal.priceTakeProfit,
|
|
1915
1979
|
priceStopLoss: signal.priceStopLoss,
|
|
1916
1980
|
minuteEstimatedTime: signal.minuteEstimatedTime,
|
|
@@ -1930,7 +1994,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
1930
1994
|
id: randomString(),
|
|
1931
1995
|
priceOpen: signal.priceOpen,
|
|
1932
1996
|
position: signal.position,
|
|
1933
|
-
note: signal.note,
|
|
1997
|
+
note: toPlainString(signal.note),
|
|
1934
1998
|
priceTakeProfit: signal.priceTakeProfit,
|
|
1935
1999
|
priceStopLoss: signal.priceStopLoss,
|
|
1936
2000
|
minuteEstimatedTime: signal.minuteEstimatedTime,
|
|
@@ -1949,6 +2013,7 @@ const GET_SIGNAL_FN = trycatch(async (self) => {
|
|
|
1949
2013
|
id: randomString(),
|
|
1950
2014
|
priceOpen: currentPrice,
|
|
1951
2015
|
...signal,
|
|
2016
|
+
note: toPlainString(signal.note),
|
|
1952
2017
|
symbol: self.params.execution.context.symbol,
|
|
1953
2018
|
exchangeName: self.params.method.context.exchangeName,
|
|
1954
2019
|
strategyName: self.params.method.context.strategyName,
|
|
@@ -2174,6 +2239,8 @@ const RETURN_SCHEDULED_SIGNAL_ACTIVE_FN = async (self, scheduled, currentPrice)
|
|
|
2174
2239
|
strategyName: self.params.method.context.strategyName,
|
|
2175
2240
|
exchangeName: self.params.method.context.exchangeName,
|
|
2176
2241
|
symbol: self.params.execution.context.symbol,
|
|
2242
|
+
percentTp: 0,
|
|
2243
|
+
percentSl: 0,
|
|
2177
2244
|
};
|
|
2178
2245
|
if (self.params.callbacks?.onTick) {
|
|
2179
2246
|
self.params.callbacks.onTick(self.params.execution.context.symbol, result, self.params.execution.context.backtest);
|
|
@@ -2277,7 +2344,7 @@ const CLOSE_PENDING_SIGNAL_FN = async (self, signal, currentPrice, closeReason)
|
|
|
2277
2344
|
self.params.callbacks.onClose(self.params.execution.context.symbol, signal, currentPrice, self.params.execution.context.backtest);
|
|
2278
2345
|
}
|
|
2279
2346
|
// КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
|
|
2280
|
-
await self.params.partial.clear(self.params.execution.context.symbol, signal, currentPrice);
|
|
2347
|
+
await self.params.partial.clear(self.params.execution.context.symbol, signal, currentPrice, self.params.execution.context.backtest);
|
|
2281
2348
|
await self.params.risk.removeSignal(self.params.execution.context.symbol, {
|
|
2282
2349
|
strategyName: self.params.method.context.strategyName,
|
|
2283
2350
|
riskName: self.params.riskName,
|
|
@@ -2300,31 +2367,56 @@ const CLOSE_PENDING_SIGNAL_FN = async (self, signal, currentPrice, closeReason)
|
|
|
2300
2367
|
return result;
|
|
2301
2368
|
};
|
|
2302
2369
|
const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
|
|
2303
|
-
|
|
2370
|
+
let percentTp = 0;
|
|
2371
|
+
let percentSl = 0;
|
|
2372
|
+
// Calculate percentage of path to TP/SL for partial fill/loss callbacks
|
|
2304
2373
|
{
|
|
2305
|
-
let revenuePercent = 0;
|
|
2306
2374
|
if (signal.position === "long") {
|
|
2307
|
-
// For long:
|
|
2308
|
-
|
|
2375
|
+
// For long: calculate progress towards TP or SL
|
|
2376
|
+
const currentDistance = currentPrice - signal.priceOpen;
|
|
2377
|
+
if (currentDistance > 0) {
|
|
2378
|
+
// Moving towards TP
|
|
2379
|
+
const tpDistance = signal.priceTakeProfit - signal.priceOpen;
|
|
2380
|
+
const progressPercent = (currentDistance / tpDistance) * 100;
|
|
2381
|
+
percentTp = Math.min(progressPercent, 100);
|
|
2382
|
+
await self.params.partial.profit(self.params.execution.context.symbol, signal, currentPrice, percentTp, 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, percentTp, self.params.execution.context.backtest);
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
else if (currentDistance < 0) {
|
|
2388
|
+
// Moving towards SL
|
|
2389
|
+
const slDistance = signal.priceOpen - signal.priceStopLoss;
|
|
2390
|
+
const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
|
|
2391
|
+
percentSl = Math.min(progressPercent, 100);
|
|
2392
|
+
await self.params.partial.loss(self.params.execution.context.symbol, signal, currentPrice, percentSl, self.params.execution.context.backtest, self.params.execution.context.when);
|
|
2393
|
+
if (self.params.callbacks?.onPartialLoss) {
|
|
2394
|
+
self.params.callbacks.onPartialLoss(self.params.execution.context.symbol, signal, currentPrice, percentSl, self.params.execution.context.backtest);
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2309
2397
|
}
|
|
2310
2398
|
else if (signal.position === "short") {
|
|
2311
|
-
// For short:
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
self.params.callbacks
|
|
2399
|
+
// For short: calculate progress towards TP or SL
|
|
2400
|
+
const currentDistance = signal.priceOpen - currentPrice;
|
|
2401
|
+
if (currentDistance > 0) {
|
|
2402
|
+
// Moving towards TP
|
|
2403
|
+
const tpDistance = signal.priceOpen - signal.priceTakeProfit;
|
|
2404
|
+
const progressPercent = (currentDistance / tpDistance) * 100;
|
|
2405
|
+
percentTp = Math.min(progressPercent, 100);
|
|
2406
|
+
await self.params.partial.profit(self.params.execution.context.symbol, signal, currentPrice, percentTp, self.params.execution.context.backtest, self.params.execution.context.when);
|
|
2407
|
+
if (self.params.callbacks?.onPartialProfit) {
|
|
2408
|
+
self.params.callbacks.onPartialProfit(self.params.execution.context.symbol, signal, currentPrice, percentTp, self.params.execution.context.backtest);
|
|
2409
|
+
}
|
|
2320
2410
|
}
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
self.params.callbacks
|
|
2411
|
+
if (currentDistance < 0) {
|
|
2412
|
+
// Moving towards SL
|
|
2413
|
+
const slDistance = signal.priceStopLoss - signal.priceOpen;
|
|
2414
|
+
const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
|
|
2415
|
+
percentSl = Math.min(progressPercent, 100);
|
|
2416
|
+
await self.params.partial.loss(self.params.execution.context.symbol, signal, currentPrice, percentSl, self.params.execution.context.backtest, self.params.execution.context.when);
|
|
2417
|
+
if (self.params.callbacks?.onPartialLoss) {
|
|
2418
|
+
self.params.callbacks.onPartialLoss(self.params.execution.context.symbol, signal, currentPrice, percentSl, self.params.execution.context.backtest);
|
|
2419
|
+
}
|
|
2328
2420
|
}
|
|
2329
2421
|
}
|
|
2330
2422
|
}
|
|
@@ -2335,6 +2427,8 @@ const RETURN_PENDING_SIGNAL_ACTIVE_FN = async (self, signal, currentPrice) => {
|
|
|
2335
2427
|
strategyName: self.params.method.context.strategyName,
|
|
2336
2428
|
exchangeName: self.params.method.context.exchangeName,
|
|
2337
2429
|
symbol: self.params.execution.context.symbol,
|
|
2430
|
+
percentTp,
|
|
2431
|
+
percentSl,
|
|
2338
2432
|
};
|
|
2339
2433
|
if (self.params.callbacks?.onTick) {
|
|
2340
2434
|
self.params.callbacks.onTick(self.params.execution.context.symbol, result, self.params.execution.context.backtest);
|
|
@@ -2456,7 +2550,7 @@ const CLOSE_PENDING_SIGNAL_IN_BACKTEST_FN = async (self, signal, averagePrice, c
|
|
|
2456
2550
|
self.params.callbacks.onClose(self.params.execution.context.symbol, signal, averagePrice, self.params.execution.context.backtest);
|
|
2457
2551
|
}
|
|
2458
2552
|
// КРИТИЧНО: Очищаем состояние ClientPartial при закрытии позиции
|
|
2459
|
-
await self.params.partial.clear(self.params.execution.context.symbol, signal, averagePrice);
|
|
2553
|
+
await self.params.partial.clear(self.params.execution.context.symbol, signal, averagePrice, self.params.execution.context.backtest);
|
|
2460
2554
|
await self.params.risk.removeSignal(self.params.execution.context.symbol, {
|
|
2461
2555
|
strategyName: self.params.method.context.strategyName,
|
|
2462
2556
|
riskName: self.params.riskName,
|
|
@@ -2599,29 +2693,50 @@ const PROCESS_PENDING_SIGNAL_CANDLES_FN = async (self, signal, candles) => {
|
|
|
2599
2693
|
return await CLOSE_PENDING_SIGNAL_IN_BACKTEST_FN(self, signal, closePrice, closeReason, currentCandleTimestamp);
|
|
2600
2694
|
}
|
|
2601
2695
|
// Call onPartialProfit/onPartialLoss callbacks during backtest candle processing
|
|
2602
|
-
// Calculate
|
|
2696
|
+
// Calculate percentage of path to TP/SL
|
|
2603
2697
|
{
|
|
2604
|
-
let revenuePercent = 0;
|
|
2605
2698
|
if (signal.position === "long") {
|
|
2606
|
-
|
|
2699
|
+
// For long: calculate progress towards TP or SL
|
|
2700
|
+
const currentDistance = averagePrice - signal.priceOpen;
|
|
2701
|
+
if (currentDistance > 0) {
|
|
2702
|
+
// Moving towards TP
|
|
2703
|
+
const tpDistance = signal.priceTakeProfit - signal.priceOpen;
|
|
2704
|
+
const progressPercent = (currentDistance / tpDistance) * 100;
|
|
2705
|
+
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);
|
|
2706
|
+
if (self.params.callbacks?.onPartialProfit) {
|
|
2707
|
+
self.params.callbacks.onPartialProfit(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
else if (currentDistance < 0) {
|
|
2711
|
+
// Moving towards SL
|
|
2712
|
+
const slDistance = signal.priceOpen - signal.priceStopLoss;
|
|
2713
|
+
const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
|
|
2714
|
+
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);
|
|
2715
|
+
if (self.params.callbacks?.onPartialLoss) {
|
|
2716
|
+
self.params.callbacks.onPartialLoss(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2607
2719
|
}
|
|
2608
2720
|
else if (signal.position === "short") {
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
self.params.callbacks
|
|
2721
|
+
// For short: calculate progress towards TP or SL
|
|
2722
|
+
const currentDistance = signal.priceOpen - averagePrice;
|
|
2723
|
+
if (currentDistance > 0) {
|
|
2724
|
+
// Moving towards TP
|
|
2725
|
+
const tpDistance = signal.priceOpen - signal.priceTakeProfit;
|
|
2726
|
+
const progressPercent = (currentDistance / tpDistance) * 100;
|
|
2727
|
+
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);
|
|
2728
|
+
if (self.params.callbacks?.onPartialProfit) {
|
|
2729
|
+
self.params.callbacks.onPartialProfit(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
|
|
2730
|
+
}
|
|
2617
2731
|
}
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2732
|
+
if (currentDistance < 0) {
|
|
2733
|
+
// Moving towards SL
|
|
2734
|
+
const slDistance = signal.priceStopLoss - signal.priceOpen;
|
|
2735
|
+
const progressPercent = (Math.abs(currentDistance) / slDistance) * 100;
|
|
2736
|
+
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);
|
|
2737
|
+
if (self.params.callbacks?.onPartialLoss) {
|
|
2738
|
+
self.params.callbacks.onPartialLoss(self.params.execution.context.symbol, signal, averagePrice, Math.min(progressPercent, 100), self.params.execution.context.backtest);
|
|
2739
|
+
}
|
|
2625
2740
|
}
|
|
2626
2741
|
}
|
|
2627
2742
|
}
|
|
@@ -2910,6 +3025,8 @@ class ClientStrategy {
|
|
|
2910
3025
|
action: "active",
|
|
2911
3026
|
signal: scheduled,
|
|
2912
3027
|
currentPrice: lastPrice,
|
|
3028
|
+
percentSl: 0,
|
|
3029
|
+
percentTp: 0,
|
|
2913
3030
|
strategyName: this.params.method.context.strategyName,
|
|
2914
3031
|
exchangeName: this.params.method.context.exchangeName,
|
|
2915
3032
|
symbol: this.params.execution.context.symbol,
|
|
@@ -5611,7 +5728,7 @@ const columns$4 = [
|
|
|
5611
5728
|
{
|
|
5612
5729
|
key: "note",
|
|
5613
5730
|
label: "Note",
|
|
5614
|
-
format: (data) => data.signal.note ?? "N/A",
|
|
5731
|
+
format: (data) => toPlainString(data.signal.note ?? "N/A"),
|
|
5615
5732
|
},
|
|
5616
5733
|
{
|
|
5617
5734
|
key: "openPrice",
|
|
@@ -5758,14 +5875,33 @@ let ReportStorage$4 = class ReportStorage {
|
|
|
5758
5875
|
async getReport(strategyName) {
|
|
5759
5876
|
const stats = await this.getData();
|
|
5760
5877
|
if (stats.totalSignals === 0) {
|
|
5761
|
-
return
|
|
5878
|
+
return [
|
|
5879
|
+
`# Backtest Report: ${strategyName}`,
|
|
5880
|
+
"",
|
|
5881
|
+
"No signals closed yet."
|
|
5882
|
+
].join("\n");
|
|
5762
5883
|
}
|
|
5763
5884
|
const header = columns$4.map((col) => col.label);
|
|
5764
5885
|
const separator = columns$4.map(() => "---");
|
|
5765
5886
|
const rows = this._signalList.map((closedSignal) => columns$4.map((col) => col.format(closedSignal)));
|
|
5766
5887
|
const tableData = [header, separator, ...rows];
|
|
5767
|
-
const table =
|
|
5768
|
-
return
|
|
5888
|
+
const table = tableData.map(row => `| ${row.join(" | ")} |`).join("\n");
|
|
5889
|
+
return [
|
|
5890
|
+
`# Backtest Report: ${strategyName}`,
|
|
5891
|
+
"",
|
|
5892
|
+
table,
|
|
5893
|
+
"",
|
|
5894
|
+
`**Total signals:** ${stats.totalSignals}`,
|
|
5895
|
+
`**Closed signals:** ${stats.totalSignals}`,
|
|
5896
|
+
`**Win rate:** ${stats.winRate === null ? "N/A" : `${stats.winRate.toFixed(2)}% (${stats.winCount}W / ${stats.lossCount}L) (higher is better)`}`,
|
|
5897
|
+
`**Average PNL:** ${stats.avgPnl === null ? "N/A" : `${stats.avgPnl > 0 ? "+" : ""}${stats.avgPnl.toFixed(2)}% (higher is better)`}`,
|
|
5898
|
+
`**Total PNL:** ${stats.totalPnl === null ? "N/A" : `${stats.totalPnl > 0 ? "+" : ""}${stats.totalPnl.toFixed(2)}% (higher is better)`}`,
|
|
5899
|
+
`**Standard Deviation:** ${stats.stdDev === null ? "N/A" : `${stats.stdDev.toFixed(3)}% (lower is better)`}`,
|
|
5900
|
+
`**Sharpe Ratio:** ${stats.sharpeRatio === null ? "N/A" : `${stats.sharpeRatio.toFixed(3)} (higher is better)`}`,
|
|
5901
|
+
`**Annualized Sharpe Ratio:** ${stats.annualizedSharpeRatio === null ? "N/A" : `${stats.annualizedSharpeRatio.toFixed(3)} (higher is better)`}`,
|
|
5902
|
+
`**Certainty Ratio:** ${stats.certaintyRatio === null ? "N/A" : `${stats.certaintyRatio.toFixed(3)} (higher is better)`}`,
|
|
5903
|
+
`**Expected Yearly Returns:** ${stats.expectedYearlyReturns === null ? "N/A" : `${stats.expectedYearlyReturns > 0 ? "+" : ""}${stats.expectedYearlyReturns.toFixed(2)}% (higher is better)`}`,
|
|
5904
|
+
].join("\n");
|
|
5769
5905
|
}
|
|
5770
5906
|
/**
|
|
5771
5907
|
* Saves strategy report to disk.
|
|
@@ -6023,7 +6159,7 @@ const columns$3 = [
|
|
|
6023
6159
|
{
|
|
6024
6160
|
key: "note",
|
|
6025
6161
|
label: "Note",
|
|
6026
|
-
format: (data) => data.note ?? "N/A",
|
|
6162
|
+
format: (data) => toPlainString(data.note ?? "N/A"),
|
|
6027
6163
|
},
|
|
6028
6164
|
{
|
|
6029
6165
|
key: "currentPrice",
|
|
@@ -6047,6 +6183,16 @@ const columns$3 = [
|
|
|
6047
6183
|
label: "Stop Loss",
|
|
6048
6184
|
format: (data) => data.stopLoss !== undefined ? `${data.stopLoss.toFixed(8)} USD` : "N/A",
|
|
6049
6185
|
},
|
|
6186
|
+
{
|
|
6187
|
+
key: "percentTp",
|
|
6188
|
+
label: "% to TP",
|
|
6189
|
+
format: (data) => data.percentTp !== undefined ? `${data.percentTp.toFixed(2)}%` : "N/A",
|
|
6190
|
+
},
|
|
6191
|
+
{
|
|
6192
|
+
key: "percentSl",
|
|
6193
|
+
label: "% to SL",
|
|
6194
|
+
format: (data) => data.percentSl !== undefined ? `${data.percentSl.toFixed(2)}%` : "N/A",
|
|
6195
|
+
},
|
|
6050
6196
|
{
|
|
6051
6197
|
key: "pnl",
|
|
6052
6198
|
label: "PNL (net)",
|
|
@@ -6149,6 +6295,8 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
6149
6295
|
openPrice: data.signal.priceOpen,
|
|
6150
6296
|
takeProfit: data.signal.priceTakeProfit,
|
|
6151
6297
|
stopLoss: data.signal.priceStopLoss,
|
|
6298
|
+
percentTp: data.percentTp,
|
|
6299
|
+
percentSl: data.percentSl,
|
|
6152
6300
|
};
|
|
6153
6301
|
// Replace existing event or add new one
|
|
6154
6302
|
if (existingIndex !== -1) {
|
|
@@ -6290,14 +6438,33 @@ let ReportStorage$3 = class ReportStorage {
|
|
|
6290
6438
|
async getReport(strategyName) {
|
|
6291
6439
|
const stats = await this.getData();
|
|
6292
6440
|
if (stats.totalEvents === 0) {
|
|
6293
|
-
return
|
|
6441
|
+
return [
|
|
6442
|
+
`# Live Trading Report: ${strategyName}`,
|
|
6443
|
+
"",
|
|
6444
|
+
"No events recorded yet."
|
|
6445
|
+
].join("\n");
|
|
6294
6446
|
}
|
|
6295
6447
|
const header = columns$3.map((col) => col.label);
|
|
6296
6448
|
const separator = columns$3.map(() => "---");
|
|
6297
6449
|
const rows = this._eventList.map((event) => columns$3.map((col) => col.format(event)));
|
|
6298
6450
|
const tableData = [header, separator, ...rows];
|
|
6299
|
-
const table =
|
|
6300
|
-
return
|
|
6451
|
+
const table = tableData.map(row => `| ${row.join(" | ")} |`).join("\n");
|
|
6452
|
+
return [
|
|
6453
|
+
`# Live Trading Report: ${strategyName}`,
|
|
6454
|
+
"",
|
|
6455
|
+
table,
|
|
6456
|
+
"",
|
|
6457
|
+
`**Total events:** ${stats.totalEvents}`,
|
|
6458
|
+
`**Closed signals:** ${stats.totalClosed}`,
|
|
6459
|
+
`**Win rate:** ${stats.winRate === null ? "N/A" : `${stats.winRate.toFixed(2)}% (${stats.winCount}W / ${stats.lossCount}L) (higher is better)`}`,
|
|
6460
|
+
`**Average PNL:** ${stats.avgPnl === null ? "N/A" : `${stats.avgPnl > 0 ? "+" : ""}${stats.avgPnl.toFixed(2)}% (higher is better)`}`,
|
|
6461
|
+
`**Total PNL:** ${stats.totalPnl === null ? "N/A" : `${stats.totalPnl > 0 ? "+" : ""}${stats.totalPnl.toFixed(2)}% (higher is better)`}`,
|
|
6462
|
+
`**Standard Deviation:** ${stats.stdDev === null ? "N/A" : `${stats.stdDev.toFixed(3)}% (lower is better)`}`,
|
|
6463
|
+
`**Sharpe Ratio:** ${stats.sharpeRatio === null ? "N/A" : `${stats.sharpeRatio.toFixed(3)} (higher is better)`}`,
|
|
6464
|
+
`**Annualized Sharpe Ratio:** ${stats.annualizedSharpeRatio === null ? "N/A" : `${stats.annualizedSharpeRatio.toFixed(3)} (higher is better)`}`,
|
|
6465
|
+
`**Certainty Ratio:** ${stats.certaintyRatio === null ? "N/A" : `${stats.certaintyRatio.toFixed(3)} (higher is better)`}`,
|
|
6466
|
+
`**Expected Yearly Returns:** ${stats.expectedYearlyReturns === null ? "N/A" : `${stats.expectedYearlyReturns > 0 ? "+" : ""}${stats.expectedYearlyReturns.toFixed(2)}% (higher is better)`}`,
|
|
6467
|
+
].join("\n");
|
|
6301
6468
|
}
|
|
6302
6469
|
/**
|
|
6303
6470
|
* Saves strategy report to disk.
|
|
@@ -6550,7 +6717,7 @@ const columns$2 = [
|
|
|
6550
6717
|
{
|
|
6551
6718
|
key: "note",
|
|
6552
6719
|
label: "Note",
|
|
6553
|
-
format: (data) => data.note ?? "N/A",
|
|
6720
|
+
format: (data) => toPlainString(data.note ?? "N/A"),
|
|
6554
6721
|
},
|
|
6555
6722
|
{
|
|
6556
6723
|
key: "currentPrice",
|
|
@@ -6694,14 +6861,28 @@ let ReportStorage$2 = class ReportStorage {
|
|
|
6694
6861
|
async getReport(strategyName) {
|
|
6695
6862
|
const stats = await this.getData();
|
|
6696
6863
|
if (stats.totalEvents === 0) {
|
|
6697
|
-
return
|
|
6864
|
+
return [
|
|
6865
|
+
`# Scheduled Signals Report: ${strategyName}`,
|
|
6866
|
+
"",
|
|
6867
|
+
"No scheduled signals recorded yet."
|
|
6868
|
+
].join("\n");
|
|
6698
6869
|
}
|
|
6699
6870
|
const header = columns$2.map((col) => col.label);
|
|
6700
6871
|
const separator = columns$2.map(() => "---");
|
|
6701
6872
|
const rows = this._eventList.map((event) => columns$2.map((col) => col.format(event)));
|
|
6702
6873
|
const tableData = [header, separator, ...rows];
|
|
6703
|
-
const table =
|
|
6704
|
-
return
|
|
6874
|
+
const table = tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
6875
|
+
return [
|
|
6876
|
+
`# Scheduled Signals Report: ${strategyName}`,
|
|
6877
|
+
"",
|
|
6878
|
+
table,
|
|
6879
|
+
"",
|
|
6880
|
+
`**Total events:** ${stats.totalEvents}`,
|
|
6881
|
+
`**Scheduled signals:** ${stats.totalScheduled}`,
|
|
6882
|
+
`**Cancelled signals:** ${stats.totalCancelled}`,
|
|
6883
|
+
`**Cancellation rate:** ${stats.cancellationRate === null ? "N/A" : `${stats.cancellationRate.toFixed(2)}% (lower is better)`}`,
|
|
6884
|
+
`**Average wait time (cancelled):** ${stats.avgWaitTime === null ? "N/A" : `${stats.avgWaitTime.toFixed(2)} minutes`}`
|
|
6885
|
+
].join("\n");
|
|
6705
6886
|
}
|
|
6706
6887
|
/**
|
|
6707
6888
|
* Saves strategy report to disk.
|
|
@@ -7019,7 +7200,11 @@ class PerformanceStorage {
|
|
|
7019
7200
|
async getReport(strategyName) {
|
|
7020
7201
|
const stats = await this.getData(strategyName);
|
|
7021
7202
|
if (stats.totalEvents === 0) {
|
|
7022
|
-
return
|
|
7203
|
+
return [
|
|
7204
|
+
`# Performance Report: ${strategyName}`,
|
|
7205
|
+
"",
|
|
7206
|
+
"No performance metrics recorded yet."
|
|
7207
|
+
].join("\n");
|
|
7023
7208
|
}
|
|
7024
7209
|
// Sort metrics by total duration (descending) to show bottlenecks first
|
|
7025
7210
|
const sortedMetrics = Object.values(stats.metricStats).sort((a, b) => b.totalDuration - a.totalDuration);
|
|
@@ -7056,13 +7241,29 @@ class PerformanceStorage {
|
|
|
7056
7241
|
metric.maxWaitTime.toFixed(2),
|
|
7057
7242
|
]);
|
|
7058
7243
|
const summaryTableData = [summaryHeader, summarySeparator, ...summaryRows];
|
|
7059
|
-
const summaryTable =
|
|
7244
|
+
const summaryTable = summaryTableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
7060
7245
|
// Calculate percentage of total time for each metric
|
|
7061
7246
|
const percentages = sortedMetrics.map((metric) => {
|
|
7062
7247
|
const pct = (metric.totalDuration / stats.totalDuration) * 100;
|
|
7063
7248
|
return `- **${metric.metricType}**: ${pct.toFixed(1)}% (${metric.totalDuration.toFixed(2)}ms total)`;
|
|
7064
7249
|
});
|
|
7065
|
-
return
|
|
7250
|
+
return [
|
|
7251
|
+
`# Performance Report: ${strategyName}`,
|
|
7252
|
+
"",
|
|
7253
|
+
`**Total events:** ${stats.totalEvents}`,
|
|
7254
|
+
`**Total execution time:** ${stats.totalDuration.toFixed(2)}ms`,
|
|
7255
|
+
`**Number of metric types:** ${Object.keys(stats.metricStats).length}`,
|
|
7256
|
+
"",
|
|
7257
|
+
"## Time Distribution",
|
|
7258
|
+
"",
|
|
7259
|
+
percentages.join("\n"),
|
|
7260
|
+
"",
|
|
7261
|
+
"## Detailed Metrics",
|
|
7262
|
+
"",
|
|
7263
|
+
summaryTable,
|
|
7264
|
+
"",
|
|
7265
|
+
"**Note:** All durations are in milliseconds. P95/P99 represent 95th and 99th percentile response times. Wait times show the interval between consecutive events of the same type."
|
|
7266
|
+
].join("\n");
|
|
7066
7267
|
}
|
|
7067
7268
|
/**
|
|
7068
7269
|
* Saves performance report to disk.
|
|
@@ -7466,7 +7667,7 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
7466
7667
|
// Build table rows
|
|
7467
7668
|
const rows = topStrategies.map((result, index) => columns.map((col) => col.format(result, index)));
|
|
7468
7669
|
const tableData = [header, separator, ...rows];
|
|
7469
|
-
return
|
|
7670
|
+
return tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
7470
7671
|
}
|
|
7471
7672
|
/**
|
|
7472
7673
|
* Generates PNL table showing all closed signals across all strategies (View).
|
|
@@ -7503,7 +7704,7 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
7503
7704
|
// Build table rows
|
|
7504
7705
|
const rows = allSignals.map((signal) => pnlColumns.map((col) => col.format(signal)));
|
|
7505
7706
|
const tableData = [header, separator, ...rows];
|
|
7506
|
-
return
|
|
7707
|
+
return tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
7507
7708
|
}
|
|
7508
7709
|
/**
|
|
7509
7710
|
* Generates markdown report with all strategy results (View).
|
|
@@ -7518,7 +7719,30 @@ let ReportStorage$1 = class ReportStorage {
|
|
|
7518
7719
|
const results = await this.getData(symbol, metric, context);
|
|
7519
7720
|
// Get total signals for best strategy
|
|
7520
7721
|
const bestStrategySignals = results.bestStats?.totalSignals ?? 0;
|
|
7521
|
-
return
|
|
7722
|
+
return [
|
|
7723
|
+
`# Walker Comparison Report: ${results.walkerName}`,
|
|
7724
|
+
"",
|
|
7725
|
+
`**Symbol:** ${results.symbol}`,
|
|
7726
|
+
`**Exchange:** ${results.exchangeName}`,
|
|
7727
|
+
`**Frame:** ${results.frameName}`,
|
|
7728
|
+
`**Optimization Metric:** ${results.metric}`,
|
|
7729
|
+
`**Strategies Tested:** ${results.totalStrategies}`,
|
|
7730
|
+
"",
|
|
7731
|
+
`## Best Strategy: ${results.bestStrategy}`,
|
|
7732
|
+
"",
|
|
7733
|
+
`**Best ${results.metric}:** ${formatMetric(results.bestMetric)}`,
|
|
7734
|
+
`**Total Signals:** ${bestStrategySignals}`,
|
|
7735
|
+
"",
|
|
7736
|
+
"## Top Strategies Comparison",
|
|
7737
|
+
"",
|
|
7738
|
+
this.getComparisonTable(metric, 10),
|
|
7739
|
+
"",
|
|
7740
|
+
"## All Signals (PNL Table)",
|
|
7741
|
+
"",
|
|
7742
|
+
this.getPnlTable(),
|
|
7743
|
+
"",
|
|
7744
|
+
"**Note:** Higher values are better for all metrics except Standard Deviation (lower is better)."
|
|
7745
|
+
].join("\n");
|
|
7522
7746
|
}
|
|
7523
7747
|
/**
|
|
7524
7748
|
* Saves walker report to disk.
|
|
@@ -8032,14 +8256,24 @@ class HeatmapStorage {
|
|
|
8032
8256
|
async getReport(strategyName) {
|
|
8033
8257
|
const data = await this.getData();
|
|
8034
8258
|
if (data.symbols.length === 0) {
|
|
8035
|
-
return
|
|
8259
|
+
return [
|
|
8260
|
+
`# Portfolio Heatmap: ${strategyName}`,
|
|
8261
|
+
"",
|
|
8262
|
+
"*No data available*"
|
|
8263
|
+
].join("\n");
|
|
8036
8264
|
}
|
|
8037
8265
|
const header = columns$1.map((col) => col.label);
|
|
8038
8266
|
const separator = columns$1.map(() => "---");
|
|
8039
8267
|
const rows = data.symbols.map((row) => columns$1.map((col) => col.format(row)));
|
|
8040
8268
|
const tableData = [header, separator, ...rows];
|
|
8041
|
-
const table =
|
|
8042
|
-
return
|
|
8269
|
+
const table = tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
8270
|
+
return [
|
|
8271
|
+
`# Portfolio Heatmap: ${strategyName}`,
|
|
8272
|
+
"",
|
|
8273
|
+
`**Total Symbols:** ${data.totalSymbols} | **Portfolio PNL:** ${data.portfolioTotalPnl !== null ? str(data.portfolioTotalPnl, "%+.2f%%") : "N/A"} | **Portfolio Sharpe:** ${data.portfolioSharpeRatio !== null ? str(data.portfolioSharpeRatio, "%.2f") : "N/A"} | **Total Trades:** ${data.portfolioTotalTrades}`,
|
|
8274
|
+
"",
|
|
8275
|
+
table
|
|
8276
|
+
].join("\n");
|
|
8043
8277
|
}
|
|
8044
8278
|
/**
|
|
8045
8279
|
* Saves heatmap report to disk.
|
|
@@ -8762,6 +8996,8 @@ class OptimizerTemplateService {
|
|
|
8762
8996
|
interval,
|
|
8763
8997
|
prompt,
|
|
8764
8998
|
});
|
|
8999
|
+
// Convert prompt to plain text first
|
|
9000
|
+
const plainPrompt = toPlainString(prompt);
|
|
8765
9001
|
// Escape special characters to prevent code injection
|
|
8766
9002
|
const escapedStrategyName = String(strategyName)
|
|
8767
9003
|
.replace(/\\/g, '\\\\')
|
|
@@ -8769,7 +9005,7 @@ class OptimizerTemplateService {
|
|
|
8769
9005
|
const escapedInterval = String(interval)
|
|
8770
9006
|
.replace(/\\/g, '\\\\')
|
|
8771
9007
|
.replace(/"/g, '\\"');
|
|
8772
|
-
const escapedPrompt = String(
|
|
9008
|
+
const escapedPrompt = String(plainPrompt)
|
|
8773
9009
|
.replace(/\\/g, '\\\\')
|
|
8774
9010
|
.replace(/`/g, '\\`')
|
|
8775
9011
|
.replace(/\$/g, '\\$');
|
|
@@ -9950,7 +10186,7 @@ const HANDLE_PROFIT_FN = async (symbol, data, currentPrice, revenuePercent, back
|
|
|
9950
10186
|
}
|
|
9951
10187
|
}
|
|
9952
10188
|
if (shouldPersist) {
|
|
9953
|
-
await self._persistState(symbol);
|
|
10189
|
+
await self._persistState(symbol, backtest);
|
|
9954
10190
|
}
|
|
9955
10191
|
};
|
|
9956
10192
|
/**
|
|
@@ -9997,7 +10233,7 @@ const HANDLE_LOSS_FN = async (symbol, data, currentPrice, lossPercent, backtest,
|
|
|
9997
10233
|
}
|
|
9998
10234
|
}
|
|
9999
10235
|
if (shouldPersist) {
|
|
10000
|
-
await self._persistState(symbol);
|
|
10236
|
+
await self._persistState(symbol, backtest);
|
|
10001
10237
|
}
|
|
10002
10238
|
};
|
|
10003
10239
|
/**
|
|
@@ -10133,7 +10369,10 @@ class ClientPartial {
|
|
|
10133
10369
|
* @param symbol - Trading pair symbol
|
|
10134
10370
|
* @returns Promise that resolves when persistence is complete
|
|
10135
10371
|
*/
|
|
10136
|
-
async _persistState(symbol) {
|
|
10372
|
+
async _persistState(symbol, backtest) {
|
|
10373
|
+
if (backtest) {
|
|
10374
|
+
return;
|
|
10375
|
+
}
|
|
10137
10376
|
this.params.logger.debug("ClientPartial persistState", { symbol });
|
|
10138
10377
|
if (this._states === NEED_FETCH) {
|
|
10139
10378
|
throw new Error("ClientPartial not initialized. Call waitForInit() before using.");
|
|
@@ -10256,7 +10495,7 @@ class ClientPartial {
|
|
|
10256
10495
|
* // Cleanup: PartialConnectionService.getPartial.clear(signal.id)
|
|
10257
10496
|
* ```
|
|
10258
10497
|
*/
|
|
10259
|
-
async clear(symbol, data, priceClose) {
|
|
10498
|
+
async clear(symbol, data, priceClose, backtest) {
|
|
10260
10499
|
this.params.logger.log("ClientPartial clear", {
|
|
10261
10500
|
symbol,
|
|
10262
10501
|
data,
|
|
@@ -10266,7 +10505,7 @@ class ClientPartial {
|
|
|
10266
10505
|
throw new Error("ClientPartial not initialized. Call waitForInit() before using.");
|
|
10267
10506
|
}
|
|
10268
10507
|
this._states.delete(data.id);
|
|
10269
|
-
await this._persistState(symbol);
|
|
10508
|
+
await this._persistState(symbol, backtest);
|
|
10270
10509
|
}
|
|
10271
10510
|
}
|
|
10272
10511
|
|
|
@@ -10437,7 +10676,7 @@ class PartialConnectionService {
|
|
|
10437
10676
|
* @param priceClose - Final closing price
|
|
10438
10677
|
* @returns Promise that resolves when clear is complete
|
|
10439
10678
|
*/
|
|
10440
|
-
this.clear = async (symbol, data, priceClose) => {
|
|
10679
|
+
this.clear = async (symbol, data, priceClose, backtest) => {
|
|
10441
10680
|
this.loggerService.log("partialConnectionService profit", {
|
|
10442
10681
|
symbol,
|
|
10443
10682
|
data,
|
|
@@ -10445,7 +10684,7 @@ class PartialConnectionService {
|
|
|
10445
10684
|
});
|
|
10446
10685
|
const partial = this.getPartial(data.id);
|
|
10447
10686
|
await partial.waitForInit(symbol);
|
|
10448
|
-
await partial.clear(symbol, data, priceClose);
|
|
10687
|
+
await partial.clear(symbol, data, priceClose, backtest);
|
|
10449
10688
|
this.getPartial.clear(data.id);
|
|
10450
10689
|
};
|
|
10451
10690
|
}
|
|
@@ -10592,14 +10831,26 @@ class ReportStorage {
|
|
|
10592
10831
|
async getReport(symbol, strategyName) {
|
|
10593
10832
|
const stats = await this.getData();
|
|
10594
10833
|
if (stats.totalEvents === 0) {
|
|
10595
|
-
return
|
|
10834
|
+
return [
|
|
10835
|
+
`# Partial Profit/Loss Report: ${symbol}:${strategyName}`,
|
|
10836
|
+
"",
|
|
10837
|
+
"No partial profit/loss events recorded yet."
|
|
10838
|
+
].join("\n");
|
|
10596
10839
|
}
|
|
10597
10840
|
const header = columns.map((col) => col.label);
|
|
10598
10841
|
const separator = columns.map(() => "---");
|
|
10599
10842
|
const rows = this._eventList.map((event) => columns.map((col) => col.format(event)));
|
|
10600
10843
|
const tableData = [header, separator, ...rows];
|
|
10601
|
-
const table =
|
|
10602
|
-
return
|
|
10844
|
+
const table = tableData.map((row) => `| ${row.join(" | ")} |`).join("\n");
|
|
10845
|
+
return [
|
|
10846
|
+
`# Partial Profit/Loss Report: ${symbol}:${strategyName}`,
|
|
10847
|
+
"",
|
|
10848
|
+
table,
|
|
10849
|
+
"",
|
|
10850
|
+
`**Total events:** ${stats.totalEvents}`,
|
|
10851
|
+
`**Profit events:** ${stats.totalProfit}`,
|
|
10852
|
+
`**Loss events:** ${stats.totalLoss}`
|
|
10853
|
+
].join("\n");
|
|
10603
10854
|
}
|
|
10604
10855
|
/**
|
|
10605
10856
|
* Saves symbol-strategy report to disk.
|
|
@@ -10914,13 +11165,14 @@ class PartialGlobalService {
|
|
|
10914
11165
|
* @param priceClose - Final closing price
|
|
10915
11166
|
* @returns Promise that resolves when clear is complete
|
|
10916
11167
|
*/
|
|
10917
|
-
this.clear = async (symbol, data, priceClose) => {
|
|
11168
|
+
this.clear = async (symbol, data, priceClose, backtest) => {
|
|
10918
11169
|
this.loggerService.log("partialGlobalService profit", {
|
|
10919
11170
|
symbol,
|
|
10920
11171
|
data,
|
|
10921
11172
|
priceClose,
|
|
11173
|
+
backtest,
|
|
10922
11174
|
});
|
|
10923
|
-
return await this.partialConnectionService.clear(symbol, data, priceClose);
|
|
11175
|
+
return await this.partialConnectionService.clear(symbol, data, priceClose, backtest);
|
|
10924
11176
|
};
|
|
10925
11177
|
}
|
|
10926
11178
|
}
|