Complete .mq5 source code

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.

Historical test results

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.

How the Supertrend signal works

Bullish flip

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.

Bearish flip

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 Custom Indicator 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, save it in the MQL5/Indicators folder, and compile with F7.

Change the settings or add your own signal filter

Describe the ATR inputs, alert rules, visual style, or confirmation filter you want. Pineify generates editable MQL5 source code that you can inspect and compile in MetaEditor.

Pine Script vs MQL5: Same Strategy, Different Platforms

AspectPine Script (TradingView)MQL5 (MetaTrader 5)
ExecutionSeries evaluated on chart updatesEvent handlers with explicit buffers
DeploymentRuns in TradingView with alertsRuns in the MT5 terminal or on a VPS
Broker accessVia TradingView broker integrationDirect broker connectivity
BacktestingStrategy Tester for strategy scriptsStrategy Tester for Expert Advisors
Code complexitySimpler, functional syntaxC++-like, more powerful

The calculation can be implemented on either platform, but the runtime model and buffer APIs differ. Read the Pine Script Supertrend reference before porting logic between them.

Frequently Asked Questions

Related MQL5 pages

Risk and testing note

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.