//+------------------------------------------------------------------+ //| MAEA.mq5 | //| Moving Average EA | //| Trend-following EA with EMA zone breakout | //+------------------------------------------------------------------+ #property copyright "MAEA" #property link "" #property version "1.00" #property strict #include #include // === EMA SETTINGS === input int EMAPeriod = 30; input int EMAShift = 0; input int MinZoneWidthPoints = 100; // === RISK MANAGEMENT === input double LotSize = 0.01; input double TakeProfitUSD = 5.0; input int BreakevenPoints = 100; input int TrailingStopPoints = 100; // === FILTERS === input bool UseMTFFilter = true; input bool UseNewsFilter = true; input int MaxSpread = 30; input int VolumePeriod = 20; // === NEWS FILTER === input string NewsAvoidHours = "14,15,20,21"; input string NewsAvoidDays = "1,5"; // === RISK PROTECTION === input double MaxDrawdownPercent = 10.0; // === EA SETTINGS === input int MagicNumber = 12345; // === GLOBAL VARIABLES === CTrade trade; int handleEMAHigh = INVALID_HANDLE; int handleEMALow = INVALID_HANDLE; int handleD1EMAHigh = INVALID_HANDLE; int handleD1EMALow = INVALID_HANDLE; double g_EMAHighBuffer[]; double g_EMALowBuffer[]; double g_D1EMAHighBuffer[]; double g_D1EMALowBuffer[]; datetime g_LastBarTime = 0; double g_EntryPrice = 0; double g_BreakevenPrice = 0; bool g_BreakevenTriggered = false; bool g_TradingAllowed = true; double g_AccountHighBalance = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { Print("[MAEA] Initializing EA..."); trade.SetExpertMagicNumber(MagicNumber); handleEMAHigh = iMA(_Symbol, PERIOD_CURRENT, EMAPeriod, EMAShift, MODE_EMA, PRICE_HIGH); handleEMALow = iMA(_Symbol, PERIOD_CURRENT, EMAPeriod, EMAShift, MODE_EMA, PRICE_LOW); handleD1EMAHigh = iMA(_Symbol, PERIOD_D1, EMAPeriod, EMAShift, MODE_EMA, PRICE_HIGH); handleD1EMALow = iMA(_Symbol, PERIOD_D1, EMAPeriod, EMAShift, MODE_EMA, PRICE_LOW); if(handleEMAHigh == INVALID_HANDLE || handleEMALow == INVALID_HANDLE || handleD1EMAHigh == INVALID_HANDLE || handleD1EMALow == INVALID_HANDLE) { Print("[MAEA] Failed to create EMA handles"); return INIT_FAILED; } Print("[MAEA] Handles - EMAHigh: ", handleEMAHigh, ", EMALow: ", handleEMALow); ArraySetAsSeries(g_EMAHighBuffer, true); ArraySetAsSeries(g_EMALowBuffer, true); ArraySetAsSeries(g_D1EMAHighBuffer, true); ArraySetAsSeries(g_D1EMALowBuffer, true); g_AccountHighBalance = AccountInfoDouble(ACCOUNT_BALANCE); g_LastBarTime = iTime(_Symbol, PERIOD_CURRENT, 0); Print("[MAEA] Initialization successful"); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Print("[MAEA] Deinitializing. Reason: ", reason); if(handleEMAHigh != INVALID_HANDLE) IndicatorRelease(handleEMAHigh); if(handleEMALow != INVALID_HANDLE) IndicatorRelease(handleEMALow); if(handleD1EMAHigh != INVALID_HANDLE) IndicatorRelease(handleD1EMAHigh); if(handleD1EMALow != INVALID_HANDLE) IndicatorRelease(handleD1EMALow); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0); if(currentBarTime != g_LastBarTime) { g_LastBarTime = currentBarTime; CheckDrawdown(); OnNewBar(); } ManageOpenPosition(); } //+------------------------------------------------------------------+ //| New bar processing | //+------------------------------------------------------------------+ void OnNewBar() { if(HasOpenPosition()) { CheckOppositeSignal(); return; } CheckBreakthroughSignal(); } //+------------------------------------------------------------------+ //| Check breakthrough signal and open position | //+------------------------------------------------------------------+ void CheckBreakthroughSignal() { if(!CopyIndicatorData()) return; double prevClose = iClose(_Symbol, PERIOD_CURRENT, 1); double currClose = iClose(_Symbol, PERIOD_CURRENT, 0); double emaHigh = g_EMAHighBuffer[0]; double emaLow = g_EMALowBuffer[0]; double zoneWidth = MathAbs(emaHigh - emaLow); if(zoneWidth < MinZoneWidthPoints * _Point) return; bool prevInsideZone = (prevClose <= emaHigh && prevClose >= emaLow); if(!prevInsideZone) return; bool buySignal = (currClose > emaHigh); bool sellSignal = (currClose < emaLow); if(!buySignal && !sellSignal) return; if(buySignal && !AllFiltersPass(true)) return; if(sellSignal && !AllFiltersPass(false)) return; if(buySignal) { OpenBuyPosition(); } else if(sellSignal) { OpenSellPosition(); } } //+------------------------------------------------------------------+ //| Check opposite signal and close position | //+------------------------------------------------------------------+ void CheckOppositeSignal() { if(!CopyIndicatorData()) return; if(!HasOpenPosition()) return; double currClose = iClose(_Symbol, PERIOD_CURRENT, 0); double prevClose = iClose(_Symbol, PERIOD_CURRENT, 1); double emaHigh = g_EMAHighBuffer[0]; double emaLow = g_EMALowBuffer[0]; bool buySignal = (prevClose <= emaHigh && prevClose >= emaLow && currClose > emaHigh); bool sellSignal = (prevClose <= emaHigh && prevClose >= emaLow && currClose < emaLow); if(!buySignal && !sellSignal) return; ulong positionTicket = GetPositionTicket(); if(positionTicket == 0) return; ENUM_POSITION_TYPE posType = GetPositionType(positionTicket); if(posType == POSITION_TYPE_BUY && sellSignal) { trade.PositionClose(positionTicket); ResetPositionVariables(); Print("Buy position closed due to opposite sell signal"); } else if(posType == POSITION_TYPE_SELL && buySignal) { trade.PositionClose(positionTicket); ResetPositionVariables(); Print("Sell position closed due to opposite buy signal"); } } //+------------------------------------------------------------------+ //| Manage open position (TP, SL, breakeven, trailing stop) | //+------------------------------------------------------------------+ void ManageOpenPosition() { if(!HasOpenPosition()) return; ulong positionTicket = GetPositionTicket(); if(positionTicket == 0) return; if(!PositionSelectByTicket(positionTicket)) return; double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT); double currentSL = PositionGetDouble(POSITION_SL); double currentTP = PositionGetDouble(POSITION_TP); double profit = PositionGetDouble(POSITION_PROFIT); ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); if(currentTP > 0 && profit >= TakeProfitUSD) { trade.PositionClose(positionTicket); ResetPositionVariables(); Print("Position closed by Take Profit"); return; } double pointsProfit = 0; double breakevenLevel = 0; if(posType == POSITION_TYPE_BUY) { pointsProfit = (currentPrice - openPrice) / _Point; breakevenLevel = openPrice + BreakevenPoints * _Point; } else { pointsProfit = (openPrice - currentPrice) / _Point; breakevenLevel = openPrice - BreakevenPoints * _Point; } if(!g_BreakevenTriggered && pointsProfit >= BreakevenPoints) { if(posType == POSITION_TYPE_BUY) { trade.PositionModify(positionTicket, openPrice, currentTP); } else { trade.PositionModify(positionTicket, openPrice, currentTP); } g_BreakevenTriggered = true; Print("Breakeven triggered for position ", positionTicket); } if(g_BreakevenTriggered && TrailingStopPoints > 0) { double trailDistance = TrailingStopPoints * _Point; double newSL; if(posType == POSITION_TYPE_BUY) { newSL = currentPrice - trailDistance; if(newSL > currentSL + _Point && newSL > openPrice) { trade.PositionModify(positionTicket, newSL, currentTP); } } else { newSL = currentPrice + trailDistance; if(newSL < currentSL - _Point && newSL < openPrice) { trade.PositionModify(positionTicket, newSL, currentTP); } } } } //+------------------------------------------------------------------+ //| Open buy position | //+------------------------------------------------------------------+ void OpenBuyPosition() { if(!CopyIndicatorData()) return; double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double sl = g_EMALowBuffer[0]; double tp = CalculateTakeProfit(ask, sl, true); if(!trade.Buy(LotSize, _Symbol, ask, sl, tp, "MAEA Buy")) { int err = GetLastError(); Print("Buy order failed. Error: ", err); } else { g_EntryPrice = ask; g_BreakevenTriggered = false; Print("Buy position opened at ", ask); } } //+------------------------------------------------------------------+ //| Open sell position | //+------------------------------------------------------------------+ void OpenSellPosition() { if(!CopyIndicatorData()) return; double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double sl = g_EMAHighBuffer[0]; double tp = CalculateTakeProfit(bid, sl, false); if(!trade.Sell(LotSize, _Symbol, bid, sl, tp, "MAEA Sell")) { int err = GetLastError(); Print("Sell order failed. Error: ", err); } else { g_EntryPrice = bid; g_BreakevenTriggered = false; Print("Sell position opened at ", bid); } } //+------------------------------------------------------------------+ //| Calculate take profit in price | //+------------------------------------------------------------------+ double CalculateTakeProfit(double entryPrice, double sl, bool isBuy) { double pointValue = SymbolInfoDouble(_Symbol, SYMBOL_POINT); double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); double profitPoints = TakeProfitUSD / (tickValue * LotSize / tickSize); double profitPrice = profitPoints * pointValue; if(isBuy) { return entryPrice + profitPrice; } else { return entryPrice - profitPrice; } } //+------------------------------------------------------------------+ //| Copy indicator data | //+------------------------------------------------------------------+ bool CopyIndicatorData() { if(CopyBuffer(handleEMAHigh, 0, 0, 10, g_EMAHighBuffer) <= 0) return false; if(CopyBuffer(handleEMALow, 0, 0, 10, g_EMALowBuffer) <= 0) return false; if(UseMTFFilter) { if(CopyBuffer(handleD1EMAHigh, 0, 0, 2, g_D1EMAHighBuffer) <= 0) return false; if(CopyBuffer(handleD1EMALow, 0, 0, 2, g_D1EMALowBuffer) <= 0) return false; } return true; } //+------------------------------------------------------------------+ //| Check all filters | //+------------------------------------------------------------------+ bool AllFiltersPass(bool isBuyDirection) { if(!CheckVolumeFilter()) return false; if(!CheckSpreadFilter()) return false; if(!CheckDrawdownAllowed()) return false; if(UseMTFFilter && !CheckMTFFilter(isBuyDirection)) return false; if(UseNewsFilter && !CheckNewsFilter()) return false; return true; } //+------------------------------------------------------------------+ //| Volume filter | //+------------------------------------------------------------------+ bool CheckVolumeFilter() { long currentVolume = iVolume(_Symbol, PERIOD_CURRENT, 0); long avgVolume = 0; for(int i = 1; i <= VolumePeriod; i++) { avgVolume += iVolume(_Symbol, PERIOD_CURRENT, i); } avgVolume = avgVolume / VolumePeriod; return (currentVolume > avgVolume); } //+------------------------------------------------------------------+ //| Spread filter | //+------------------------------------------------------------------+ bool CheckSpreadFilter() { long spread; SymbolInfoInteger(_Symbol, SYMBOL_SPREAD, spread); return (spread <= MaxSpread); } //+------------------------------------------------------------------+ //| MTF filter | //+------------------------------------------------------------------+ bool CheckMTFFilter(bool isBuyDirection) { double d1Close = iClose(_Symbol, PERIOD_D1, 0); double d1EMAHigh = g_D1EMAHighBuffer[0]; double d1EMALow = g_D1EMALowBuffer[0]; if(d1Close > d1EMAHigh) { return isBuyDirection; } else if(d1Close < d1EMALow) { return !isBuyDirection; } else { return false; } } //+------------------------------------------------------------------+ //| News filter | //+------------------------------------------------------------------+ bool CheckNewsFilter() { datetime gmtTime = TimeGMT(); datetime thaiTime = gmtTime + 25200; MqlDateTime timeStruct; TimeToStruct(thaiTime, timeStruct); string hoursArr[]; StringSplit(NewsAvoidHours, ',', hoursArr); for(int i = 0; i < ArraySize(hoursArr); i++) { string h = hoursArr[i]; StringTrimRight(h); StringTrimLeft(h); if(h == IntegerToString(timeStruct.hour)) { return false; } } string daysArr[]; StringSplit(NewsAvoidDays, ',', daysArr); for(int i = 0; i < ArraySize(daysArr); i++) { string d = daysArr[i]; StringTrimRight(d); StringTrimLeft(d); if(d == IntegerToString(timeStruct.day_of_week)) { return false; } } return true; } //+------------------------------------------------------------------+ //| Check drawdown | //+------------------------------------------------------------------+ void CheckDrawdown() { double currentBalance = AccountInfoDouble(ACCOUNT_BALANCE); if(currentBalance > g_AccountHighBalance) { g_AccountHighBalance = currentBalance; } if(currentBalance > 0 && g_AccountHighBalance > 0) { double drawdownPercent = ((g_AccountHighBalance - currentBalance) / g_AccountHighBalance) * 100; if(drawdownPercent >= MaxDrawdownPercent) { g_TradingAllowed = false; Print("Drawdown limit reached. Trading stopped."); } else { g_TradingAllowed = true; } } } //+------------------------------------------------------------------+ //| Check if trading is allowed based on drawdown | //+------------------------------------------------------------------+ bool CheckDrawdownAllowed() { return g_TradingAllowed; } //+------------------------------------------------------------------+ //| Check if has open position | //+------------------------------------------------------------------+ bool HasOpenPosition() { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionGetSymbol(i) == _Symbol) { ulong magic = PositionGetInteger(POSITION_MAGIC); if(magic == MagicNumber) { return true; } } } return false; } //+------------------------------------------------------------------+ //| Get position ticket | //+------------------------------------------------------------------+ ulong GetPositionTicket() { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionGetSymbol(i) == _Symbol) { ulong ticket = PositionGetInteger(POSITION_TICKET); ulong magic = PositionGetInteger(POSITION_MAGIC); if(magic == MagicNumber) { return ticket; } } } return 0; } //+------------------------------------------------------------------+ //| Get position type | //+------------------------------------------------------------------+ ENUM_POSITION_TYPE GetPositionType(ulong ticket) { if(PositionSelectByTicket(ticket)) { return (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); } return POSITION_TYPE_BUY; } //+------------------------------------------------------------------+ //| Reset position variables | //+------------------------------------------------------------------+ void ResetPositionVariables() { g_EntryPrice = 0; g_BreakevenTriggered = false; } //+------------------------------------------------------------------+