Library/Technical Indicators/Volatility and Channels

D07-F04-A01 / Complete engineering topic

True Range

A production-minded guide to True Range.

D07 · TECHNICAL INDICATORS
D07-F04-A01Canonical / Tested / Open
D07 / D07-F04

How large was this bar’s discontinuity-aware price span? That is the practical question behind True Range. This tutorial builds the answer from finalized data, shows the exact arithmetic, and gives you a laboratory for breaking the indicator safely.

The mental model in one minute

True Range is not the distance price actually traveled inside a bar. OHLC data cannot reveal that path. It is the largest of one intrabar span and two previous-close gaps.

The output is a measurement. It is not an order, forecast, confidence interval, or promise that a familiar-looking market pattern will repeat.

Contract before chart

  • Variant: TA-Lib leaves the first bar unavailable. This package uses high minus low at index 0, matching the existing D07 Supertrend package.
  • Defaults: no smoothing parameter.
  • First publication: index 0 under the package high-low fallback.
  • Data: finalized, causal observations on one price basis.
  • Missing, malformed, or mixed-basis input: reject rather than repair silently.

Formula and symbols

TRt=max(HtLt, HtCt1, LtCt1)TR_t=\max(H_t-L_t,\ |H_t-C_{t-1}|,\ |L_t-C_{t-1}|)

SymbolMeaningUnit
HtH_tcurrent highprice units
LtL_tcurrent lowprice units
Ct1C_{t-1}previous closeprice units
TRtTR_tlargest eligible spanprice units

Calculate it by hand

Use a previous close of 100 and a new bar with high 108, low 104, and close 106.

  1. Intrabar span: 108 - 104 = 4.
  2. High gap: |108 - 100| = 8.
  3. Low gap: |104 - 100| = 4.
  4. True Range: max(4, 8, 4) = 8 price units; high-gap is the driver.

The close-to-close change is 6, but True Range is 8. The calculation asks how far the bar’s observed envelope extends from the previous close, not where the bar finished.

That calculation is deliberately small enough to audit without code. The 240-row fixtures then show how the same rule behaves through warm-up, regime changes, ties, gaps, shocks, and expiry.

The complete algorithm

  1. At index zero, publish high minus low under this package’s explicit fallback.
  2. Later, calculate high minus low, absolute high minus previous close, and absolute low minus previous close.
  3. Choose the greatest value. On an exact tie, stable priority is high-low, then high-gap, then low-gap.
  4. Publish all three components and the winning driver so a platform difference can be located.
Rendering system map…

The flow exposes the point where a result becomes unavailable, where recursive state begins, or where a rolling owner expires.

See the mechanism

Mechanism map for True Range

The map connects formula terms to the exact data they consume.

Follow a complete causal trace

Canonical 240-row trace for True Range

Only rows at or before the active observation may influence the displayed state. Warm-up is shown as unavailable rather than silently converted to zero.

Memory is part of the definition

One transition only: the current high/low plus the immediately previous close. A revision to close at row t can change TR at t and t+1.

TR is nonnegative, directionless, translation-invariant, and scales linearly when every price is multiplied by a positive constant.

Memory and family comparison for True Range

This is why two indicators can react differently to the same shock even when their plotted bands appear similar.

Six ways to try to break it

ScenarioTeaching jobExpected lesson
intrabar-controlIntrabar range controlThe high-low span dominates most bars.
upside-gapUpside gap dominanceThe previous-close-to-low or high gap overtakes the intrabar span.
downside-gapDownside gap dominanceA downward discontinuity makes the low gap the active driver.
outside-barOutside-bar maximumWide outside bars return to high-low dominance.
first-bar-policyFirst-bar platform policyIndex zero exposes this package's high-low fallback.
component-tieEqual-component tie policyHigh-low and high-gap tie exactly; the documented stable priority selects high-low.

Boundary atlas for True Range

Predict the active component before opening the lab. Then jump to the relevant checkpoint and inspect the formula trace rather than guessing from line shape.

Interactive laboratory

Open the True Range guided lab. It contains six 240-row scenarios. Change the period or multiplier where the formula exposes one; the lab recomputes immediately, stops playback, resets to an informative state, and preserves the same calculation path used by Step. Use the checkpoint selector to compare first publication, mechanism, boundary, memory, platform reconciliation, and mature state.

Production implementation

The Python and TypeScript references are topic-scoped and intentionally readable. They validate before calculating, return aligned arrays, and use None/null for unavailable states. Shared fixtures establish parity; independent family checks establish properties without trusting either implementation as the oracle.

When production output disagrees with a chart, log:

  1. symbol and source ownership;
  2. timeframe, session, timestamp, and bar finality;
  3. adjustment basis;
  4. parameter values and first-ready index;
  5. the earliest divergent component;
  6. window inclusion, seed, denominator, multiplier, equality, and null policy;
  7. the earliest revised observation.

