Complete .mq5 source code

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

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.

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.