True Range: How the Gap-Aware Bar Measurement Works
A candle's height is not how far price traveled. True range fixes that by asking a wider question — how far did this bar move, counting the distance it jumped before it opened? Vike computes it as batch_true_range, the smallest detector in our indicator engine and the one everything volatility-shaped is built on.
What the true range actually measures
batch_true_range reduces to candle height.Three distances go in. The largest one comes out, and that is the whole function.
First comes the obvious one: this bar's high minus its low. The second and third both reach backwards to the previous close, one from the high and one from the low, each wrapped in an absolute value so neither can go negative. Because of those absolute values, a backward distance can only win when the previous close sits outside this bar's range entirely — which is another way of saying the market gapped.
That single property is worth holding onto. No gap, no difference: the answer is the candle's height, exactly as your eye reads it.
The exact true range formula our engine runs
Textbooks write this as a max over three terms and move on. Our version is fifteen lines, and two of them are surprising.
How Vike computes it.batch_true_rangecallsmath::true_range, which fills the output with zeros, writeshigh − lowinto slot zero, then walks forward writingmax(high − low, |high − prev close|, |low − prev close|). There is no warm-up: every slot is written, the registration declares a lookback of zero, and nothing ever comes back as a missing value. Compare that withbatch_adxorbatch_atr, which both hand you nothing for their first thirteen bars.
Bar zero is the exception, and it has to be. Nothing precedes the first bar of a series, so there is no close to gap from and the function falls back to plain height. That fallback value is real, it is returned, and it is the reason the seeding used by math::atr_v deliberately discards it.
I ported the rule out of the crate and then ran it against the Python original the crate was translated from, over 347,171 daily bars from 243 Binance pairs. Maximum absolute difference: 0.000e+00. The two implementations disagree on exactly one input, an empty series, where Rust hands back an empty vector and Python raises an IndexError. Nothing on a chart reaches it. It is written down anyway, because an undocumented difference is one somebody discovers at the worst moment.
When a gap sets the range instead
Crypto never closes, so the gap terms should be dead weight here. They are not.
Across those 346,928 bars with a previous close to measure from, the true range beat plain height on 3,732 of them — 1.08%. Upward jumps did it 1,596 times through |high − prev close|; downward ones fired 2,136 times, through |low − prev close| instead — this tape gaps down more readily than it gaps up. The median offender understated by a modest 1.01x, but the ninetieth percentile was 4.76x and the worst single bar was 167x. Seven hundred and six bars understated by at least double.
None of those are rounding artifacts, and I learned that the hard way. They are thin-book minutes at a daily boundary, listing days, and the occasional venue hiccup — and every one of them would have been invisible to a volatility measure built on candle height alone.
Reading the two gap terms apart
Sign matters, and the absolute value hides it. That is deliberate, but it means the output alone cannot tell you which way price jumped.
When |high − prev close| sets the range, yesterday's close sat below today's entire bar: price gapped up and left that close stranded underneath. When |low − prev close| sets it, the previous close was stranded above instead. Both produce a single positive number, so if direction matters to you, read it off the candles rather than off this series. When I need the direction I read the term index, which is why every chart here labels the winner.
Where true range misleads
Now the uncomfortable part. A wide true range is a fact about one bar, not a forecast, and it is easy to size a position as though it were.
The last example below is a real gap bar whose true range was the widest anything on its chart had printed, and whose very next bar came back to normal. Anyone who set a stop from that single reading was three and a half times too far away for the market that actually followed. This is precisely the failure mode that smoothing exists to blunt, which is what the average true range does with fourteen of these values at a time.
A quieter trap sits underneath. Because there is no warm-up, this series is defined on bar one of any window you slice — so it will happily give you a confident-looking number computed from a single candle and a boundary that means nothing. The value is real. Its context is not.
Frequently asked questions
What is the difference between true range and high minus low? Nothing at all on 98.92% of the daily bars measured here. On the remaining 1.08% the previous close sat outside the bar, a gap term won, and plain height understated the move — by 4.76x at the ninetieth percentile of those bars.
Does true range have a period setting? No. It reads one bar and the close before it, takes no parameters, and its registration declares no lookback. Every period you have seen attached to it belongs to something built on top, such as ATR.
Can the true range be zero? Only if a bar's high, low and previous close are all identical. Across the tape scanned here that never happened once, though the arithmetic permits it and the function returns the zero without complaint.
Why does the first bar use a different formula?
There is no previous close to gap from. The crate writes high − low into that slot, which is honest but not comparable with the rest — and math::atr_v, one of our two ATR seeds, drops it for that reason.
Which indicators are built on it? The ATR and the normalized ATR most directly, plus the directional movement family through DMI and ADX, the choppiness index, and the vortex indicator. Bounded oscillators such as the stochastic read the high-low range instead and never touch this one.
This is educational material, not financial advice. Every figure here is measured on bars that have already closed, past behavior generalizes poorly, and trading carries real risk of loss — size any position so that being wrong stays survivable.