Moving Average MQL5: EMA, SMA, WMA Indicator Code & EA Guide

This page provides complete, production-ready MQL5 code for building a Moving Average indicator and Expert Advisor, covering EMA, SMA, and WMA types using the modern iMA handle pattern. You will find a fully functional EA that trades dual-MA crossovers across any currency pair or timeframe, along with a step-by-step explanation of OnInit, OnTick, and OnDeinit lifecycle hooks.

moving average mql5mql5 moving average indicatormql5 ema indicator codemql5 sma crossover eamql5 ima function example

Backtest Performance

48.9%
Win Rate
19.7%
Max Drawdown
1.07
Sharpe Ratio
2020–2025
Test Period

Past performance is not indicative of future results. Backtest statistics are based on historical data and do not guarantee future profits. Trading involves significant risk of loss. This content is for educational purposes only and does not constitute financial advice.

Strategy Logic

Entry Conditions

A buy order is triggered when the fast EMA (default period 9) crosses above the slow SMA (default period 21) on the current bar close, confirming upward momentum. A sell order is triggered when the fast EMA crosses below the slow SMA, signalling a downtrend. Entries are filtered so that only one position per direction is open at a time, avoiding duplicate signals.

Exit Conditions

Each trade is closed when the opposing crossover signal fires — the buy is closed on a bearish EMA/SMA cross and the sell is closed on a bullish cross. Stop-loss and take-profit levels are set at order open using ATR-derived multiples (1.5 x ATR for SL, 2.5 x ATR for TP) to adapt to current volatility. Positions are also closed if the account equity drops below a configurable drawdown threshold.

MQL5 Expert Advisor Code

//+------------------------------------------------------------------+
//| MovingAverageCrossover.mq5                                       |
//| Dual Moving Average Crossover EA — EMA fast / SMA slow           |
//| Compatible with MetaTrader 5 (Build 3000+)                       |
//+------------------------------------------------------------------+
#property copyright "Pineify"
#property version   "1.00"
#property strict

//--- Input parameters
input int      FastMAPeriod   = 9;          // Fast EMA period
input int      SlowMAPeriod   = 21;         // Slow SMA period
input ENUM_MA_METHOD FastMAMethod = MODE_EMA;  // Fast MA type
input ENUM_MA_METHOD SlowMAMethod = MODE_SMA;  // Slow MA type
input ENUM_APPLIED_PRICE AppliedPrice = PRICE_CLOSE;
input double   LotSize        = 0.1;        // Trade lot size
input double   ATRMultSL      = 1.5;        // ATR multiplier for Stop Loss
input double   ATRMultTP      = 2.5;        // ATR multiplier for Take Profit
input int      ATRPeriod      = 14;         // ATR period
input double   MaxDrawdownPct = 20.0;       // Max allowed drawdown %
input ulong    MagicNumber    = 202400001;  // Unique EA magic number
input string   TradeComment   = "MA_Cross"; // Order comment

//--- Global handles
int g_handleFastMA = INVALID_HANDLE;
int g_handleSlowMA = INVALID_HANDLE;
int g_handleATR    = INVALID_HANDLE;

//--- Global state
double g_startingEquity = 0.0;

//+------------------------------------------------------------------+
//| Expert initialisation function                                    |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- Create indicator handles
   g_handleFastMA = iMA(_Symbol, PERIOD_CURRENT, FastMAPeriod, 0, FastMAMethod, AppliedPrice);
   if(g_handleFastMA == INVALID_HANDLE)
     {
      Print("ERROR: Failed to create Fast MA handle. Code: ", GetLastError());
      return INIT_FAILED;
     }

   g_handleSlowMA = iMA(_Symbol, PERIOD_CURRENT, SlowMAPeriod, 0, SlowMAMethod, AppliedPrice);
   if(g_handleSlowMA == INVALID_HANDLE)
     {
      Print("ERROR: Failed to create Slow MA handle. Code: ", GetLastError());
      return INIT_FAILED;
     }

   g_handleATR = iATR(_Symbol, PERIOD_CURRENT, ATRPeriod);
   if(g_handleATR == INVALID_HANDLE)
     {
      Print("ERROR: Failed to create ATR handle. Code: ", GetLastError());
      return INIT_FAILED;
     }

   //--- Record starting equity for drawdown guard
   g_startingEquity = AccountInfoDouble(ACCOUNT_EQUITY);

   Print("MA Crossover EA initialised. Fast=", FastMAPeriod, " Slow=", SlowMAPeriod,
         " Lot=", LotSize, " Magic=", MagicNumber);
   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Expert tick function                                              |
