Custom Indicator MQL5: Complete Coding Tutorial for Traders 2026

This page provides a complete step-by-step tutorial for building custom indicators in MQL5, covering buffer setup, drawing styles, input parameters, and multi-timeframe data access. Whether you are a beginner writing your first OnCalculate() function or an experienced coder looking to implement advanced buffer indexing, this guide walks through every concept with working code examples.

custom indicator mql5mql5 custom indicator tutorialhow to create indicator mql5mql5 indicator programmingmetatrader 5 custom indicator code

Strategy Logic

Entry Conditions

N/A — this is a tutorial/indicator reference page. The example indicator demonstrates how to compute and plot a dual-smoothed moving average overlay, which traders can adapt into their own entry signal logic.

Exit Conditions

N/A — this is a tutorial/indicator reference page. Exit conditions are not defined here; the focus is on correct MQL5 indicator architecture, buffer registration, and real-time recalculation patterns.

MQL5 Expert Advisor Code

//+------------------------------------------------------------------+
//|  DualSmoothMA.mq5                                                |
//|  Custom Indicator MQL5 Tutorial — Pineify.app                    |
//|  Demonstrates: buffers, inputs, OnCalculate, OnInit, OnDeinit    |
//+------------------------------------------------------------------+
#property copyright   "Pineify.app"
#property link        "https://pineify.app"
#property version     "1.00"
#property description "Dual-smoothed Moving Average — MQL5 tutorial indicator"

// Indicator drawn in the main chart window
#property indicator_chart_window

// Two indicator buffers: fast MA and slow MA
#property indicator_buffers 2
#property indicator_plots   2

//--- Plot 1: Fast MA
#property indicator_label1  "Fast MA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2

//--- Plot 2: Slow MA
#property indicator_label2  "Slow MA"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrOrangeRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

//--- Input parameters
input int    InpFastPeriod  = 9;          // Fast MA period
input int    InpSlowPeriod  = 21;         // Slow MA period
input int    InpSmoothPeriod = 3;         // Secondary smoothing period
input ENUM_MA_METHOD    InpMAMethod  = MODE_EMA;  // MA method
input ENUM_APPLIED_PRICE InpPrice    = PRICE_CLOSE; // Applied price

//--- Indicator buffers
double FastMABuffer[];
double SlowMABuffer[];

//--- Indicator handles for internal MA calculations
int handleFastMA  = INVALID_HANDLE;
int handleSlowMA  = INVALID_HANDLE;
int handleSmoothFast = INVALID_HANDLE;
int handleSmoothSlow = INVALID_HANDLE;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Validate input periods
   if(InpFastPeriod <= 0 || InpSlowPeriod <= 0 || InpSmoothPeriod <= 0)
     {
      Print("ERROR: All period inputs must be greater than zero.");
      return INIT_PARAMETERS_INCORRECT;
     }
   if(InpFastPeriod >= InpSlowPeriod)
     {
      Print("ERROR: Fast period must be less than slow period.");
      return INIT_PARAMETERS_INCORRECT;
     }

//--- Map arrays to indicator buffers (index matches #property indicator_buffers order)
   SetIndexBuffer(0, FastMABuffer, INDICATOR_DATA);
   SetIndexBuffer(1, SlowMABuffer, INDICATOR_DATA);

//--- Set empty value for gaps (bars not yet calculated)
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);

//--- Shift labels to align with price visually
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, InpFastPeriod + InpSmoothPeriod - 2);
   PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, InpSlowPeriod + InpSmoothPeriod - 2);

//--- Create handles for first-pass MA (fast and slow)
   handleFastMA = iMA(_Symbol, _Period, InpFastPeriod, 0, InpMAMethod, InpPrice);
   if(handleFastMA == INVALID_HANDLE)
     {
      Print("ERROR: Failed to create fast MA handle. Error=", GetLastError());
      return INIT_FAILED;
     }

   handleSlowMA = iMA(_Symbol, _Period, InpSlowPeriod, 0, InpMAMethod, InpPrice);
   if(handleSlowMA == INVALID_HANDLE)
     {
      Print("ERROR: Failed to create slow MA handle. Error=", GetLastError());
      return INIT_FAILED;
     }

