EMA Indicator MT5: MQL5 Custom EMA Code, Multi-EMA & EA Integration
This page covers how to build a custom EMA indicator in MQL5 for MetaTrader 5, including single and multi-EMA setups, iMA handle pattern usage, and how to wire EMA signals into an Expert Advisor. It is aimed at traders who want to replicate or extend the built-in Moving Average indicator with their own alert logic, cross detection, and EA integration.
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 long trade is triggered when the fast EMA (e.g. 9-period) crosses above the slow EMA (e.g. 21-period) on the close of a confirmed bar. An additional filter checks that the close is above the trend EMA (e.g. 50-period) to avoid counter-trend entries. The EA places a market buy order using MqlTradeRequest on the next tick after the crossover bar closes.
Exit Conditions
The position is closed when the fast EMA crosses back below the slow EMA, signalling momentum reversal. A trailing stop set at a configurable ATR multiple is also applied so that open profits are protected as the trend extends. If neither exit fires, a fixed take-profit level calculated as 2x the initial stop-loss distance caps the maximum gain per trade.
MQL5 Expert Advisor Code
//+------------------------------------------------------------------+
//| MultiEMA_Indicator.mq5 |
//| Custom multi-EMA indicator with EA integration example |
//| For educational purposes. Past results do not guarantee future |
//| performance. Trade at your own risk. |
//+------------------------------------------------------------------+
#property copyright "Pineify Example"
#property link "https://pineify.app"
#property version "1.00"
#property strict
//--- indicator settings (when compiled as indicator)
#property indicator_chart_window
#property indicator_buffers 3
#property indicator_plots 3
#property indicator_label1 "Fast EMA"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
#property indicator_style1 STYLE_SOLID
#property indicator_width1 2
#property indicator_label2 "Slow EMA"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrOrange
#property indicator_style2 STYLE_SOLID
#property indicator_width2 2
#property indicator_label3 "Trend EMA"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrGray
#property indicator_style3 STYLE_DOT
#property indicator_width3 1
//--- input parameters
input int InpFastPeriod = 9; // Fast EMA period
input int InpSlowPeriod = 21; // Slow EMA period
input int InpTrendPeriod = 50; // Trend EMA period
input ENUM_APPLIED_PRICE InpPrice = PRICE_CLOSE; // Applied price
input bool InpEnableAlerts = true; // Enable cross alerts
//--- indicator buffers
double FastEMABuffer[];
double SlowEMABuffer[];
double TrendEMABuffer[];
//--- indicator handles
int hFastEMA = INVALID_HANDLE;
int hSlowEMA = INVALID_HANDLE;
int hTrendEMA = INVALID_HANDLE;
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- map buffers
SetIndexBuffer(0, FastEMABuffer, INDICATOR_DATA);
SetIndexBuffer(1, SlowEMABuffer, INDICATOR_DATA);
SetIndexBuffer(2, TrendEMABuffer, INDICATOR_DATA);
//--- create handles
hFastEMA = iMA(_Symbol, PERIOD_CURRENT, InpFastPeriod, 0, MODE_EMA, InpPrice);
hSlowEMA = iMA(_Symbol, PERIOD_CURRENT, InpSlowPeriod, 0, MODE_EMA, InpPrice);
hTrendEMA = iMA(_Symbol, PERIOD_CURRENT, InpTrendPeriod, 0, MODE_EMA, InpPrice);
if(hFastEMA == INVALID_HANDLE || hSlowEMA == INVALID_HANDLE || hTrendEMA == INVALID_HANDLE)
{
Print("Error creating EMA handles: ", GetLastError());
return INIT_FAILED;
}
//--- short name shown in Data Window
IndicatorSetString(INDICATOR_SHORTNAME,
StringFormat("MultiEMA(%d,%d,%d)", InpFastPeriod, InpSlowPeriod, InpTrendPeriod));
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(hFastEMA != INVALID_HANDLE) IndicatorRelease(hFastEMA);
if(hSlowEMA != INVALID_HANDLE) IndicatorRelease(hSlowEMA);
if(hTrendEMA != INVALID_HANDLE) IndicatorRelease(hTrendEMA);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
if(rates_total < InpTrendPeriod) return 0;
int to_copy = rates_total - prev_calculated + 1;
if(prev_calculated == 0) to_copy = rates_total;
//--- copy values from handles into buffers
if(CopyBuffer(hFastEMA, 0, 0, to_copy, FastEMABuffer) < 0) return 0;
if(CopyBuffer(hSlowEMA, 0, 0, to_copy, SlowEMABuffer) < 0) return 0;
if(CopyBuffer(hTrendEMA, 0, 0, to_copy, TrendEMABuffer) < 0) return 0;
//--- alert on confirmed bar cross (index 1 = last closed bar)
if(InpEnableAlerts && prev_calculated > 0)
{
bool bullCross = FastEMABuffer[1] > SlowEMABuffer[1] &&
FastEMABuffer[2] <= SlowEMABuffer[2];
bool bearCross = FastEMABuffer[1] < SlowEMABuffer[1] &&
FastEMABuffer[2] >= SlowEMABuffer[2];
if(bullCross)
Alert(_Symbol, " EMA Bullish Cross — Fast(", InpFastPeriod, ") crossed above Slow(", InpSlowPeriod, ")");
if(bearCross)
Alert(_Symbol, " EMA Bearish Cross — Fast(", InpFastPeriod, ") crossed below Slow(", InpSlowPeriod, ")");
}
return rates_total;
}
//+------------------------------------------------------------------+
//| EA HELPER FUNCTIONS — usable when embedding in an Expert Advisor|
//+------------------------------------------------------------------+
//--- Global EA handles (declare in EA's global scope)
int g_hFast = INVALID_HANDLE;
int g_hSlow = INVALID_HANDLE;
int g_hTrend = INVALID_HANDLE;
//--- Call from EA OnInit()
bool EMA_Init(int fastPeriod, int slowPeriod, int trendPeriod)
{
g_hFast = iMA(_Symbol, PERIOD_CURRENT, fastPeriod, 0, MODE_EMA, PRICE_CLOSE);
g_hSlow = iMA(_Symbol, PERIOD_CURRENT, slowPeriod, 0, MODE_EMA, PRICE_CLOSE);
g_hTrend = iMA(_Symbol, PERIOD_CURRENT, trendPeriod, 0, MODE_EMA, PRICE_CLOSE);
return (g_hFast != INVALID_HANDLE && g_hSlow != INVALID_HANDLE && g_hTrend != INVALID_HANDLE);
}
//--- Call from EA OnDeinit()
void EMA_Deinit()
{
if(g_hFast != INVALID_HANDLE) { IndicatorRelease(g_hFast); g_hFast = INVALID_HANDLE; }
if(g_hSlow != INVALID_HANDLE) { IndicatorRelease(g_hSlow); g_hSlow = INVALID_HANDLE; }
if(g_hTrend != INVALID_HANDLE) { IndicatorRelease(g_hTrend); g_hTrend = INVALID_HANDLE; }
}
//--- Returns +1 for bullish cross, -1 for bearish cross, 0 for no signal
int EMA_Signal()
{
double fast[3], slow[3], trend[3];
if(CopyBuffer(g_hFast, 0, 0, 3, fast) < 3) return 0;
if(CopyBuffer(g_hSlow, 0, 0, 3, slow) < 3) return 0;
if(CopyBuffer(g_hTrend, 0, 0, 3, trend) < 3) return 0;
// Index 1 = last closed bar; index 2 = bar before that
bool bullCross = (fast[1] > slow[1]) && (fast[2] <= slow[2]) && (fast[1] > trend[1]);
bool bearCross = (fast[1] < slow[1]) && (fast[2] >= slow[2]) && (fast[1] < trend[1]);
if(bullCross) return 1;
if(bearCross) return -1;
return 0;
}
//--- Place a market order (EA OnTick example)
bool EMA_PlaceOrder(int signal, double lotSize, int slPoints, int tpPoints)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = lotSize;
request.type = (signal == 1) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
request.price = (signal == 1) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
: SymbolInfoDouble(_Symbol, SYMBOL_BID);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
request.sl = (signal == 1) ? request.price - slPoints * point
: request.price + slPoints * point;
request.tp = (signal == 1) ? request.price + tpPoints * point
: request.price - tpPoints * point;
request.deviation = 10;
request.magic = 20240101;
request.comment = "MultiEMA";
request.type_filling = ORDER_FILLING_IOC;
if(!OrderSend(request, result))
{
Print("OrderSend failed: ", result.retcode, " — ", result.comment);
return false;
}
return true;
}
//+------------------------------------------------------------------+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.