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.
Backtest Performance
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
| Aspect | Pine Script (TradingView) | MQL5 (MetaTrader 5) |
|---|---|---|
| Execution | Bar-based, backtesting only | Tick-based, live trading |
| Deployment | TradingView alerts | Runs 24/5 on VPS/MT5 |
| Broker access | Via TradingView broker integration | Direct broker connectivity |
| Backtesting | Built-in, no data download needed | Strategy Tester, tick data required |
| Code complexity | Simpler, functional syntax | C++-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.