//+------------------------------------------------------------------+ //| XAU/XAG Risk-Based ATR EA (MT5) | //| Trades XAUUSD & XAGUSD with EMA+RSI(+ADX) filters | //| SL/TP and lot size based on account value and risk % | //+------------------------------------------------------------------+ #property strict #include CTrade trade; //------------------------- Inputs ---------------------------------- // Symbols (broker naming may differ, e.g. "XAUUSD.", "GOLD", etc.) input string InpSymbol1 = "XAUUSD"; input string InpSymbol2 = "XAGUSD"; // Timeframe and indicators input ENUM_TIMEFRAMES InpTF = PERIOD_M15; input int InpFastEMA = 50; input int InpSlowEMA = 200; input int InpRSIPeriod = 14; input double InpRSIBuyAbove = 52.0; input double InpRSISellBelow = 48.0; input bool InpUseADX = true; input int InpADXPeriod = 14; input double InpADXMin = 18.0; input int InpATRPeriod = 14; input double InpSL_ATR_Mult = 2.0; // Stop distance = ATR * mult input double InpRiskReward = 2.0; // TP distance = SL distance * RR // Risk / execution input double InpRiskPercent = 1.0; // % of balance to risk per trade input int InpMaxPositionsTotal = 2; // total open positions across symbols input double InpMaxSpreadPoints = 50; // skip if spread > this (points) input int InpSlippagePoints = 20; // max deviation in points input bool InpAllowNewBarOnly = true; // only evaluate on new bar // Optional trade session control (server time) input bool InpUseSessionFilter = false; input int InpSessionStartHour = 6; input int InpSessionEndHour = 20; //------------------------- Internals ------------------------------- datetime g_lastBarTime = 0; // Helper: check if symbol is one of ours bool IsTargetSymbol(const string sym) { return (sym == InpSymbol1 || sym == InpSymbol2); } // Helper: count open positions total (or for a symbol) int CountPositions(const string sym = "") { int count = 0; for(int i=0; i= InpSessionStartHour && t.hour < InpSessionEndHour); else return (t.hour >= InpSessionStartHour || t.hour < InpSessionEndHour); } // Normalize lots to symbol constraints double NormalizeLots(const string sym, double lots) { double minLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MAX); double stepLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_STEP); if(lots < minLot) lots = minLot; if(lots > maxLot) lots = maxLot; // Round down to step double steps = MathFloor(lots / stepLot); double norm = steps * stepLot; // Ensure not below min due to rounding if(norm < minLot) norm = minLot; return norm; } // Compute lot size from risk % and stop distance (price units) double LotsFromRisk(const string sym, double stopDistancePrice) { // Risk amount in account currency double balance = AccountInfoDouble(ACCOUNT_BALANCE); double riskMoney = balance * (InpRiskPercent / 100.0); if(riskMoney <= 0) return 0.0; // Convert stop distance (price) to money per 1 lot using tick value/size double tickValue = SymbolInfoDouble(sym, SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(sym, SYMBOL_TRADE_TICK_SIZE); if(tickValue <= 0 || tickSize <= 0) return 0.0; // How many ticks in stop distance? double ticks = stopDistancePrice / tickSize; if(ticks <= 0) return 0.0; // Loss per 1 lot if SL hit: double lossPerLot = ticks * tickValue; if(lossPerLot <= 0) return 0.0; double lots = riskMoney / lossPerLot; return NormalizeLots(sym, lots); } // Get indicator value helpers (current closed bar = shift 1) double iEMA(const string sym, ENUM_TIMEFRAMES tf, int period, int shift) { int h = iMA(sym, tf, period, 0, MODE_EMA, PRICE_CLOSE); if(h == INVALID_HANDLE) return EMPTY_VALUE; double buf[]; if(CopyBuffer(h, 0, shift, 1, buf) != 1) return EMPTY_VALUE; IndicatorRelease(h); return buf[0]; } double iRSIValue(const string sym, ENUM_TIMEFRAMES tf, int period, int shift) { int h = iRSI(sym, tf, period, PRICE_CLOSE); if(h == INVALID_HANDLE) return EMPTY_VALUE; double buf[]; if(CopyBuffer(h, 0, shift, 1, buf) != 1) return EMPTY_VALUE; IndicatorRelease(h); return buf[0]; } double iADXValue(const string sym, ENUM_TIMEFRAMES tf, int period, int shift) { int h = iADX(sym, tf, period); if(h == INVALID_HANDLE) return EMPTY_VALUE; double buf[]; if(CopyBuffer(h, 0, shift, 1, buf) != 1) return EMPTY_VALUE; IndicatorRelease(h); return buf[0]; } double iATRValue(const string sym, ENUM_TIMEFRAMES tf, int period, int shift) { int h = iATR(sym, tf, period); if(h == INVALID_HANDLE) return EMPTY_VALUE; double buf[]; if(CopyBuffer(h, 0, shift, 1, buf) != 1) return EMPTY_VALUE; IndicatorRelease(h); return buf[0]; } // Decide trade direction: +1 buy, -1 sell, 0 none int GetSignal(const string sym) { // Use closed bar values to reduce repainting int shift = 1; double fast = iEMA(sym, InpTF, InpFastEMA, shift); double slow = iEMA(sym, InpTF, InpSlowEMA, shift); double rsi = iRSIValue(sym, InpTF, InpRSIPeriod, shift); if(fast == EMPTY_VALUE || slow == EMPTY_VALUE || rsi == EMPTY_VALUE) return 0; if(InpUseADX) { double adx = iADXValue(sym, InpTF, InpADXPeriod, shift); if(adx == EMPTY_VALUE) return 0; if(adx < InpADXMin) return 0; // avoid weak trends } // Trend + momentum confirmation if(fast > slow && rsi >= InpRSIBuyAbove) return +1; if(fast < slow && rsi <= InpRSISellBelow) return -1; return 0; } // Place trade with ATR-based SL/TP and risk-based lots bool ExecuteTrade(const string sym, int direction) { if(direction == 0) return false; // Basic constraints if(!SymbolInfoInteger(sym, SYMBOL_SELECT)) SymbolSelect(sym, true); if(!SpreadOK(sym)) return false; if(!SessionOK()) return false; if(CountPositions("") >= InpMaxPositionsTotal) return false; if(CountPositions(sym) > 0) return false; // one trade per symbol double atr = iATRValue(sym, InpTF, InpATRPeriod, 1); if(atr == EMPTY_VALUE || atr <= 0) return false; double slDist = atr * InpSL_ATR_Mult; double tpDist = slDist * InpRiskReward; double ask = SymbolInfoDouble(sym, SYMBOL_ASK); double bid = SymbolInfoDouble(sym, SYMBOL_BID); double pt = SymbolInfoDouble(sym, SYMBOL_POINT); if(ask <= 0 || bid <= 0 || pt <= 0) return false; // Lot size from risk double lots = LotsFromRisk(sym, slDist); if(lots <= 0) return false; trade.SetDeviationInPoints(InpSlippagePoints); double price, sl, tp; if(direction > 0) { price = ask; sl = price - slDist; tp = price + tpDist; sl = NormalizeDouble(sl, (int)SymbolInfoInteger(sym, SYMBOL_DIGITS)); tp = NormalizeDouble(tp, (int)SymbolInfoInteger(sym, SYMBOL_DIGITS)); return trade.Buy(lots, sym, price, sl, tp, "EMA+RSI ATR risk buy"); } else { price = bid; sl = price + slDist; tp = price - tpDist; sl = NormalizeDouble(sl, (int)SymbolInfoInteger(sym, SYMBOL_DIGITS)); tp = NormalizeDouble(tp, (int)SymbolInfoInteger(sym, SYMBOL_DIGITS)); return trade.Sell(lots, sym, price, sl, tp, "EMA+RSI ATR risk sell"); } } // New bar detection (for the configured timeframe) bool IsNewBar() { if(!InpAllowNewBarOnly) return true; datetime t = iTime(_Symbol, InpTF, 0); if(t == 0) return false; if(t != g_lastBarTime) { g_lastBarTime = t; return true; } return false; } //------------------------- MT5 Events ------------------------------ int OnInit() { // Ensure symbols are in Market Watch SymbolSelect(InpSymbol1, true); SymbolSelect(InpSymbol2, true); g_lastBarTime = 0; return(INIT_SUCCEEDED); } void OnTick() { if(!IsNewBar()) return; // Evaluate both symbols on each new bar string syms[2] = {InpSymbol1, InpSymbol2}; for(int i=0; i<2; i++) { string sym = syms[i]; if(!IsTargetSymbol(sym)) continue; int sig = GetSignal(sym); if(sig != 0) ExecuteTrade(sym, sig); } } //+------------------------------------------------------------------+