FXTAA Русская версия

Candles: a simple volatility indicator for the forex market

The distance from high to low is the most honest volatility reading available on a chart. It needs no parameters and hides nothing — which is exactly why it is worth understanding before reaching for ATR.

Updated: 2026-09-22

Volatility is not direction. It tells you how far price is travelling per unit of time, which determines how wide a stop has to be, how big a position can be, and whether a strategy has room to work at all.

Raw range versus ATR

The raw range of a candle is High - Low. The Average True Range adds two things: it averages over N bars, and it accounts for gaps by taking the largest of three distances — current range, previous close to current high, and previous close to current low.

In forex the gap correction matters far less than in equities, because the market trades continuously from Sunday evening to Friday evening. The one place it matters is the weekend gap, and there it matters a lot.

Raw rangeATR
ParametersNonePeriod (default 14)
Reacts to a single barImmediatelyDiluted by the average
Handles gapsNoYes
Good forSeeing what just happenedSizing and stops

A minimal range indicator

This draws each bar's range as a histogram and colours it by whether it exceeds the recent average. That single comparison is most of the value — an absolute pip number means nothing without context.

// CandleRange.mq4 — bar range, coloured against its own average
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_color1 clrSteelBlue
#property indicator_color2 clrIndianRed

input int AvgPeriod = 20;

double normal[], elevated[];

int OnInit()
{
   SetIndexBuffer(0, normal);   SetIndexStyle(0, DRAW_HISTOGRAM, STYLE_SOLID, 2);
   SetIndexBuffer(1, elevated); SetIndexStyle(1, DRAW_HISTOGRAM, STYLE_SOLID, 2);
   IndicatorShortName("Range(" + IntegerToString(AvgPeriod) + ")");
   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[])
{
   int start = MathMax(prev_calculated - 1, AvgPeriod);

   for(int i = start; i < rates_total; i++)
   {
      int bar = rates_total - 1 - i;          // series index
      double range = (high[i] - low[i]) / Point;

      double sum = 0;
      for(int k = 1; k <= AvgPeriod; k++)
         sum += (high[i-k] - low[i-k]) / Point;
      double avg = sum / AvgPeriod;

      normal[bar]   = (range <= avg) ? range : EMPTY_VALUE;
      elevated[bar] = (range >  avg) ? range : EMPTY_VALUE;
   }
   return(rates_total);
}

Normalising so numbers compare

Forty pips means something different on USDJPY than on EURCHF, and something different in August than in October. Divide the current range by the average and you get a unitless ratio that is comparable across pairs and periods.

Using it for position size

The standard application is inverting it: risk a fixed amount of money, let volatility set the stop distance, and let the stop distance set the lot size. This keeps the money at risk constant while the market changes underneath you.

// risk-based lot size from a volatility stop
double riskMoney  = AccountBalance() * 0.01;          // 1% of balance
double atrPips    = iATR(NULL, 0, 14, 1) / Point;
double stopPips   = atrPips * 1.5;                     // stop = 1.5 x ATR
double pipValue   = MarketInfo(Symbol(), MODE_TICKVALUE);
double lots       = riskMoney / (stopPips * pipValue);

lots = MathFloor(lots / MarketInfo(Symbol(), MODE_LOTSTEP))
       * MarketInfo(Symbol(), MODE_LOTSTEP);

Note the 1 as the last argument to iATR — that reads the closed bar. Using 0 reads the bar still forming, whose range grows as you watch, which means your position size depends on what second you happened to click.

Session structure shows up immediately

Plot average range by hour of day and the market's shape becomes obvious: a quiet Asian session, an expansion at the London open, the largest ranges during the London–New York overlap, and a decline afterwards.

This is the cheapest useful filter in trading. A breakout system that trades the Asian range and a breakout system that trades the London open are not the same system, even if the code is identical.

Where the honesty runs out

Range tells you what already happened. It has no predictive content on its own — compression is followed by expansion eventually, but "eventually" is not a trade. Treat it as a measuring instrument for sizing and filtering, not as a signal generator.