294 lines
9.3 KiB
MQL5
294 lines
9.3 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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);
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|