//--- Create handles for second-pass smoothing (MA of MA)
   handleSmoothFast = iMAOnArray(FastMABuffer, 0, InpSmoothPeriod, 0, MODE_SMA, 0);
   // Note: For tutorial clarity we smooth inside OnCalculate manually below.
   // iMAOnArray is shown here as reference; we release this handle immediately.
   if(handleSmoothFast != INVALID_HANDLE)
      IndicatorRelease(handleSmoothFast);

//--- Set short name visible in the Data Window
   string shortName = StringFormat("DualSmoothMA(%d,%d,%d)", InpFastPeriod, InpSlowPeriod, InpSmoothPeriod);
   IndicatorSetString(INDICATOR_SHORTNAME, shortName);

   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Release all indicator handles to free terminal resources
   if(handleFastMA != INVALID_HANDLE)
     {
      IndicatorRelease(handleFastMA);
      handleFastMA = INVALID_HANDLE;
     }
   if(handleSlowMA != INVALID_HANDLE)
     {
      IndicatorRelease(handleSlowMA);
      handleSlowMA = INVALID_HANDLE;
     }
   Print("DualSmoothMA deinitialized. Reason code=", reason);
  }

//+------------------------------------------------------------------+
//| 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[])
  {
//--- Minimum bars needed before we can draw anything
   int minBars = InpSlowPeriod + InpSmoothPeriod;
   if(rates_total < minBars)
      return 0;

//--- Determine the starting bar for this call
//    prev_calculated == 0 means a full recalculation is required
   int start = (prev_calculated == 0) ? minBars - 1 : prev_calculated - 1;

//--- Copy fast MA values from handle into a temporary buffer
   double tempFast[];
   double tempSlow[];
   ArraySetAsSeries(tempFast, false);
   ArraySetAsSeries(tempSlow, false);

   if(CopyBuffer(handleFastMA, 0, 0, rates_total, tempFast) <= 0)
     {
      Print("ERROR: CopyBuffer fast MA failed. Error=", GetLastError());
      return prev_calculated;
     }
   if(CopyBuffer(handleSlowMA, 0, 0, rates_total, tempSlow) <= 0)
     {
      Print("ERROR: CopyBuffer slow MA failed. Error=", GetLastError());
      return prev_calculated;
     }

//--- Apply secondary SMA smoothing and write to output buffers
   for(int i = start; i < rates_total; i++)
     {
      //--- Fast: SMA of fast MA values over InpSmoothPeriod bars
      double sumFast = 0.0;
      double sumSlow = 0.0;
      int    count   = 0;
      for(int k = 0; k < InpSmoothPeriod && (i - k) >= 0; k++)
        {
         sumFast += tempFast[i - k];
         sumSlow += tempSlow[i - k];
         count++;
        }
      FastMABuffer[i] = (count > 0) ? sumFast / count : EMPTY_VALUE;
      SlowMABuffer[i] = (count > 0) ? sumSlow / count : EMPTY_VALUE;
     }

//--- Return the total number of bars processed
   return rates_total;
  }
//+------------------------------------------------------------------+

Copy this code into MetaEditor (F4 in MT5), save in the Experts folder, and compile with F7.

Generate a Custom Multi-pair Educational 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

AspectPine Script (TradingView)MQL5 (MetaTrader 5)
ExecutionBar-based, backtesting onlyTick-based, live trading
DeploymentTradingView alertsRuns 24/5 on VPS/MT5
Broker accessVia TradingView broker integrationDirect broker connectivity
BacktestingBuilt-in, no data download neededStrategy Tester, tick data required
Code complexitySimpler, functional syntaxC++-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.

Frequently Asked Questions

Related MQL5 Expert Advisors