Compare, do not conflate

Nearby topicWhat changes
ATRsmooths True Range and therefore carries recursive memory.
Bollinger Bandsmeasure dispersion of closes, not high-low/gap span.
Donchian Channelsretain rolling extremes as price levels.

Misuse boundary

  • Do not: Calling TR realized volatility, variance, or intrabar path length.
  • Do not: Mixing an adjusted previous close with unadjusted current high and low.
  • Do not: Assuming the sign or location of the winning gap predicts the next bar.

The synthetic examples establish implementation behavior only. No historical association, transaction cost, out-of-sample result, or investment suitability claim is made.

Historical example decision

No named historical episode is used. The mechanism is learned more cleanly from controlled synthetic paths, and a named case would require point-in-time, licensed, adjustment-consistent OHLC evidence before publication.

Summary

You can now state the precise question True Range answers, calculate it, explain its memory, diagnose a platform mismatch, and separate a measurement from a trading claim. Use the family comparison rather than chart resemblance to choose the next concept.

Primary evidence

See the topic reference ledger for source roles, dates, and limitations.

True Range calculation flow

Purpose: show the exact selected state, readiness branch, and output.

Rendering system map…

Takeaway: the formula, memory, and unavailable state are part of the contract, not chart formatting.

ReferencesPrimary sources and evidence notes

Expand the source trail, evidence role, and limitations behind the engineering choices.

TRANGE — True Range

  • Organization or authors: TA-Lib
  • Source type: Maintained official documentation
  • Publication, version, or access date: maintained; accessed 2026-07-26
  • URL or identifier: TRANGE — True Range
  • Accessed: 2026-07-26
  • Jurisdiction: not jurisdiction-specific
  • Evidence role: sourced definition, maintained implementation, or bounded historical context
  • Supports: Defines the three-way maximum and TA-Lib first-row availability.
  • Limitations: TA-Lib’s first-row convention differs from this package.

ta_TRANGE.c

  • Organization or authors: TA-Lib
  • Source type: Maintained reference implementation
  • Publication, version, or access date: main branch; accessed 2026-07-26
  • URL or identifier: ta_TRANGE.c
  • Accessed: 2026-07-26
  • Jurisdiction: not jurisdiction-specific
  • Evidence role: sourced definition, maintained implementation, or bounded historical context
  • Supports: Shows the maintained library’s executable True Range semantics.
  • Limitations: Library source proves TA-Lib behavior, not a universal first-bar rule.

ATR — Average True Range

  • Organization or authors: TA-Lib
  • Source type: Maintained official documentation
  • Publication, version, or access date: maintained; accessed 2026-07-26
  • URL or identifier: ATR — Average True Range
  • Accessed: 2026-07-26
  • Jurisdiction: not jurisdiction-specific
  • Evidence role: sourced definition, maintained implementation, or bounded historical context
  • Supports: Documents ATR as a Wilder indicator and its library availability.
  • Limitations: The first-ready row follows TA-Lib’s own True Range convention.

Public evidence boundary

The fixture is author-generated synthetic teaching material. Every displayed number is an author-derived calculation from those inputs. No provider observation, historical return, causal claim, forecast, or profitability result is published. Platform documentation supports only the named platform convention; local seeds, nulls, equality, and scope remain implementation choices.

volatility_channels.ts
function finite(v:number[],n:string){if(!Array.isArray(v)||!v.length||v.some(x=>!Number.isFinite(x)))throw new Error(`${n} must contain finite numbers`)}
function hlc(h:number[],l:number[],c:number[]){finite(h,"high");finite(l,"low");finite(c,"close");if(h.length!==l.length||h.length!==c.length)throw new Error("high, low, and close lengths must match");if(h.some((x,i)=>x<l[i]||c[i]<l[i]||c[i]>x))throw new Error("each bar requires low <= close <= high")}
export function true_range(high:number[],low:number[],close:number[]){hlc(high,low,close);const true_range:number[]=[],high_low:number[]=[],high_gap:(number|null)[]=[],low_gap:(number|null)[]=[],driver:string[]=[];for(let i=0;i<high.length;i++){const a=high[i]-low[i],b=i?Math.abs(high[i]-close[i-1]):null,c=i?Math.abs(low[i]-close[i-1]):null;const choices:[number,string][]=[[a,"high-low"]];if(b!==null&&c!==null)choices.push([b,"high-gap"],[c,"low-gap"]);const best=choices.reduce((x,y)=>y[0]>x[0]?y:x);true_range.push(best[0]);high_low.push(a);high_gap.push(b);low_gap.push(c);driver.push(best[1])}return{true_range,high_low,high_gap,low_gap,driver}}
Full-height labtrue range labOpen full screen