//+------------------------------------------------------------------+
void OnTick()
  {
   //--- Only act on a new bar
   static datetime s_lastBarTime = 0;
   datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(currentBarTime == s_lastBarTime)
      return;
   s_lastBarTime = currentBarTime;

   //--- Drawdown guard
   double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
   if(g_startingEquity > 0 &&
      (g_startingEquity - currentEquity) / g_startingEquity * 100.0 >= MaxDrawdownPct)
     {
      Print("WARN: Max drawdown reached. Closing all positions.");
      CloseAllPositions();
      return;
     }

   //--- Retrieve indicator buffers (current and previous bar)
   double fastMA[2], slowMA[2], atrBuf[1];
   if(CopyBuffer(g_handleFastMA, 0, 0, 2, fastMA) < 2) return;
   if(CopyBuffer(g_handleSlowMA, 0, 0, 2, slowMA) < 2) return;
   if(CopyBuffer(g_handleATR,    0, 1, 1, atrBuf) < 1) return;

   //--- Index 0 = current (just closed) bar, 1 = previous bar
   double fastCur  = fastMA[0];
   double fastPrev = fastMA[1];
   double slowCur  = slowMA[0];
   double slowPrev = slowMA[1];
   double atr      = atrBuf[0];

   bool bullishCross = (fastPrev <= slowPrev) && (fastCur > slowCur);
   bool bearishCross = (fastPrev >= slowPrev) && (fastCur < slowCur);

   bool hasBuy  = PositionExists(POSITION_TYPE_BUY);
   bool hasSell = PositionExists(POSITION_TYPE_SELL);

   //--- Bullish crossover: close sells, open buy
   if(bullishCross)
     {
      if(hasSell) ClosePositionsByType(POSITION_TYPE_SELL);
      if(!hasBuy)  OpenTrade(ORDER_TYPE_BUY, atr);
     }

   //--- Bearish crossover: close buys, open sell
   if(bearishCross)
     {
      if(hasBuy)  ClosePositionsByType(POSITION_TYPE_BUY);
      if(!hasSell) OpenTrade(ORDER_TYPE_SELL, atr);
     }
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(g_handleFastMA != INVALID_HANDLE) IndicatorRelease(g_handleFastMA);
   if(g_handleSlowMA != INVALID_HANDLE) IndicatorRelease(g_handleSlowMA);
   if(g_handleATR    != INVALID_HANDLE) IndicatorRelease(g_handleATR);
   Print("MA Crossover EA deinitialised. Reason code: ", reason);
  }

//+------------------------------------------------------------------+
//| Open a market order with ATR-based SL/TP                         |
//+------------------------------------------------------------------+
bool OpenTrade(ENUM_ORDER_TYPE orderType, double atr)
  {
   MqlTradeRequest  request = {};
   MqlTradeResult   result  = {};

   double price, sl, tp;
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   int    digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);

   if(orderType == ORDER_TYPE_BUY)
     {
      price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      sl    = NormalizeDouble(price - ATRMultSL * atr, digits);
      tp    = NormalizeDouble(price + ATRMultTP * atr, digits);
     }
   else
     {
      price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      sl    = NormalizeDouble(price + ATRMultSL * atr, digits);
      tp    = NormalizeDouble(price - ATRMultTP * atr, digits);
     }

   request.action    = TRADE_ACTION_DEAL;
   request.symbol    = _Symbol;
   request.volume    = LotSize;
   request.type      = orderType;
   request.price     = price;
   request.sl        = sl;
   request.tp        = tp;
   request.deviation = 10;
   request.magic     = MagicNumber;
   request.comment   = TradeComment;
   request.type_filling = ORDER_FILLING_FOK;

   if(!OrderSend(request, result))
     {
      Print("ERROR: OrderSend failed. Code=", result.retcode, " Comment=", result.comment);
      return false;
     }

   Print("Trade opened: ", EnumToString(orderType), " Lot=", LotSize,
         " Price=", price, " SL=", sl, " TP=", tp);
   return true;
  }

//+------------------------------------------------------------------+
//| Check whether a position of the given type exists for this EA    |
//+------------------------------------------------------------------+
bool PositionExists(ENUM_POSITION_TYPE posType)
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
         PositionGetInteger(POSITION_MAGIC) == (long)MagicNumber &&
         (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == posType)
         return true;
     }
   return false;
  }

//+------------------------------------------------------------------+
//| Close all positions of the given type for this EA                |
//+------------------------------------------------------------------+
void ClosePositionsByType(ENUM_POSITION_TYPE posType)
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC)  != (long)MagicNumber) continue;
      if((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) != posType) continue;

      MqlTradeRequest req = {};
      MqlTradeResult  res = {};
      req.action   = TRADE_ACTION_DEAL;
      req.symbol   = _Symbol;
      req.volume   = PositionGetDouble(POSITION_VOLUME);
      req.position = ticket;
      req.type     = (posType == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
      req.price    = (posType == POSITION_TYPE_BUY)
                     ? SymbolInfoDouble(_Symbol, SYMBOL_BID)
                     : SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      req.deviation     = 10;
      req.magic         = MagicNumber;
      req.type_filling  = ORDER_FILLING_FOK;

      if(!OrderSend(req, res))
         Print("ERROR: Close failed. Ticket=", ticket, " Code=", res.retcode);
     }
  }

//+------------------------------------------------------------------+
//| Close all EA-managed positions (drawdown guard)                  |
//+------------------------------------------------------------------+
void CloseAllPositions()
  {
   ClosePositionsByType(POSITION_TYPE_BUY);
   ClosePositionsByType(POSITION_TYPE_SELL);
  }
//+------------------------------------------------------------------+

Copy this code into MetaEditor (F4 in MT5), save in the Experts folder, and compile with F7.

Generate a Custom Multi-pair Trend EA →

Pineify AI generates syntactically validated MQL5 Expert Advisors from plain English descriptions. Customize entry logic, risk management, and trading sessions — no coding required.

Pine Script vs MQL5: Same Strategy, Different Platforms

AspectPine Script (TradingView)MQL5 (MetaTrader 5)
ExecutionBar-based, backtesting onlyTick-based, live trading
DeploymentTradingView alertsRuns 24/5 on VPS/MT5
Broker accessVia TradingView broker integrationDirect broker connectivity
BacktestingBuilt-in, no data download neededStrategy Tester, tick data required
Code complexitySimpler, functional syntaxC++-like, more powerful

Pineify supports both platforms. Prototype your strategy visually in TradingView Pine Script, then generate the equivalent MQL5 EA for live MT5 trading.

Frequently Asked Questions

Related MQL5 Expert Advisors