QA GATE · GOOD 18/18 automated ai-check.js · 18 readability, YMYL and image gates · mechanics verified against vike-indicators · live real-data chart embedded
Burstiness (sentence-length SD)9.0
Lexical diversity (MATTR-50)0.83
Consecutive similar sentences2
Repeated paragraph starts (≥3)0
AI-marker phrases0
Demonstrated experience2
Avg sentence length (words)16.5
Repeated 5-grams0
H2 section balance (50–500w)0 off
YMYL risk disclaimeryes
Mechanics verified vs codeyes
Primary keyword coverageyes
Internal links + CTA6
Content visuals10
Every figure has a caption10/10
Alt / caption text coverage100%
Distributed (≥1 per section)5/6 (83%)
Filenames sluggedn/a (inline)
Live interactive chart6 real
Learn · Trading Indicators · Volatility

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

previous closehigh − low|high − prev close||low − prev close|the widest of the three is the true range — here, the thirdthe absolute values are why a backward distance can only win on a gap
One bar, three candidate distances. On a bar whose previous close lies inside its own range, the first candidate always wins and batch_true_range reduces to candle height.
FIL/USDT daily — a real true-range reading detected 2025-11-07, the bar's own high − low won outright at 2.107; the previous close sat inside the range, so neither gap term could beat it.

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

nothing else has emitted a value yettrue rangebar 0no lookbackATRbar 13rma seed completebar 14natr, bar 14ADXbar 27two Wilder passesa detector with no warm-up is also a detector with no context
Zero lookback is a real property and a real hazard: this series will return a number from the first candle of any window you hand it.
ETH/USDT daily — a real true-range reading detected 2018-02-09, |high − prev close| set the true range at 128.2 — the previous close was below the whole bar, and high − low would have reported 100.54, 1.28x too small.

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_range calls math::true_range, which fills the output with zeros, writes high − low into slot zero, then walks forward writing max(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 with batch_adx or batch_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

which term set the rangehigh − low98.92%|high − prev|0.46%|low − prev|0.62%346,928 bars with a previous closewhen a gap won, by how muchmedian1.01xp904.76xp9931.3xworst bar167x
Rare, and not small when it happens. The distribution is the argument for carrying the gap terms on a venue that never closes.
LINK/USDT daily — a real true-range reading detected 2020-03-05, |high − prev close| set the true range at 2.0772 — the previous close was below the whole bar, and high − low would have reported 0.0626, 33.18x too small.
BNB/USDT daily — a real true-range reading detected 2020-03-30, |low − prev close| set the true range at 12.7016 — the previous close was above the whole bar, and high − low would have reported 0.3506, 36.23x too small.

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

gapped upprev closethe winner|high − prev close| winsgapped downprev closethe winner|low − prev close| winssame output type, opposite direction — the absolute value discards the sign
Which term won tells you which way price jumped. The number alone does not, so read direction off the candles.
DOGE/USDT daily — a real true-range reading detected 2020-06-15, |low − prev close| set the true range at 0.0001009 — the previous close was above the whole bar, and high − low would have reported 9.78e-05, 1.03x too small.

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

ICP/USDT daily — a real true-range reading detected 2023-01-07, |low − prev close| set the widest true range on this chart at 2.394, and the very next bar came back to 0.195 — a stop sized on it would have been 12.3x too wide.
The bar above is real and so is its sequel. Its true range was set by a gap and was the widest value on the chart; the next bar printed roughly a third of it. One reading is one bar, and a stop drawn from it inherits that bar's luck rather than the market's behavior.

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.

Read the average true range breakdown