ATR in MQL5 is a good exercise in building an indicator that plots in its own window and uses Wilder’s smoothing. See the explainer for why True Range includes the previous close.
What you’ll need
- MetaTrader 5 with MetaEditor
- Save as
AlgoGen.ATR.mq5underMQL5/Indicators/
The source
//+------------------------------------------------------------------+
//| AlgoGen.ATR.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_separate_window
#property indicator_buffers 2
#property indicator_plots 1
#property indicator_label1 "ATR"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrOrangeRed
#property indicator_width1 2
input int InpPeriod = 14; // ATR period
double ATRBuffer[], TRBuffer[];
int OnInit()
{
SetIndexBuffer(0, ATRBuffer, INDICATOR_DATA);
SetIndexBuffer(1, TRBuffer, INDICATOR_CALCULATIONS);
IndicatorSetString(INDICATOR_SHORTNAME, "AlgoGen ATR(" + (string)InpPeriod + ")");
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);
// True Range for each bar.
int trStart = (prev_calculated > 1) ? prev_calculated - 1 : 0;
for(int i = trStart; i < rates_total; i++)
{
if(i == 0)
TRBuffer[0] = high[0] - low[0];
else
{
double hl = high[i] - low[i];
double hc = MathAbs(high[i] - close[i - 1]);
double lc = MathAbs(low[i] - close[i - 1]);
TRBuffer[i] = MathMax(hl, MathMax(hc, lc));
}
}
// Wilder smoothing of TR.
if(prev_calculated == 0)
{
double sum = 0.0;
for(int j = 1; j <= InpPeriod; j++) sum += TRBuffer[j];
ATRBuffer[InpPeriod] = sum / InpPeriod;
for(int i = InpPeriod + 1; i < rates_total; i++)
ATRBuffer[i] = (ATRBuffer[i - 1] * (InpPeriod - 1) + TRBuffer[i]) / InpPeriod;
}
else
{
for(int i = prev_calculated - 1; i < rates_total; i++)
if(i > InpPeriod)
ATRBuffer[i] = (ATRBuffer[i - 1] * (InpPeriod - 1) + TRBuffer[i]) / InpPeriod;
}
return(rates_total);
}
//+------------------------------------------------------------------+
Install & compile
- Drop it in
MQL5/Indicators/, press F7. - Drag onto a chart — a separate window shows the orange ATR line, matching the output chart.
Gotchas
- The gap terms.
hcandlccompare toclose[i-1]— that’s what captures overnight/limit gaps. Dropping them collapses ATR into a plain range and defeats the point. - Wilder seed. The first ATR value is a simple average of the first
InpPeriodTrue Ranges; subsequent bars use the1/Nrecurrence. - Built-in / MQL4. MT5 has
iATR; MQL4 hasiATRtoo and the same recurrence applies if you hand-roll it. The custom version lets you also expose True Range for other calculations.
Same indicator elsewhere: Python, Pine Script, EasyLanguage, NinjaScript. Then backtest an ATR stop 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
- New Concepts in Technical Trading Systems (Windsor Books)
- Technical indicator functions (MetaQuotes)
