@backtest-kit/signals 14.0.0 → 15.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +201 -201
- package/build/index.cjs +94 -75
- package/build/index.mjs +94 -75
- package/package.json +87 -87
- package/types.d.ts +10 -11
package/build/index.cjs
CHANGED
|
@@ -140,13 +140,6 @@ const columns$3 = [
|
|
|
140
140
|
? `${await backtestKit.formatPrice(symbol, Number(v))} USD`
|
|
141
141
|
: "N/A",
|
|
142
142
|
},
|
|
143
|
-
{
|
|
144
|
-
key: "atr14_raw",
|
|
145
|
-
label: "ATR(14) Raw",
|
|
146
|
-
format: async (v, symbol) => v !== null
|
|
147
|
-
? `${await backtestKit.formatPrice(symbol, Number(v))} USD`
|
|
148
|
-
: "N/A",
|
|
149
|
-
},
|
|
150
143
|
{
|
|
151
144
|
key: "atr20",
|
|
152
145
|
label: "ATR(20)",
|
|
@@ -209,6 +202,11 @@ const columns$3 = [
|
|
|
209
202
|
? `${await backtestKit.formatPrice(symbol, Number(v))} USD`
|
|
210
203
|
: "N/A",
|
|
211
204
|
},
|
|
205
|
+
{
|
|
206
|
+
key: "volumeTrendRatio",
|
|
207
|
+
label: "Volume Trend Ratio",
|
|
208
|
+
format: (v) => (v !== null ? `${Number(v).toFixed(2)}x` : "N/A"),
|
|
209
|
+
},
|
|
212
210
|
{
|
|
213
211
|
key: "currentPrice",
|
|
214
212
|
label: "Current Price",
|
|
@@ -343,8 +341,8 @@ function calculateFibonacciLevels$2(candles, endIndex) {
|
|
|
343
341
|
"61.8%": high - range * 0.618,
|
|
344
342
|
"78.6%": high - range * 0.786,
|
|
345
343
|
"100.0%": low,
|
|
346
|
-
"127.2%": high - range * 1.272,
|
|
347
|
-
"161.8%": high - range * 1.618,
|
|
344
|
+
"127.2% (downside)": high - range * 1.272,
|
|
345
|
+
"161.8% (downside)": high - range * 1.618,
|
|
348
346
|
};
|
|
349
347
|
const currentPrice = Number(candles[endIndex].close);
|
|
350
348
|
let nearestLevel = {
|
|
@@ -438,20 +436,19 @@ function generateAnalysis$3(symbol, candles) {
|
|
|
438
436
|
if (i < WARMUP_PERIOD$3) {
|
|
439
437
|
return;
|
|
440
438
|
}
|
|
441
|
-
// Volume trend calculation
|
|
442
|
-
const volumeSma6 = new tradingSignals.FasterSMA(6);
|
|
443
|
-
const volumeSma6Prev = new tradingSignals.FasterSMA(6);
|
|
439
|
+
// Volume trend calculation: average of last 6 candles vs previous 6
|
|
444
440
|
const volumeStart = Math.max(0, i + 1 - 12);
|
|
445
441
|
const prevVolumeData = volumes.slice(volumeStart, Math.min(volumeStart + 6, i + 1));
|
|
446
442
|
const recentVolumeData = volumes.slice(Math.max(0, i + 1 - 6), i + 1);
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
const
|
|
443
|
+
const averageOf = (values) => {
|
|
444
|
+
const safe = values.filter((value) => !isUnsafe$4(value));
|
|
445
|
+
if (safe.length === 0) {
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
return safe.reduce((sum, value) => sum + value, 0) / safe.length;
|
|
449
|
+
};
|
|
450
|
+
const recentVolumeRaw = averageOf(recentVolumeData);
|
|
451
|
+
const prevVolumeRaw = averageOf(prevVolumeData);
|
|
455
452
|
const recentVolume = !isUnsafe$4(recentVolumeRaw)
|
|
456
453
|
? recentVolumeRaw
|
|
457
454
|
: volumes[i];
|
|
@@ -503,9 +500,6 @@ function generateAnalysis$3(symbol, candles) {
|
|
|
503
500
|
atr14: atr14.getResult() != null && !isUnsafe$4(atr14.getResult())
|
|
504
501
|
? atr14.getResult()
|
|
505
502
|
: null,
|
|
506
|
-
atr14_raw: atr14.getResult() != null && !isUnsafe$4(atr14.getResult())
|
|
507
|
-
? atr14.getResult()
|
|
508
|
-
: null,
|
|
509
503
|
atr20: atr20.getResult() != null && !isUnsafe$4(atr20.getResult())
|
|
510
504
|
? atr20.getResult()
|
|
511
505
|
: null,
|
|
@@ -556,11 +550,12 @@ function generateAnalysis$3(symbol, candles) {
|
|
|
556
550
|
fibonacciDistance: fibonacciNearest.distance,
|
|
557
551
|
bodySize,
|
|
558
552
|
closePrice: close,
|
|
559
|
-
date: new Date(),
|
|
553
|
+
date: new Date(_candle.timestamp),
|
|
560
554
|
lookbackPeriod: "48 candles (48 hours) with SMA(50) from 100 hours",
|
|
561
555
|
});
|
|
562
556
|
});
|
|
563
|
-
|
|
557
|
+
// Return only the last TABLE_ROWS_LIMIT rows
|
|
558
|
+
return results.slice(-TABLE_ROWS_LIMIT$3);
|
|
564
559
|
}
|
|
565
560
|
/**
|
|
566
561
|
* Generates markdown table with long-term technical analysis history.
|
|
@@ -623,9 +618,7 @@ async function generateHistoryTable$3(indicators, symbol) {
|
|
|
623
618
|
markdown +=
|
|
624
619
|
"- **ATR(14)**: over previous 14 candles (14 hours on 1h timeframe) before row timestamp (Min: 0 USD, Max: +∞)\n";
|
|
625
620
|
markdown +=
|
|
626
|
-
"- **ATR(
|
|
627
|
-
markdown +=
|
|
628
|
-
"- **ATR(20) Raw**: raw value over previous 20 candles (20 hours on 1h timeframe) before row timestamp (Min: 0 USD, Max: +∞)\n";
|
|
621
|
+
"- **ATR(20)**: over previous 20 candles (20 hours on 1h timeframe) before row timestamp (Min: 0 USD, Max: +∞)\n";
|
|
629
622
|
markdown +=
|
|
630
623
|
"- **CCI(20)**: over previous 20 candles (20 hours on 1h timeframe) before row timestamp (Min: -∞, Max: +∞)\n";
|
|
631
624
|
markdown +=
|
|
@@ -650,12 +643,14 @@ async function generateHistoryTable$3(indicators, symbol) {
|
|
|
650
643
|
"- **EMA(34)**: over previous 34 candles (34 hours on 1h timeframe) before row timestamp (Min: 0 USD, Max: +∞ USD)\n";
|
|
651
644
|
markdown +=
|
|
652
645
|
"- **Momentum(10)**: over previous 10 candles (10 hours on 1h timeframe) before row timestamp (Min: -∞ USD, Max: +∞ USD)\n";
|
|
646
|
+
markdown +=
|
|
647
|
+
"- **Volume Trend Ratio**: average volume of last 6 candles relative to previous 6 candles before row timestamp (Min: 0x, Max: +∞x; above 1x = volume increasing)\n";
|
|
653
648
|
markdown +=
|
|
654
649
|
"- **Support**: over previous 4 candles (4 hours on 1h timeframe) before row timestamp (Min: 0 USD, Max: +∞ USD)\n";
|
|
655
650
|
markdown +=
|
|
656
651
|
"- **Resistance**: over previous 4 candles (4 hours on 1h timeframe) before row timestamp (Min: 0 USD, Max: +∞ USD)\n";
|
|
657
652
|
markdown +=
|
|
658
|
-
"- **Fibonacci Nearest Level**: nearest level name before row timestamp\n";
|
|
653
|
+
"- **Fibonacci Nearest Level**: nearest level name before row timestamp; levels marked (downside) lie below the range low\n";
|
|
659
654
|
markdown +=
|
|
660
655
|
"- **Fibonacci Nearest Price**: nearest price level before row timestamp (Min: 0 USD, Max: +∞ USD)\n";
|
|
661
656
|
markdown +=
|
|
@@ -770,9 +765,7 @@ class LongTermHistoryService {
|
|
|
770
765
|
this.getReport = async (symbol) => {
|
|
771
766
|
this.loggerService.log("longTermHistoryService getReport", { symbol });
|
|
772
767
|
const fullCandles = await backtestKit.getCandles(symbol, "1h", 100);
|
|
773
|
-
|
|
774
|
-
const allRows = await this.getData(symbol, fullCandles);
|
|
775
|
-
const rows = allRows.slice(-TABLE_ROWS_LIMIT$3);
|
|
768
|
+
const rows = await this.getData(symbol, fullCandles);
|
|
776
769
|
return generateHistoryTable$3(rows, symbol);
|
|
777
770
|
};
|
|
778
771
|
/**
|
|
@@ -1068,6 +1061,11 @@ const columns$2 = [
|
|
|
1068
1061
|
label: "Volume Ratio",
|
|
1069
1062
|
format: (v) => (v !== null ? `${Number(v).toFixed(2)}x` : "N/A"),
|
|
1070
1063
|
},
|
|
1064
|
+
{
|
|
1065
|
+
key: "volumeTrendRatio",
|
|
1066
|
+
label: "Volume Trend Ratio",
|
|
1067
|
+
format: (v) => (v !== null ? `${Number(v).toFixed(2)}x` : "N/A"),
|
|
1068
|
+
},
|
|
1071
1069
|
{
|
|
1072
1070
|
key: "volatility5",
|
|
1073
1071
|
label: "Volatility(5)",
|
|
@@ -1157,9 +1155,11 @@ function calculateVolumeMetrics(candles, endIndex) {
|
|
|
1157
1155
|
const volumeSma5 = new tradingSignals.FasterSMA(5);
|
|
1158
1156
|
volumes.forEach((vol) => volumeSma5.update(vol, false));
|
|
1159
1157
|
const avgVolumeRaw = volumeSma5.getResult();
|
|
1160
|
-
const avgVolume = !isUnsafe$3(avgVolumeRaw) ? avgVolumeRaw :
|
|
1158
|
+
const avgVolume = !isUnsafe$3(avgVolumeRaw) ? avgVolumeRaw : null;
|
|
1161
1159
|
const currentVolume = volumes[volumes.length - 1];
|
|
1162
|
-
const volumeRatio = avgVolume > 0 && !isUnsafe$3(currentVolume)
|
|
1160
|
+
const volumeRatio = avgVolume !== null && avgVolume > 0 && !isUnsafe$3(currentVolume)
|
|
1161
|
+
? currentVolume / avgVolume
|
|
1162
|
+
: null;
|
|
1163
1163
|
let volumeTrendRatio = null;
|
|
1164
1164
|
if (volumes.length >= 6) {
|
|
1165
1165
|
const recent3 = volumes.slice(-3);
|
|
@@ -1196,15 +1196,15 @@ function calculatePriceChanges(candles, endIndex) {
|
|
|
1196
1196
|
return { priceChange1m: null, priceChange3m: null, priceChange5m: null };
|
|
1197
1197
|
}
|
|
1198
1198
|
const current = closes[closes.length - 1];
|
|
1199
|
-
const priceChange1m = closes.length >= 2
|
|
1199
|
+
const priceChange1m = closes.length >= 2 && closes[closes.length - 2] > 0
|
|
1200
1200
|
? ((current - closes[closes.length - 2]) / closes[closes.length - 2]) *
|
|
1201
1201
|
100
|
|
1202
1202
|
: null;
|
|
1203
|
-
const priceChange3m = closes.length >= 4
|
|
1203
|
+
const priceChange3m = closes.length >= 4 && closes[closes.length - 4] > 0
|
|
1204
1204
|
? ((current - closes[closes.length - 4]) / closes[closes.length - 4]) *
|
|
1205
1205
|
100
|
|
1206
1206
|
: null;
|
|
1207
|
-
const priceChange5m = closes.length >= 6
|
|
1207
|
+
const priceChange5m = closes.length >= 6 && closes[closes.length - 6] > 0
|
|
1208
1208
|
? ((current - closes[closes.length - 6]) / closes[closes.length - 6]) *
|
|
1209
1209
|
100
|
|
1210
1210
|
: null;
|
|
@@ -1350,9 +1350,9 @@ function generateAnalysis$2(symbol, candles) {
|
|
|
1350
1350
|
sma8.update(close, false);
|
|
1351
1351
|
dema8.update(close, false);
|
|
1352
1352
|
wma5.update(close, false);
|
|
1353
|
-
//
|
|
1354
|
-
// EMA(21)
|
|
1355
|
-
//
|
|
1353
|
+
// Skip rows until the primary indicators are warmed up.
|
|
1354
|
+
// WARMUP_PERIOD covers EMA(21); slower indicators (e.g. StochRSI(14)
|
|
1355
|
+
// needs ~28 candles) render as N/A in the first rows until ready.
|
|
1356
1356
|
if (i < WARMUP_PERIOD$2) {
|
|
1357
1357
|
return;
|
|
1358
1358
|
}
|
|
@@ -1523,7 +1523,7 @@ function generateAnalysis$2(symbol, candles) {
|
|
|
1523
1523
|
squeezeMomentum,
|
|
1524
1524
|
pressureIndex,
|
|
1525
1525
|
closePrice: close,
|
|
1526
|
-
date: new Date(),
|
|
1526
|
+
date: new Date(_candle.timestamp),
|
|
1527
1527
|
lookbackPeriod: "60 candles (60 minutes)",
|
|
1528
1528
|
});
|
|
1529
1529
|
});
|
|
@@ -1609,7 +1609,7 @@ async function generateHistoryTable$2(indicators, symbol) {
|
|
|
1609
1609
|
markdown +=
|
|
1610
1610
|
"- **Bollinger Width(8,2.0)**: width percentage before row timestamp (Min: 0%, Max: +∞)\n";
|
|
1611
1611
|
markdown +=
|
|
1612
|
-
"- **Bollinger Position**: price position within bands before row timestamp (
|
|
1612
|
+
"- **Bollinger Position**: price position within bands before row timestamp (0% = lower band, 100% = upper band; can go below 0% or above 100% when price is outside the bands)\n";
|
|
1613
1613
|
markdown +=
|
|
1614
1614
|
"- **Stochastic K(3,3,3)**: over previous 3 candles (3 minutes on 1m timeframe) before row timestamp (Min: 0, Max: 100)\n";
|
|
1615
1615
|
markdown +=
|
|
@@ -1646,6 +1646,8 @@ async function generateHistoryTable$2(indicators, symbol) {
|
|
|
1646
1646
|
"- **Volume SMA(5)**: over previous 5 candles (5 minutes on 1m timeframe) before row timestamp (Min: 0, Max: +∞)\n";
|
|
1647
1647
|
markdown +=
|
|
1648
1648
|
"- **Volume Ratio**: volume relative to average at row timestamp (Min: 0x, Max: +∞x)\n";
|
|
1649
|
+
markdown +=
|
|
1650
|
+
"- **Volume Trend Ratio**: average volume of last 3 candles relative to previous 3 candles at row timestamp (Min: 0x, Max: +∞x; above 1x = volume increasing)\n";
|
|
1649
1651
|
markdown +=
|
|
1650
1652
|
"- **Support**: over previous 30 candles (30 minutes on 1m timeframe) before row timestamp (Min: 0 USD, Max: +∞ USD)\n";
|
|
1651
1653
|
markdown +=
|
|
@@ -1896,6 +1898,11 @@ const columns$1 = [
|
|
|
1896
1898
|
label: "ROC(10) - Rate of Change %",
|
|
1897
1899
|
format: (v) => (v !== null ? `${Number(v).toFixed(3)}%` : "N/A"),
|
|
1898
1900
|
},
|
|
1901
|
+
{
|
|
1902
|
+
key: "volumeTrendRatio",
|
|
1903
|
+
label: "Volume Trend Ratio",
|
|
1904
|
+
format: (v) => (v !== null ? `${Number(v).toFixed(2)}x` : "N/A"),
|
|
1905
|
+
},
|
|
1899
1906
|
{
|
|
1900
1907
|
key: "sma50",
|
|
1901
1908
|
label: "SMA(50)",
|
|
@@ -2070,8 +2077,8 @@ function calculateFibonacciLevels$1(candles, endIndex) {
|
|
|
2070
2077
|
"61.8%": high - range * 0.618,
|
|
2071
2078
|
"78.6%": high - range * 0.786,
|
|
2072
2079
|
"100.0%": low,
|
|
2073
|
-
"127.2%": high - range * 1.272,
|
|
2074
|
-
"161.8%": high - range * 1.618,
|
|
2080
|
+
"127.2% (downside)": high - range * 1.272,
|
|
2081
|
+
"161.8% (downside)": high - range * 1.618,
|
|
2075
2082
|
};
|
|
2076
2083
|
const currentPrice = Number(candles[endIndex].close);
|
|
2077
2084
|
let nearestLevel = {
|
|
@@ -2250,7 +2257,8 @@ function generateAnalysis$1(symbol, candles) {
|
|
|
2250
2257
|
bollingerWidth10_2: bollingerResult &&
|
|
2251
2258
|
!isUnsafe$2(bollingerResult.upper) &&
|
|
2252
2259
|
!isUnsafe$2(bollingerResult.lower) &&
|
|
2253
|
-
!isUnsafe$2(bollingerResult.middle)
|
|
2260
|
+
!isUnsafe$2(bollingerResult.middle) &&
|
|
2261
|
+
bollingerResult.middle !== 0
|
|
2254
2262
|
? ((bollingerResult.upper - bollingerResult.lower) /
|
|
2255
2263
|
bollingerResult.middle) *
|
|
2256
2264
|
100
|
|
@@ -2311,7 +2319,7 @@ function generateAnalysis$1(symbol, candles) {
|
|
|
2311
2319
|
fibonacciDistance: fibonacciNearest.distance,
|
|
2312
2320
|
bodySize,
|
|
2313
2321
|
closePrice: close,
|
|
2314
|
-
date: new Date(),
|
|
2322
|
+
date: new Date(_candle.timestamp),
|
|
2315
2323
|
lookbackPeriod: "144 candles (36 hours)",
|
|
2316
2324
|
});
|
|
2317
2325
|
});
|
|
@@ -2398,6 +2406,8 @@ async function generateHistoryTable$1(indicators, symbol) {
|
|
|
2398
2406
|
"- **ROC(5)**: over previous 5 candles (75 minutes on 15m timeframe) before row timestamp (Min: -∞%, Max: +∞%)\n";
|
|
2399
2407
|
markdown +=
|
|
2400
2408
|
"- **ROC(10)**: over previous 10 candles (150 minutes on 15m timeframe) before row timestamp (Min: -∞%, Max: +∞%)\n";
|
|
2409
|
+
markdown +=
|
|
2410
|
+
"- **Volume Trend Ratio**: average volume of last 8 candles relative to previous 8 candles before row timestamp (Min: 0x, Max: +∞x; above 1x = volume increasing)\n";
|
|
2401
2411
|
markdown +=
|
|
2402
2412
|
"- **SMA(50)**: over previous 50 candles (750 minutes on 15m timeframe) before row timestamp (Min: 0 USD, Max: +∞ USD)\n";
|
|
2403
2413
|
markdown +=
|
|
@@ -2413,9 +2423,9 @@ async function generateHistoryTable$1(indicators, symbol) {
|
|
|
2413
2423
|
markdown +=
|
|
2414
2424
|
"- **Resistance**: over previous 48 candles (12 hours on 15m timeframe) before row timestamp (Min: 0 USD, Max: +∞ USD)\n";
|
|
2415
2425
|
markdown +=
|
|
2416
|
-
"- **Fibonacci Nearest Level**: nearest level name before row timestamp\n";
|
|
2426
|
+
"- **Fibonacci Nearest Level**: nearest level name before row timestamp; levels marked (downside) lie below the range low\n";
|
|
2417
2427
|
markdown +=
|
|
2418
|
-
"- **Fibonacci Nearest Price**: nearest price level over 288 candles (
|
|
2428
|
+
"- **Fibonacci Nearest Price**: nearest price level over up to 288 candles (limited by the 144-candle / 36h report history) before row timestamp (Min: 0 USD, Max: +∞ USD)\n";
|
|
2419
2429
|
markdown +=
|
|
2420
2430
|
"- **Fibonacci Distance**: distance to nearest level before row timestamp (Min: 0 USD, Max: +∞ USD)\n";
|
|
2421
2431
|
markdown +=
|
|
@@ -2570,6 +2580,11 @@ const TABLE_ROWS_LIMIT = 30;
|
|
|
2570
2580
|
* Ensures all technical indicators (especially EMA(34)) have sufficient data.
|
|
2571
2581
|
*/
|
|
2572
2582
|
const WARMUP_PERIOD = 34;
|
|
2583
|
+
/**
|
|
2584
|
+
* Rolling window (in candles) for the volatility calculation.
|
|
2585
|
+
* Keeps the metric comparable across rows regardless of total history length.
|
|
2586
|
+
*/
|
|
2587
|
+
const VOLATILITY_WINDOW = 20;
|
|
2573
2588
|
const columns = [
|
|
2574
2589
|
{
|
|
2575
2590
|
key: "rsi14",
|
|
@@ -3003,16 +3018,20 @@ function generateAnalysis(symbol, candles) {
|
|
|
3003
3018
|
if (i < WARMUP_PERIOD) {
|
|
3004
3019
|
return;
|
|
3005
3020
|
}
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
const
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3021
|
+
let volatilitySumSq = 0;
|
|
3022
|
+
let volatilityCount = 0;
|
|
3023
|
+
for (let idx = Math.max(1, i + 1 - VOLATILITY_WINDOW); idx <= i; idx++) {
|
|
3024
|
+
const prevPrice = closes[idx - 1];
|
|
3025
|
+
const currPrice = closes[idx];
|
|
3026
|
+
if (isUnsafe$1(prevPrice) || isUnsafe$1(currPrice) || prevPrice <= 0) {
|
|
3027
|
+
continue;
|
|
3028
|
+
}
|
|
3029
|
+
const change = ((currPrice - prevPrice) / prevPrice) * 100;
|
|
3030
|
+
volatilitySumSq += change ** 2;
|
|
3031
|
+
volatilityCount += 1;
|
|
3032
|
+
}
|
|
3033
|
+
const volatility = volatilityCount > 0
|
|
3034
|
+
? Math.sqrt(volatilitySumSq / volatilityCount)
|
|
3016
3035
|
: null;
|
|
3017
3036
|
const { support, resistance } = calculateSupportResistance(candles, i);
|
|
3018
3037
|
const fibonacci = calculateFibonacciLevels(candles, i);
|
|
@@ -3108,7 +3127,7 @@ function generateAnalysis(symbol, candles) {
|
|
|
3108
3127
|
fibonacciPositionPercent: fibonacci.fibonacciPositionPercent,
|
|
3109
3128
|
bodySize,
|
|
3110
3129
|
closePrice: close,
|
|
3111
|
-
date: new Date(),
|
|
3130
|
+
date: new Date(_candle.timestamp),
|
|
3112
3131
|
lookbackPeriod: "96 candles (48 hours)",
|
|
3113
3132
|
});
|
|
3114
3133
|
});
|
|
@@ -3222,7 +3241,7 @@ async function generateHistoryTable(indicators, symbol) {
|
|
|
3222
3241
|
markdown +=
|
|
3223
3242
|
"- **Volume**: trading volume at row timestamp (Min: 0, Max: +∞)\n";
|
|
3224
3243
|
markdown +=
|
|
3225
|
-
"- **Volatility**:
|
|
3244
|
+
"- **Volatility**: RMS of 1-candle percentage changes over previous 20 candles (10 hours on 30m timeframe) before row timestamp (Min: 0%, Max: +∞)\n";
|
|
3226
3245
|
markdown += "\n## Column Descriptions\n\n";
|
|
3227
3246
|
markdown += "**RSI(14) - Relative Strength Index**: Momentum oscillator measuring the speed and magnitude of price changes on 30-minute timeframe. Values above 70 indicate overbought conditions suitable for profit-taking, below 30 indicate oversold conditions suitable for entries.\n\n";
|
|
3228
3247
|
markdown += "**StochRSI(14) - RSI Oscillator**: Applies Stochastic calculation to RSI values, providing more sensitive overbought/oversold signals for swing trading. Range 0-100, with 80+ being overbought and 20- being oversold.\n\n";
|
|
@@ -3627,11 +3646,11 @@ class HourCandleHistoryService {
|
|
|
3627
3646
|
markdown += `### 1h Candle ${index + 1}\n`;
|
|
3628
3647
|
markdown += `- **Price Change**: ${priceChangePercent.toFixed(3)}%\n`;
|
|
3629
3648
|
markdown += `- **Time**: ${formattedTime}\n`;
|
|
3630
|
-
markdown += `- **Open**: ${backtestKit.formatPrice(symbol, candle.open)} USD\n`;
|
|
3631
|
-
markdown += `- **High**: ${backtestKit.formatPrice(symbol, candle.high)} USD\n`;
|
|
3632
|
-
markdown += `- **Low**: ${backtestKit.formatPrice(symbol, candle.low)} USD\n`;
|
|
3633
|
-
markdown += `- **Close**: ${backtestKit.formatPrice(symbol, candle.close)} USD\n`;
|
|
3634
|
-
markdown += `- **Volume**: ${backtestKit.formatQuantity(symbol, candle.volume)}\n`;
|
|
3649
|
+
markdown += `- **Open**: ${await backtestKit.formatPrice(symbol, candle.open)} USD\n`;
|
|
3650
|
+
markdown += `- **High**: ${await backtestKit.formatPrice(symbol, candle.high)} USD\n`;
|
|
3651
|
+
markdown += `- **Low**: ${await backtestKit.formatPrice(symbol, candle.low)} USD\n`;
|
|
3652
|
+
markdown += `- **Close**: ${await backtestKit.formatPrice(symbol, candle.close)} USD\n`;
|
|
3653
|
+
markdown += `- **Volume**: ${await backtestKit.formatQuantity(symbol, candle.volume)}\n`;
|
|
3635
3654
|
markdown += `- **1h Volatility**: ${volatilityPercent.toFixed(2)}%\n`;
|
|
3636
3655
|
markdown += `- **Body Size**: ${bodyPercent.toFixed(1)}%\n\n`;
|
|
3637
3656
|
}
|
|
@@ -3781,7 +3800,7 @@ class OneMinuteCandleHistoryService {
|
|
|
3781
3800
|
markdown += `- **High**: ${await backtestKit.formatPrice(symbol, candle.high)} USD\n`;
|
|
3782
3801
|
markdown += `- **Low**: ${await backtestKit.formatPrice(symbol, candle.low)} USD\n`;
|
|
3783
3802
|
markdown += `- **Close**: ${await backtestKit.formatPrice(symbol, candle.close)} USD\n`;
|
|
3784
|
-
markdown += `- **Volume**: ${backtestKit.formatQuantity(symbol, candle.volume)}\n`;
|
|
3803
|
+
markdown += `- **Volume**: ${await backtestKit.formatQuantity(symbol, candle.volume)}\n`;
|
|
3785
3804
|
markdown += `- **1m Volatility**: ${volatilityPercent.toFixed(2)}%\n`;
|
|
3786
3805
|
markdown += `- **Body Size**: ${bodyPercent.toFixed(1)}%\n\n`;
|
|
3787
3806
|
}
|
|
@@ -3931,7 +3950,7 @@ class ThirtyMinuteCandleHistoryService {
|
|
|
3931
3950
|
report += `- **High**: ${await backtestKit.formatPrice(symbol, candle.high)} USD\n`;
|
|
3932
3951
|
report += `- **Low**: ${await backtestKit.formatPrice(symbol, candle.low)} USD\n`;
|
|
3933
3952
|
report += `- **Close**: ${await backtestKit.formatPrice(symbol, candle.close)} USD\n`;
|
|
3934
|
-
report += `- **Volume**: ${backtestKit.formatQuantity(symbol, candle.volume)}\n`;
|
|
3953
|
+
report += `- **Volume**: ${await backtestKit.formatQuantity(symbol, candle.volume)}\n`;
|
|
3935
3954
|
report += `- **30m Volatility**: ${volatilityPercent.toFixed(2)}%\n`;
|
|
3936
3955
|
report += `- **Body Size**: ${bodyPercent.toFixed(1)}%\n\n`;
|
|
3937
3956
|
}
|
|
@@ -4262,18 +4281,18 @@ class BookDataMathService {
|
|
|
4262
4281
|
});
|
|
4263
4282
|
const depth = await backtestKit.getOrderBook(symbol, MAX_DEPTH_LEVELS);
|
|
4264
4283
|
// Just process raw data - no calculations
|
|
4265
|
-
const bids = processOrderBookSide(depth.bids.sort((a, b) => parseFloat(b.price) - parseFloat(a.price))); // Сортировка по убыванию
|
|
4266
|
-
const asks = processOrderBookSide(depth.asks.sort((a, b) => parseFloat(a.price) - parseFloat(b.price))); // Сортировка по возрастанию
|
|
4267
|
-
const bestBid = bids.length > 0 ? bids[0].price :
|
|
4268
|
-
const bestAsk = asks.length > 0 ? asks[0].price :
|
|
4269
|
-
const midPrice = (bestBid + bestAsk) / 2;
|
|
4270
|
-
const spread = bestAsk - bestBid;
|
|
4284
|
+
const bids = processOrderBookSide([...depth.bids].sort((a, b) => parseFloat(b.price) - parseFloat(a.price))); // Сортировка по убыванию
|
|
4285
|
+
const asks = processOrderBookSide([...depth.asks].sort((a, b) => parseFloat(a.price) - parseFloat(b.price))); // Сортировка по возрастанию
|
|
4286
|
+
const bestBid = bids.length > 0 ? bids[0].price : null;
|
|
4287
|
+
const bestAsk = asks.length > 0 ? asks[0].price : null;
|
|
4288
|
+
const midPrice = bestBid !== null && bestAsk !== null ? (bestBid + bestAsk) / 2 : null;
|
|
4289
|
+
const spread = bestBid !== null && bestAsk !== null ? bestAsk - bestBid : null;
|
|
4271
4290
|
// Calculate depth imbalance
|
|
4272
4291
|
const totalBidVolume = bids.reduce((sum, bid) => sum + bid.quantity, 0);
|
|
4273
4292
|
const totalAskVolume = asks.reduce((sum, ask) => sum + ask.quantity, 0);
|
|
4274
4293
|
const depthImbalance = totalBidVolume + totalAskVolume > 0
|
|
4275
4294
|
? (totalBidVolume - totalAskVolume) / (totalBidVolume + totalAskVolume)
|
|
4276
|
-
:
|
|
4295
|
+
: null;
|
|
4277
4296
|
return {
|
|
4278
4297
|
symbol,
|
|
4279
4298
|
timestamp: new Date().toISOString(),
|
|
@@ -5026,7 +5045,7 @@ const commitHistorySetup = async (symbol, history) => {
|
|
|
5026
5045
|
await commitShortTermMath(symbol, history);
|
|
5027
5046
|
await commitSwingTermMath(symbol, history);
|
|
5028
5047
|
await commitLongTermMath(symbol, history);
|
|
5029
|
-
const displayName =
|
|
5048
|
+
const displayName = String(symbol).toUpperCase();
|
|
5030
5049
|
const currentPrice = await backtestKit.getAveragePrice(symbol);
|
|
5031
5050
|
const currentData = await backtestKit.getDate();
|
|
5032
5051
|
await history.push({
|