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.
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
| 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.