//+------------------------------------------------------------------+ //| 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 #include 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); } } } //+------------------------------------------------------------------+