SonnyPanel/SonnyPanelGood/EA/npoX6.mq5

294 lines
9.3 KiB
MQL5
Raw Permalink Normal View History

2026-03-22 22:19:39 +01:00
//+------------------------------------------------------------------+
//| XAUUSD Breakout EA v2.1 (Gold‑Optimized) |
//| - Donchian breakout (25) tuned for gold |
//| - ATR SL/TP tuned for gold volatility |
//| - 2‑stage trailing optimized for long gold trends |
//| - Donchian midline trend‑extension exit |
//| - Time‑based exit (30 bars) |
//| - Single trade, clean and robust |
//+------------------------------------------------------------------+
#property strict
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
CTrade Trade;
CPositionInfo Pos;
//---------------- INPUTS ----------------
input int BreakoutPeriod = 25; // Gold sweet spot
input int ATRPeriod = 14;
input bool UseDailyTrendFilter = false; // Gold doesn't need it
input double RiskPerTradePct = 1.0;
input double SL_ATR_Mult = 1.8; // Gold-optimized
input double TP_ATR_Mult = 3.2; // Gold-optimized
input double Trail_ATR_Mult = 0.9; // Slightly tighter trailing
input double Trail_ATR_Mult2 = 0.6; // Strong trailing after 2 ATR profit
input int MaxBarsInTrade = 30;
input int Magic = 999;
input int Slippage = 30;
//---------------- HANDLES ----------------
int atrHandle = INVALID_HANDLE;
int d1FastMAHandle = INVALID_HANDLE;
int d1SlowMAHandle = INVALID_HANDLE;
//---------------- FILLING FIX ----------------
int GetValidFillingType()
{
int mode = (int)SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE);
if (mode == ORDER_FILLING_IOC ||
mode == ORDER_FILLING_RETURN ||
mode == ORDER_FILLING_FOK)
return mode;
return ORDER_FILLING_IOC;
}
//+------------------------------------------------------------------+
int OnInit()
{
atrHandle = iATR(_Symbol, PERIOD_H4, ATRPeriod);
d1FastMAHandle = iMA(_Symbol, PERIOD_D1, 20, 0, MODE_SMA, PRICE_CLOSE);
d1SlowMAHandle = iMA(_Symbol, PERIOD_D1, 50, 0, MODE_SMA, PRICE_CLOSE);
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnTick()
{
string sym = _Symbol;
double bid = SymbolInfoDouble(sym, SYMBOL_BID);
double ask = SymbolInfoDouble(sym, SYMBOL_ASK);
if (bid <= 0 || ask <= 0) return;
ManageOpenPosition(sym);
if (HasOpenPosition(sym))
return;
double atr = GetATR();
if (atr <= 0) return;
double highBreak = GetDonchianHigh(BreakoutPeriod);
double lowBreak = GetDonchianLow(BreakoutPeriod);
bool allowBuy = true;
bool allowSell = true;
if (UseDailyTrendFilter)
{
ENUM_ORDER_TYPE d1Trend = GetDailyTrendDirection();
allowBuy = (d1Trend == ORDER_TYPE_BUY);
allowSell = (d1Trend == ORDER_TYPE_SELL);
}
double slDist = SL_ATR_Mult * atr;
double tpDist = TP_ATR_Mult * atr;
if (ask > highBreak && allowBuy)
OpenTrade(sym, ORDER_TYPE_BUY, ask, slDist, tpDist);
if (bid < lowBreak && allowSell)
OpenTrade(sym, ORDER_TYPE_SELL, bid, slDist, tpDist);
}
//+------------------------------------------------------------------+
bool HasOpenPosition(string sym)
{
for (int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if (!PositionSelectByTicket(ticket)) continue;
if (PositionGetString(POSITION_SYMBOL) != sym) continue;
if ((int)PositionGetInteger(POSITION_MAGIC) != Magic) continue;
return true;
}
return false;
}
//+------------------------------------------------------------------+
double GetATR()
{
double buf[1];
if (CopyBuffer(atrHandle, 0, 0, 1, buf) <= 0)
return 0.0;
return buf[0];
}
//+------------------------------------------------------------------+
double GetDonchianHigh(int period)
{
int idx = iHighest(_Symbol, PERIOD_H4, MODE_HIGH, period, 1);
return iHigh(_Symbol, PERIOD_H4, idx);
}
double GetDonchianLow(int period)
{
int idx = iLowest(_Symbol, PERIOD_H4, MODE_LOW, period, 1);
return iLow(_Symbol, PERIOD_H4, idx);
}
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE GetDailyTrendDirection()
{
double f[1], s[1];
CopyBuffer(d1FastMAHandle, 0, 0, 1, f);
CopyBuffer(d1SlowMAHandle, 0, 0, 1, s);
if (f[0] > s[0]) return ORDER_TYPE_BUY;
if (f[0] < s[0]) return ORDER_TYPE_SELL;
return ORDER_TYPE_BUY;
}
//+------------------------------------------------------------------+
double CalcLotSize(double slDist)
{
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmt = balance * (RiskPerTradePct / 100.0);
double contract = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double valuePerLotPerPoint = contract * point;
double valuePerLotPerSL = valuePerLotPerPoint * (slDist / point);
double lots = riskAmt / valuePerLotPerSL;
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
lots = MathMax(minLot, MathMin(maxLot, lots));
lots = MathFloor(lots / lotStep) * lotStep;
return NormalizeDouble(lots, 2);
}
//+------------------------------------------------------------------+
bool OpenTrade(string sym, ENUM_ORDER_TYPE type, double price,
double slDist, double tpDist)
{
double lot = CalcLotSize(slDist);
if (lot <= 0) return false;
MqlTradeRequest r;
MqlTradeResult e;
ZeroMemory(r);
ZeroMemory(e);
r.action = TRADE_ACTION_DEAL;
r.symbol = sym;
r.magic = Magic;
r.volume = lot;
r.type = type;
r.price = price;
r.deviation = Slippage;
r.type_time = ORDER_TIME_GTC;
r.type_filling = 1;
if (!OrderSend(r, e))
return false;
if (e.retcode != TRADE_RETCODE_DONE &&
e.retcode != TRADE_RETCODE_DONE_PARTIAL)
return false;
ulong ticket = e.order;
Sleep(200);
if (!PositionSelectByTicket(ticket))
return true;
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double sl = (type == ORDER_TYPE_BUY ? openPrice - slDist : openPrice + slDist);
double tp = (type == ORDER_TYPE_BUY ? openPrice + tpDist : openPrice - tpDist);
Trade.PositionModify(ticket, sl, tp);
return true;
}
//+------------------------------------------------------------------+
void ManageOpenPosition(string sym)
{
static datetime lastBarTime = 0;
datetime curBarTime = iTime(sym, PERIOD_H4, 0);
bool isNewBar = (curBarTime != lastBarTime);
if (isNewBar)
lastBarTime = curBarTime;
for (int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if (!PositionSelectByTicket(ticket)) continue;
if (PositionGetString(POSITION_SYMBOL) != sym) continue;
if ((int)PositionGetInteger(POSITION_MAGIC) != Magic) continue;
ENUM_POSITION_TYPE pt = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double priceOpen = PositionGetDouble(POSITION_PRICE_OPEN);
double sl = PositionGetDouble(POSITION_SL);
double tp = PositionGetDouble(POSITION_TP);
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
double bid = SymbolInfoDouble(sym, SYMBOL_BID);
double ask = SymbolInfoDouble(sym, SYMBOL_ASK);
double atr = GetATR();
if (atr <= 0) return;
double trail1 = Trail_ATR_Mult * atr;
double trail2 = Trail_ATR_Mult2 * atr;
datetime now = TimeCurrent();
int barsOpen = (int)((now - openTime) / (4 * 3600));
if (barsOpen >= MaxBarsInTrade)
{
Trade.PositionClose(ticket);
continue;
}
// --- Trend-extension exit using Donchian midline
if (isNewBar)
{
double dcHigh = GetDonchianHigh(BreakoutPeriod);
double dcLow = GetDonchianLow(BreakoutPeriod);
double dcMid = (dcHigh + dcLow) / 2.0;
double prevClose = iClose(sym, PERIOD_H4, 1);
if (pt == POSITION_TYPE_BUY && prevClose < dcMid)
{
Trade.PositionClose(ticket);
continue;
}
if (pt == POSITION_TYPE_SELL && prevClose > dcMid)
{
Trade.PositionClose(ticket);
continue;
}
}
// --- Two-stage trailing
if (pt == POSITION_TYPE_BUY)
{
double profit = bid - priceOpen;
double useTrail = (profit > 2 * atr ? trail2 : trail1);
double newSL = bid - useTrail;
if (newSL > sl)
Trade.PositionModify(ticket, newSL, tp);
}
else if (pt == POSITION_TYPE_SELL)
{
double profit = priceOpen - ask;
double useTrail = (profit > 2 * atr ? trail2 : trail1);
double newSL = ask + useTrail;
if (newSL < sl || sl == 0.0)
Trade.PositionModify(ticket, newSL, tp);
}
}
}
//+------------------------------------------------------------------+