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
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
| Aspect | Pine Script (TradingView) | MQL5 (MetaTrader 5) |
|---|---|---|
| Execution | Series evaluated on chart updates | Event handlers with explicit buffers |
| Deployment | Runs in TradingView with alerts | Runs in the MT5 terminal or on a VPS |
| Broker access | Via TradingView broker integration | Direct broker connectivity |
| Backtesting | Strategy Tester for strategy scripts | Strategy Tester for Expert Advisors |
| Code complexity | Simpler, functional syntax | C++-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.