MetaTrader ships Bollinger Bands, but building your own is the cleanest way to learn how MT5 handles multi-buffer overlays — and to be certain your bands use the correct (population) standard deviation. See the explainer for what we’re drawing.
What you’ll need
- MetaTrader 5 with MetaEditor
- Save as
AlgoGen.Bollinger.mq5underMQL5/Indicators/
The source
Three plotted buffers — middle, upper, lower — computed with a simple average and the population standard deviation over the same window.
//+------------------------------------------------------------------+
//| AlgoGen.Bollinger.mq5 |
//| Copyright 2026, AlgoGen |
//| https://www.algogen.io |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, AlgoGen"
#property link "https://www.algogen.io"
#property version "1.00"
#property indicator_chart_window
#property indicator_buffers 3
#property indicator_plots 3
#property indicator_label1 "Middle"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrOrangeRed
#property indicator_width1 2
#property indicator_label2 "Upper"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrMediumPurple
#property indicator_label3 "Lower"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrMediumPurple
input int InpPeriod = 20; // Period
input double InpMult = 2.0; // Std-dev multiplier
double MidBuffer[], UpBuffer[], LoBuffer[];
int OnInit()
{
SetIndexBuffer(0, MidBuffer, INDICATOR_DATA);
SetIndexBuffer(1, UpBuffer, INDICATOR_DATA);
SetIndexBuffer(2, LoBuffer, INDICATOR_DATA);
for(int p = 0; p < 3; p++)
PlotIndexSetInteger(p, PLOT_DRAW_BEGIN, InpPeriod - 1);
IndicatorSetString(INDICATOR_SHORTNAME,
StringFormat("AlgoGen BB(%d,%.1f)", InpPeriod, InpMult));
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 < InpPeriod)
return(0);
int start = (prev_calculated > InpPeriod) ? prev_calculated - 1 : InpPeriod - 1;
for(int i = start; i < rates_total; i++)
{
double sum = 0.0;
for(int j = 0; j < InpPeriod; j++)
sum += close[i - j];
double mean = sum / InpPeriod;
double sqsum = 0.0;
for(int j = 0; j < InpPeriod; j++)
{
double d = close[i - j] - mean;
sqsum += d * d;
}
double sd = MathSqrt(sqsum / InpPeriod); // population std (divide by N)
MidBuffer[i] = mean;
UpBuffer[i] = mean + InpMult * sd;
LoBuffer[i] = mean - InpMult * sd;
}
return(rates_total);
}
//+------------------------------------------------------------------+
Install & compile
- Drop it in
MQL5/Indicators/, press F7 to compile. - Drag onto a chart — three lines overlay price, matching the output chart.
Gotchas
- Divide by N, not N−1.
sqsum / InpPeriodgives the population standard deviation Bollinger Bands require. Dividing byInpPeriod - 1(sample std) makes the bands too wide — the same trap as pandas’ddof. - The built-in shortcut. In an EA you can use
iBands(...)andCopyBuffer. MT5’s built-in already uses population std, so this hand-rolled version matches it — good confirmation your loop is right. - MQL4? Same math; swap the
OnCalculatesignature and buffer setup, or calliBands.
Same indicator elsewhere: Python, Pine Script, EasyLanguage, NinjaScript. Then backtest it in AlgoGen.
This post is educational, not financial advice. Indicators describe the past; they don’t predict the future. Backtest anything before you risk real money on it.
Sources and further reading
- John Bollinger's official Bollinger Bands reference (Bollinger Capital Management)
- Technical indicator functions (MetaQuotes)
