Complete .mq5 source code

EMA Indicator MT5: MQL5 Custom EMA Code, Multi-EMA & EA Integration

Plot fast, slow and trend EMAs in an MT5 custom indicator and inspect completed-bar crossover alerts. The source also contains signal-only helpers to adapt inside a separate EA. It contains no executable trading strategy or verified backtest results.

How the indicator signal works

Calculation and signal conditions

The indicator plots three iMA outputs on the current chart and alerts on a completed-bar fast/slow crossover once per bar. Its alerts do not use the trend EMA as a filter. The optional EMA_Signal helper for a separate EA returns +1 or -1 only when the fast EMA is also above or below the trend EMA; this is not a close-price filter.

Interpretation and limitations

No positions, ATR trailing stops or fixed targets are managed here. Indicators cannot execute trading operations. A separate EA must define its own position scope, sizing, exits and execution handling, call EMA_Init/EMA_Deinit and gate EMA_Signal as needed. The illustrative 9/21/50 periods are not validated optimums.

MQL5 Custom Indicator 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()
  {
   if(InpFastPeriod<1 || InpSlowPeriod<1 || InpTrendPeriod<1) return INIT_PARAMETERS_INCORRECT;
   //--- map buffers
   SetIndexBuffer(0, FastEMABuffer,  INDICATOR_DATA);
   SetIndexBuffer(1, SlowEMABuffer,  INDICATOR_DATA);
   SetIndexBuffer(2, TrendEMABuffer, INDICATOR_DATA);
   ArraySetAsSeries(FastEMABuffer,true);
   ArraySetAsSeries(SlowEMABuffer,true);
   ArraySetAsSeries(TrendEMABuffer,true);
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,InpFastPeriod-1);
   PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,InpSlowPeriod-1);
   PlotIndexSetInteger(2,PLOT_DRAW_BEGIN,InpTrendPeriod-1);

   //--- 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 < MathMax(3,MathMax(InpTrendPeriod,MathMax(InpFastPeriod,InpSlowPeriod)))) return 0;
   ArraySetAsSeries(time,true);

   int to_copy = rates_total - prev_calculated + 1;
   if(prev_calculated <= 0 || prev_calculated>rates_total) to_copy = rates_total;

   //--- copy values from handles into buffers
   if(CopyBuffer(hFastEMA,  0, 0, to_copy, FastEMABuffer)  != to_copy) return 0;
   if(CopyBuffer(hSlowEMA,  0, 0, to_copy, SlowEMABuffer)  != to_copy) return 0;
   if(CopyBuffer(hTrendEMA, 0, 0, to_copy, TrendEMABuffer) != to_copy) return 0;

   //--- alert on confirmed bar cross (index 1 = last closed bar)
   static datetime lastAlertBar=0;
   if(InpEnableAlerts && prev_calculated > 0 && time[1]!=lastAlertBar)
     {
      lastAlertBar=time[1];
      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[2], slow[2], trend[2];
   if(CopyBuffer(g_hFast,  0, 1, 2, fast)  != 2) return 0;
   if(CopyBuffer(g_hSlow,  0, 1, 2, slow)  != 2) return 0;
   if(CopyBuffer(g_hTrend, 0, 1, 2, trend) != 2) return 0;

   // Static arrays: index 0 = older completed bar, index 1 = latest completed bar
   bool bullCross = (fast[1] > slow[1]) && (fast[0] <= slow[0]) && (fast[1] > trend[1]);
   bool bearCross = (fast[1] < slow[1]) && (fast[0] >= slow[0]) && (fast[1] < trend[1]);

   if(bullCross) return  1;
   if(bearCross) return -1;
   return 0;
  }

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 inputs, buffers, alerts, 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 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

Technical references

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.