Custom Indicator MQL5: Complete Coding Tutorial for Traders 2026
Build a two-line MQL5 indicator using registered buffers, iMA handles and manual secondary SMA smoothing. Inspect complete source, warm-up handling, CopyBuffer checks and the iCustom handle workflow. The example uses the current chart symbol and timeframe and does not contain a trading strategy.
How the indicator signal works
Calculation and signal conditions
OnInit registers two output buffers and creates fast and slow iMA handles. OnCalculate copies each full history into chronological arrays, then averages the most recent InpSmoothPeriod values for each displayed bar. Both plots wait for the slow line’s warm-up window. There are no entry signals or orders.
Interpretation and limitations
The forming bar can change with new prices. A successful incremental call recalculates the last displayed bar, while a history reset initializes both plots again. The example still copies full histories on each call, so it is not a benchmark for large-history efficiency. Release valid handles when the indicator unloads; no exit or multi-timeframe trading logic is implemented.
MQL5 Custom Indicator 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;
//+------------------------------------------------------------------+
//| 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);
//--- Hide warm-up samples before valid smoothed output
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;
}
//--- 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
bool reset = (prev_calculated<=0 || prev_calculated>rates_total);
if(reset)
{
ArrayInitialize(FastMABuffer,EMPTY_VALUE);
ArrayInitialize(SlowMABuffer,EMPTY_VALUE);
}
int start = reset ? minBars - 1 : MathMax(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) != rates_total)
{
Print("ERROR: CopyBuffer fast MA failed. Error=", GetLastError());
return prev_calculated;
}
if(CopyBuffer(handleSlowMA, 0, 0, rates_total, tempSlow) != rates_total)
{
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, 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
| 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 | Strategy orders are simulated; live automation needs a separate 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.
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.