Update v5.10: Fix startup bugs, add Pin Bar, dynamic volatility filter, and trailing step safeguards

This commit is contained in:
anggol23
2026-05-25 08:29:56 +07:00
parent 633d4ce71a
commit b90358be65
2 changed files with 105 additions and 22 deletions
+21
View File
@@ -14,6 +14,7 @@
- [Arsitektur & Penjelasan Kode](#arsitektur--penjelasan-kode)
- [Sistem Proteksi Anti-MC](#sistem-proteksi-anti-mc)
- [Cara Backtest](#cara-backtest)
- [Pembaruan Terakhir (Changelog)](#pembaruan-terakhir-changelog)
- [Disclaimer](#disclaimer)
---
@@ -290,6 +291,26 @@ Ditambah proteksi entry:
---
## Pembaruan Terakhir (Changelog v5.10)
EA ini telah diperbarui untuk meningkatkan stabilitas sistem (*system safeguard*), profitabilitas strategi, serta keamanan transaksi saat digunakan langsung (*live trading*). Berikut detail pembaruan:
### 1. Perbaikan Bug Stabilitas Sistem
* **Fix Double-Counting OnTrade:** Variabel penanda transaksi `lastDealsTotal` dipindahkan dari variabel lokal `static` menjadi variabel global dan diinisialisasi secara tepat pada `OnInit()`. Hal ini menghentikan bug pelacakan kekalahan beruntun (*consecLosses*) yang terhitung ganda secara keliru saat EA pertama kali berjalan.
* **Filter Entry Tengah Bar:** Nilai awal `lastSwingBar` diinisialisasi dengan open time bar berjalan saat EA dipasang. EA kini akan menunggu pembentukan bar baru selesai sebelum mengevaluasi sinyal entry, menghindari masuk posisi secara acak di tengah bar.
* **Lookback Kekalahan Beruntun:** Fungsi `CountRecentLosses()` sekarang memindai history transaksi 30 hari ke belakang (sebelumnya hanya awal hari ini). Membuat fitur pemotongan risiko otomatis saat beruntun rugi (*dynamic risk reduction*) bekerja akurat lintas hari.
### 2. Peningkatan Profitabilitas (Swing & Scalp)
* **Integrasi Sinyal Pin Bar (Swing):** Menambahkan deteksi otomatis pola rejection candle **Pin Bar** (Hammer/Shooting Star) sebagai pemicu alternatif untuk entry swing. EA kini dapat mengambil peluang pembalikan arah harga (*pullback*) yang kuat pada level support/resistance utama.
* **Optimasi RSI untuk Pullback Swing:** Melonggarkan filter RSI ke batas bawah **35.0** (Buy) dan batas atas **65.0** (Sell) saat tren dikonfirmasi kuat. Memberikan lebih banyak peluang bagi EA untuk melakukan entry pada koreksi harga yang sehat.
* **Filter Volatilitas Dinamis (Adaptif):** Mengganti filter statis `30 points` yang kaku dengan pemeriksaan dinamis `IsVolatilityOk()`. EA membandingkan volatilitas saat ini dengan rata-rata 50 bar terakhir. EA akan memblokir entry jika pasar mati/sepi (di bawah 60% rata-rata) demi menghindari biaya spread broker yang lebar.
### 3. Pengamanan Transaksi Live (Anti-Spam Broker)
* **Trailing Step Dinamis:** Menghindari pengiriman request modifikasi Stop Loss (SL) secara terus-menerus ke server broker (*broker spamming*). Trailing stop kini diatur dengan langkah dinamis minimal **10% ATR** untuk Swing dan **5% ATR** untuk Scalping.
* **Koreksi Validasi SL/TP:** Meningkatkan fungsi `ValidateSLTP()` agar tidak terjadi penolakan order oleh broker saat EA memproses transaksi tanpa SL/TP statis (SL/TP nol).
---
## Disclaimer
> **PERINGATAN**: Trading forex/CFD memiliki risiko tinggi. EA ini **TIDAK menjamin profit**. Past performance (backtest) TIDAK menjamin hasil di masa depan. Selalu test di **akun demo** terlebih dahulu. Gunakan modal yang siap Anda risikokan. Pembuat tidak bertanggung jawab atas kerugian yang terjadi.
+84 -22
View File
@@ -107,6 +107,7 @@ datetime lastSwingBar = 0;
datetime lastLossTime = 0;
int consecLosses = 0;
datetime lastTradeDay = 0; // Track hari terakhir untuk OnTrade reset
int lastDealsTotal = 0; // Track total deal terakhir untuk OnTrade
string gvPeakEquity;
@@ -218,6 +219,12 @@ int OnInit()
CountRecentLosses();
ParseNewsHours();
// Inisialisasi tracking bar dan trade history untuk mencegah double-counting & entry tengah bar saat startup
lastSwingBar = iTime(_Symbol, _Period, 0);
lastTradeDay = iTime(_Symbol, PERIOD_D1, 0);
HistorySelect(lastTradeDay, TimeCurrent());
lastDealsTotal = HistoryDealsTotal();
Print("HUP v5.0 DUAL ENGINE | Filling=", EnumToString(DetectFilling()),
" | Scalp=", EnableScalp ? "ON" : "OFF",
" | ConsecLoss=", consecLosses);
@@ -242,8 +249,8 @@ void OnDeinit(const int reason)
void CountRecentLosses()
{
consecLosses = 0;
datetime dayStart = iTime(_Symbol, PERIOD_D1, 0);
HistorySelect(dayStart, TimeCurrent());
// Scan history 30 hari ke belakang untuk consec losses lintas hari
HistorySelect(TimeCurrent() - 30 * 24 * 3600, TimeCurrent());
int total = HistoryDealsTotal();
for(int i = total - 1; i >= 0; i--)
@@ -513,7 +520,7 @@ bool LoadIndicators()
if(CopyBuffer(adx_handle, 1, 0, 3, plus_di) < 3) return false;
if(CopyBuffer(adx_handle, 2, 0, 3, minus_di) < 3) return false;
if(CopyBuffer(rsi_handle, 0, 0, 3, rsi_val) < 3) return false;
if(CopyBuffer(atr_handle, 0, 0, 3, atr_val) < 3) return false;
if(CopyBuffer(atr_handle, 0, 0, 50, atr_val) < 50) return false;
if(CopyBuffer(bb_handle, 1, 0, 3, bb_upper) < 3) return false; // Upper
if(CopyBuffer(bb_handle, 2, 0, 3, bb_lower) < 3) return false; // Lower
if(CopyBuffer(bb_handle, 0, 0, 3, bb_mid) < 3) return false; // Middle
@@ -521,6 +528,20 @@ bool LoadIndicators()
return true;
}
//============================================================
// VOLATILITY FILTER (DYNAMIC ATR)
//============================================================
bool IsVolatilityOk()
{
int totalATR = ArraySize(atr_val);
if(totalATR < 10) return true; // fallback jika data kurang
double sum = 0;
for(int i = 0; i < totalATR; i++) sum += atr_val[i];
double avgATR = sum / totalATR;
// Volatilitas OK jika ATR saat ini >= 60% dari rata-rata 50 bar terakhir
return (atr_val[1] >= avgATR * 0.6);
}
//============================================================
// LOT CALCULATION
//============================================================
@@ -560,11 +581,11 @@ bool ValidateSLTP(double price, double sl, double tp, int direction)
{
if(direction == +1)
{
if((price - sl) < minDist || (tp - price) < minDist) return false;
if((sl > 0 && (price - sl) < minDist) || (tp > 0 && (tp - price) < minDist)) return false;
}
else
{
if((sl - price) < minDist || (price - tp) < minDist) return false;
if((sl > 0 && (sl - price) < minDist) || (tp > 0 && (price - tp) < minDist)) return false;
}
}
@@ -575,8 +596,8 @@ bool ValidateSLTP(double price, double sl, double tp, int direction)
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(direction == +1 && (ask - sl) < freezeDist) return false;
if(direction == -1 && (sl - bid) < freezeDist) return false;
if(direction == +1 && sl > 0 && (ask - sl) < freezeDist) return false;
if(direction == -1 && sl > 0 && (sl - bid) < freezeDist) return false;
}
return true;
}
@@ -600,6 +621,36 @@ int GetTrend()
bool TrendStrong() { return adx_val[0] >= ADX_Min; }
//============================================================
// DETECT PIN BAR PATTERN
//============================================================
bool IsPinBar(int barIndex, int direction)
{
double open = iOpen(_Symbol, _Period, barIndex);
double close = iClose(_Symbol, _Period, barIndex);
double high = iHigh(_Symbol, _Period, barIndex);
double low = iLow(_Symbol, _Period, barIndex);
double range = high - low;
if(range <= 0) return false;
double body = MathAbs(close - open);
if(direction == +1) // Bullish Pin Bar (Hammer)
{
double lowerShadow = MathMin(open, close) - low;
double upperShadow = high - MathMax(open, close);
return (lowerShadow >= range * 0.6 && body <= range * 0.3 && upperShadow <= range * 0.2);
}
else if(direction == -1) // Bearish Pin Bar (Shooting Star)
{
double lowerShadow = MathMin(open, close) - low;
double upperShadow = high - MathMax(open, close);
return (upperShadow >= range * 0.6 && body <= range * 0.3 && lowerShadow <= range * 0.2);
}
return false;
}
//============================================================
// SWING BUY SIGNAL
//============================================================
@@ -612,7 +663,8 @@ bool SwingBuySignal()
if(!TrendStrong()) return false;
if(plus_di[0] <= minus_di[0]) return false;
if(rsi_val[0] >= RSI_OB || rsi_val[0] < 40.0) return false;
// Dioptimalkan menjadi 35.0 (sebelumnya 40.0) agar bisa menangkap pullback sehat yang lebih dalam
if(rsi_val[0] >= RSI_OB || rsi_val[0] < 35.0) return false;
double open1 = iOpen(_Symbol, _Period, 1);
double close1 = iClose(_Symbol, _Period, 1);
@@ -631,7 +683,8 @@ bool SwingBuySignal()
bool breakout = close1 > iHigh(_Symbol, _Period, 2);
bool engulfing = (body2 < 0) && (close1 > open2) && (open1 < close2);
return (breakout || engulfing);
bool pinbar = IsPinBar(1, +1);
return (breakout || engulfing || pinbar);
}
//============================================================
@@ -646,7 +699,8 @@ bool SwingSellSignal()
if(!TrendStrong()) return false;
if(minus_di[0] <= plus_di[0]) return false;
if(rsi_val[0] <= RSI_OS || rsi_val[0] > 60.0) return false;
// Dioptimalkan menjadi 65.0 (sebelumnya 60.0) agar bisa menangkap pullback sehat yang lebih tinggi
if(rsi_val[0] <= RSI_OS || rsi_val[0] > 65.0) return false;
double open1 = iOpen(_Symbol, _Period, 1);
double close1 = iClose(_Symbol, _Period, 1);
@@ -665,7 +719,8 @@ bool SwingSellSignal()
bool breakout = close1 < iLow(_Symbol, _Period, 2);
bool engulfing = (body2 < 0) && (close1 < open2) && (open1 > close2);
return (breakout || engulfing);
bool pinbar = IsPinBar(1, -1);
return (breakout || engulfing || pinbar);
}
//============================================================
@@ -831,7 +886,8 @@ void ManageSwing()
if(type==POSITION_TYPE_BUY)
{
double nSL = NormalizePrice(price - trailDist);
if(nSL > sl && nSL > openPrice)
// Trailing step minimal 10% dari ATR untuk mencegah spamming order modifikasi ke broker
if((nSL - sl >= atr * 0.1 || sl == 0) && nSL > openPrice)
{
trade.SetExpertMagicNumber(MagicSwing);
trade.PositionModify(ticket, nSL, newTP);
@@ -845,7 +901,8 @@ void ManageSwing()
else
{
double nSL = NormalizePrice(price + trailDist);
if((nSL < sl || sl == 0) && nSL < openPrice)
// Trailing step minimal 10% dari ATR
if((sl - nSL >= atr * 0.1 || sl == 0) && nSL < openPrice)
{
trade.SetExpertMagicNumber(MagicSwing);
trade.PositionModify(ticket, nSL, newTP);
@@ -926,22 +983,26 @@ void ManageScalp()
// Adaptive Trailing + Dynamic TP toward BB mid
if(profitDist >= trailStart)
{
// Dynamic TP: geser ke BB mid jika lebih jauh dari TP awal
// Dynamic TP: geser ke BB mid secara dinamis mengikuti pergerakan band
double newTP = tp;
if(type==POSITION_TYPE_BUY && bbMid[0] > tp && bbMid[0] > price)
if(type==POSITION_TYPE_BUY && bbMid[0] > price)
newTP = NormalizePrice(bbMid[0]);
else if(type==POSITION_TYPE_SELL && bbMid[0] < tp && bbMid[0] < price)
else if(type==POSITION_TYPE_SELL && bbMid[0] < price)
newTP = NormalizePrice(bbMid[0]);
// Hanya modifikasi jika perbedaan TP cukup signifikan untuk mencegah spamming order ke broker
bool updateTP = (newTP != tp && MathAbs(tp - newTP) >= atr * 0.1);
if(type==POSITION_TYPE_BUY)
{
double nSL = NormalizePrice(price - trailDist);
if(nSL > sl && nSL > openPrice)
// Trailing step minimal 5% dari ATR untuk scalp
if((nSL - sl >= atr * 0.05 || sl == 0) && nSL > openPrice)
{
trade.SetExpertMagicNumber(MagicScalp);
trade.PositionModify(ticket, nSL, newTP);
}
else if(newTP != tp && newTP > tp)
else if(updateTP)
{
trade.SetExpertMagicNumber(MagicScalp);
trade.PositionModify(ticket, sl, newTP);
@@ -950,12 +1011,13 @@ void ManageScalp()
else
{
double nSL = NormalizePrice(price + trailDist);
if((nSL < sl || sl == 0) && nSL < openPrice)
// Trailing step minimal 5% dari ATR untuk scalp
if((sl - nSL >= atr * 0.05 || sl == 0) && nSL < openPrice)
{
trade.SetExpertMagicNumber(MagicScalp);
trade.PositionModify(ticket, nSL, newTP);
}
else if(newTP != tp && newTP < tp)
else if(updateTP)
{
trade.SetExpertMagicNumber(MagicScalp);
trade.PositionModify(ticket, sl, newTP);
@@ -970,7 +1032,6 @@ void ManageScalp()
//============================================================
void OnTrade()
{
static int lastDealsTotal = 0;
datetime dayStart = iTime(_Symbol, PERIOD_D1, 0);
// Reset counter saat ganti hari (fix day-boundary bug)
@@ -1052,7 +1113,8 @@ void OnTick()
if(!LoadIndicators()) return;
double atr = atr_val[1];
if(atr < _Point * 30) return; // ATR terlalu kecil
if(atr < _Point * 10) return; // Proteksi batas spread dasar (sangat kecil)
if(!IsVolatilityOk()) return; // Filter volatilitas adaptif
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);