Indicator buffers, OnCalculate, and why the very first bars of every indicator are empty.
Beginner9 min readBeginner → AdvancedLesson 04 / 17
Try it in MetaEditor
A hand-rolled SMA so you can see buffers work
Save as MQL5/Indicators/Lesson4SMA.mq5, compile, and attach to a chart. Change the input period in the dialog — this is the same indicator you will reuse in Part 3.
//+------------------------------------------------------------------+
//| Lesson 4 - First indicator: simple moving average |
//+------------------------------------------------------------------+
#property copyright "Strategist Academy"
#property version "1.00"
#property description "Hand-rolled SMA so you can see how buffers work."
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
#property indicator_width1 2
input int SMA_Period = 20; // Period
double SMABuffer[];
int OnInit()
{
if(SMA_Period < 2)
{
Print("SMA_Period must be at least 2");
return(INIT_FAILED);
}
SetIndexBuffer(0, SMABuffer, INDICATOR_DATA);
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, SMA_Period - 1);
PlotIndexSetString(0, PLOT_LABEL, "SMA(" + IntegerToString(SMA_Period) + ")");
IndicatorSetString(INDICATOR_SHORTNAME, "Lesson 4 SMA(" + IntegerToString(SMA_Period) + ")");
// NOTE: we do NOT call ArraySetAsSeries() here, so index 0 is the OLDEST bar.
// This is MQL5's default. Mixing conventions is the classic beginner bug.
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[])
{
if(rates_total < SMA_Period)
return(0); // not enough bars yet - nothing to draw
int start;
if(prev_calculated == 0)
start = SMA_Period - 1; // first bar that CAN be calculated
else
start = prev_calculated - 1; // only recompute the newest bars
if(start < SMA_Period - 1)
start = SMA_Period - 1;
for(int i = start; i < rates_total && !IsStopped(); i++)
{
double sum = 0.0;
for(int k = 0; k < SMA_Period; k++)
sum += close[i - k]; // i is oldest-first: look backwards
SMABuffer[i] = sum / SMA_Period;
}
return(rates_total);
}
To run it
1. Open MetaEditor from MT5 (press F4), and pick the folder this program belongs in — Experts, Indicators, or Scripts.
2. Create a new file, paste this over it, then press F7 to compile. Fix anything the Errors tab reports.
3. Back in MT5, drag it onto a chart — or open the Strategy Tester if it is an Expert Advisor.
Buffers are arrays you fill
An MQL5 indicator does not return a value. You declare one or more buffers (plain double arrays), tell the terminal which are plotted data, and fill them inside OnCalculate.
SetIndexBuffer(0, Buffer, INDICATOR_DATA) — binds your array to plot index 0.
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, n) — hides the first n bars so you never draw a half-calculated average.
The #property indicator_* lines declare buffers, plots, colour, and style up front.
Indexing: 0 is the oldest bar
Unless you call ArraySetAsSeries(arr, true), MQL5 timeseries arrays are indexed with 0 = the oldest bar on the chart. This is the opposite of Pine's default intuition and it is the number one source of "my indicator is backwards" bugs. In this lesson we stay in default (oldest-first) order on purpose — learn one convention before mixing two.
prev_calculated is your optimisation
OnCalculate receives prev_calculated: how many bars you already handled last time. On the first call it is 0 and you compute everything. After that you only need to recompute the newest bars. Ignoring it still works, but it wastes cycles on every tick across thousands of bars.
Always guard for enough data
If the chart has fewer bars than your period, return 0 immediately. Reading past the end of a timeseries array is undefined behaviour and a classic crash on small timeframes or freshly opened symbols.
What you just did
Lesson 04 of 17 in MetaTrader 5 MQL5 for CFD Traders. When you have run the examples or read the section, tick it off and move to the next lesson.