Build Your First Custom Indicator

Step-by-step guide to creating a working custom indicator with indicator buffers and chart output.

What We Will Build

In this tutorial, we will build a complete custom indicator from scratch: a Simple Price Channel that draws upper and lower bands around price based on the highest high and lowest low over a configurable period. This is a practical indicator that many traders use for breakout detection.

Indicator Structure

Every MQL5 indicator has three mandatory sections:

  • Property declarations — Tell MT5 how to display the indicator (number of buffers, draw types, colors)
  • OnInit() — Initialize buffers and settings
  • OnCalculate() — Calculate values for each bar

Complete Source Code

//+------------------------------------------------------------------+
//| PriceChannel.mq5                                                  |
//| Custom Price Channel Indicator                                     |
//+------------------------------------------------------------------+
#property indicator_chart_window          // draw on main chart
#property indicator_buffers 2             // we need 2 data buffers
#property indicator_plots   2             // we draw 2 lines

// Plot 0: Upper Channel
#property indicator_label1  "Upper Channel"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrDodgerBlue
#property indicator_width1  2

// Plot 1: Lower Channel
#property indicator_label2  "Lower Channel"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrOrangeRed
#property indicator_width2  2

// Input parameters (user-configurable)
input int ChannelPeriod = 20;  // Channel Period

// Indicator buffers
double upperBuffer[];
double lowerBuffer[];

//+------------------------------------------------------------------+
int OnInit()
{
    // Validate inputs
    if(ChannelPeriod < 2)
    {
        Print("Error: Channel Period must be >= 2");
        return INIT_PARAMETERS_INCORRECT;
    }

    // Map buffers to plots
    SetIndexBuffer(0, upperBuffer, INDICATOR_DATA);
    SetIndexBuffer(1, lowerBuffer, INDICATOR_DATA);

    // Set indicator name in the Data Window
    IndicatorSetString(INDICATOR_SHORTNAME,
                       "Price Channel (" + IntegerToString(ChannelPeriod) + ")");

    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
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[])
{
    // Not enough bars to calculate
    if(rates_total < ChannelPeriod)
        return 0;

    // Determine starting point
    int start;
    if(prev_calculated == 0)
        start = ChannelPeriod - 1;  // first run: start after enough bars
    else
        start = prev_calculated - 1; // subsequent: only process new bars

    // Calculate for each bar
    for(int i = start; i < rates_total; i++)
    {
        double highestHigh = high[i];
        double lowestLow   = low[i];

        // Find highest high and lowest low over the period
        for(int j = 1; j < ChannelPeriod; j++)
        {
            if(high[i - j] > highestHigh)
                highestHigh = high[i - j];
            if(low[i - j] < lowestLow)
                lowestLow = low[i - j];
        }

        upperBuffer[i] = highestHigh;
        lowerBuffer[i] = lowestLow;
    }

    return rates_total;
}
//+------------------------------------------------------------------+

Step-by-Step Breakdown

#property Declarations

These tell MT5 how to render the indicator:

  • indicator_chart_window — Draw on the main price chart (vs indicator_separate_window for oscillators)
  • indicator_buffers 2 — We need 2 data arrays
  • indicator_plots 2 — We draw 2 visible elements
  • indicator_type1 DRAW_LINE — Plot 0 draws as a line

SetIndexBuffer

This function connects your array to a plot. The first parameter is the buffer index (0-based), the second is your array, and the third specifies the buffer type:

  • INDICATOR_DATA — Contains values to draw
  • INDICATOR_COLOR_INDEX — Contains color indices for multi-colored plots
  • INDICATOR_CALCULATIONS — Internal calculation buffer (not drawn)

The Calculation Loop

The key optimization is using prev_calculated. On the first call, it is 0 and we calculate all bars. On subsequent calls, it tells us how many bars were already processed, so we only calculate new bars. This makes the indicator efficient even on charts with thousands of bars.

💡
Professional quality matters

This tutorial shows the fundamentals. Production-quality indicators need additional features: multiple drawing styles, customizable colors, alert conditions, multi-timeframe support, and robust error handling. Building professional indicators requires deep MQL5 expertise — that is what we offer as a service.

Testing Your Indicator

1
Compile with F7

Fix any errors shown in the output panel. Common mistakes: missing semicolons, undeclared variables, wrong buffer count.

2
Attach to a chart

In MT5, find your indicator in Navigator > Indicators > Custom, and drag it onto a chart. Adjust the period in the dialog.

3
Verify visually

The upper line should touch the highest candle highs, and the lower line should touch the lowest candle lows over the period. Test with different period values.

Common Beginner Mistakes

  • Wrong buffer countindicator_buffers must match the total number of SetIndexBuffer() calls
  • Forgetting to return rates_totalOnCalculate must return how many bars were processed
  • Array out of bounds — Accessing high[i - j] when i - j is negative
  • Not handling prev_calculated — Recalculating all bars on every tick kills performance