Complete MQL5 guide & code

Moving Average MQL5: iMA Methods and a Crossover EA Example

This tutorial uses iMA handles in a complete educational Expert Advisor. Save the source in MQL5/Experts, not Indicators. It compares completed-bar moving averages, sends fixed-volume orders with initial ATR-based exits, and includes a baseline-equity guard. It does not plot a custom indicator. No reproducible backtest or production-readiness claim accompanies the example; verify its rules and broker constraints before use.

Implementation details & mechanics

Core implementation pattern

At the first available tick of a new chart bar, compare completed bars 2 and 1 for a fast/slow crossover. Defaults are a 9-period EMA and a 21-period SMA; the method inputs also support SMMA and LWMA. The EA requests closure of matching opposite positions and opens only after a fresh position query confirms none remain. A failed or partial close therefore prevents immediate reversal. This example fixes volume rather than cash risk and has no spread, session, or news filter.

Error handling & safety rules

Orders request stops and targets at 1.5 and 2.5 times the completed-bar ATR from the entry quote. An opposite crossover requests closure. On every chart tick, the guard compares account equity with the equity snapshot taken at initialization; after the configured decline it latches off new entries and retries closing only this symbol/magic positions. The guard is not a peak-equity drawdown measure, an account-wide close-all, or persistent across restarts. Deposits, withdrawals, and other strategies affect account equity; gaps and rejected orders can exceed the threshold. The fixed FOK policy must be supported by the broker.

MQL5 Code Implementation

//+------------------------------------------------------------------+
//| MovingAverageCrossover.mq5                                       |
//| Dual Moving Average Crossover EA — EMA fast / SMA slow           |
//| Educational EA: compile and test with broker-specific settings  |
//+------------------------------------------------------------------+
#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;
bool g_guardStopped = false;

//+------------------------------------------------------------------+
//| Expert initialisation function                                    |
//+------------------------------------------------------------------+
int OnInit()
  {
   if(FastMAPeriod <= 0 || SlowMAPeriod <= 0 || ATRPeriod <= 0 ||
      LotSize <= 0 || ATRMultSL <= 0 || ATRMultTP <= 0 ||
      MaxDrawdownPct <= 0 || MaxDrawdownPct >= 100) return INIT_PARAMETERS_INCORRECT;
   //--- 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()
  {
   double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
   if(g_startingEquity > 0 &&
      (g_startingEquity - currentEquity) / g_startingEquity * 100.0 >= MaxDrawdownPct)
      g_guardStopped = true;
   if(g_guardStopped)
     {
      CloseAllPositions();
      return;
     }
   static datetime s_lastBarTime = 0;
   datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(currentBarTime <= 0 || currentBarTime == s_lastBarTime) return;

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

   //--- Static array physical order: index 0 = bar 2, index 1 = bar 1
   double fastCur  = fastMA[1];
   double fastPrev = fastMA[0];
   double slowCur  = slowMA[1];
   double slowPrev = slowMA[0];
   double atr      = atrBuf[0];
   if(atr <= 0 || atr == EMPTY_VALUE) return;
   s_lastBarTime = currentBarTime;

   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(!PositionExists(POSITION_TYPE_SELL) && !PositionExists(POSITION_TYPE_BUY)) OpenTrade(ORDER_TYPE_BUY, atr);
     }

   //--- Bearish crossover: close buys, open sell
   if(bearishCross)
     {
      if(hasBuy)  ClosePositionsByType(POSITION_TYPE_BUY);
      if(!PositionExists(POSITION_TYPE_BUY) && !PositionExists(POSITION_TYPE_SELL)) 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) ||
      (result.retcode != TRADE_RETCODE_DONE && result.retcode != TRADE_RETCODE_DONE_PARTIAL))
     {
      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) ||
         (res.retcode != TRADE_RETCODE_DONE && res.retcode != TRADE_RETCODE_DONE_PARTIAL))
         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 your MQL5 folder (Experts, Scripts, or Include), and compile with F7.

Generate custom MQL5 code with Pineify AI

Describe the trade execution, indicator calculations, risk management, or data processing logic you need. Pineify generates editable MQL5 source code. Review its logic, request a syntax check, and compile and test the final file 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 accessStrategy orders are simulated; live automation needs a separate 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 reference guide before porting logic between them.

Frequently Asked Questions

Related MQL5 pages

Risk and testing note

These educational examples require independent compilation and testing. A compile pass or an illustrative setting does not establish execution correctness or profitability. Any reported performance must be supported by reproducible data and test settings; past results do not predict future returns. Trading involves significant risk of loss. This content is not financial advice.