Complete .mq5 source code

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.

How the Supertrend signal works

Bullish flip

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.

Bearish flip

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 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;
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, save it in the MQL5/Indicators folder, and compile with F7.

Change the settings or add your own signal filter

Describe the ATR inputs, alert rules, 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 accessVia TradingView broker 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 Supertrend reference before porting logic between them.

Frequently Asked Questions

Related MQL5 pages

Risk and testing note

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.