Library/Technical Indicators/Volatility and Channels

D07-F04-A02 / Complete engineering topic

Average True Range (ATR)

A production-minded guide to Average True Range (ATR).

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

What directionless range scale does recent trading imply? That is the practical question behind Average True Range (ATR). 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

ATR is Wilder’s recursive average of True Range in price units. It measures magnitude, not direction, probability, or expected return.

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: The package includes high-low at index 0. TA-Lib's first TR is unavailable, shifting its first ATR one row later.
  • Defaults: period=14.
  • First publication: index 13 after the 14-value arithmetic seed.
  • Data: finalized, causal observations on one price basis.
  • Missing, malformed, or mixed-basis input: reject rather than repair silently.

Formula and symbols

ATRt=(n1)ATRt1+TRtnATR_t=\frac{(n-1)ATR_{t-1}+TR_t}{n}

SymbolMeaningUnit
TRtTR_tTrue Range at row tprice units
nnpositive integer periodbars
ATRn1ATR_{n-1}arithmetic seed of the first n TR valuesprice units
ATRtATR_tWilder recursive averageprice units

Calculate it by hand

For a short four-period teaching seed, suppose the first four True Ranges are 2, 3, 4, and 7.

  1. Seed at index 3: (2 + 3 + 4 + 7) / 4 = 4.
  2. If the next True Range is 8, then ATR_4 = (3 × 4 + 8) / 4 = 5.
  3. If the following True Range returns to 2, then ATR_5 = (3 × 5 + 2) / 4 = 4.25.

The shock affects ATR immediately and then decays. It does not disappear exactly four bars later as it would from a simple rolling mean.

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. Calculate True Range with the family’s index-zero high-low fallback.
  2. Withhold ATR until n True Range values exist.
  3. Seed at index n-1 with their arithmetic mean.
  4. For every later row, update ATR_t = ATR_{t-1} + (TR_t - ATR_{t-1}) / n.
  5. Recompute every later ATR after a revision because recursive state has no finite expiry row.
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 Average True Range (ATR)

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

Follow a complete causal trace

Canonical 240-row trace for Average True Range (ATR)

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

Recursive and theoretically infinite. A one-unit shock’s remaining weight after m later updates is ((n-1)/n)^m; for n=14 the half-life is about 9.35 updates.

ATR is nonnegative and translation-invariant. Multiplying all prices by k>0 multiplies ATR by k, so raw ATR is not comparable across quote scales.

Memory and family comparison for Average True Range (ATR)

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
regime-tourVolatility regime tourATR rises and decays across calm and wide-range regimes.
constant-rangeConstant-range seedThe arithmetic seed and recursive path remain exactly stable.
gap-shockGap shock and decayOne large TR enters immediately and decays recursively.
responsive-sevenResponsive seven-period ATRA shorter period reacts faster to range expansion.
zero-rangeZero-range flat pathATR legitimately publishes zero after warm-up.
single-shock-memorySingle-shock recursive memoryOne isolated range shock decays geometrically instead of expiring on a single window boundary.

Boundary atlas for Average True Range (ATR)

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 Average True Range (ATR) 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
True Rangeis the unsmoothed one-bar input.
Keltner Channelsuse ATR as a channel-distance state.
Bollinger BandWidthnormalizes rolling close dispersion by its middle band.

Misuse boundary

  • Do not: Reading a rising ATR as bullish or a falling ATR as bearish.
  • Do not: Comparing raw ATR across differently priced instruments without normalization.
  • Do not: Expecting Wilder ATR to match an EMA or SMA of True Range with the same period.

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 Average True Range (ATR) 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.

Average True Range (ATR) 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.

New Concepts in Technical Trading Systems

  • Organization or authors: J. Welles Wilder Jr.
  • Source type: Original book
  • Publication, version, or access date: 1978
  • URL or identifier: ISBN 978-0-89459-027-6
  • Accessed: 2026-07-26
  • Jurisdiction: not jurisdiction-specific
  • Evidence role: sourced definition, maintained implementation, or bounded historical context
  • Supports: Introduces True Range and Wilder’s recursive smoothing framework.
  • Limitations: Bibliographic source; this package still freezes its own first-bar and seed alignment.

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.

ta_ATR.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_ATR.c
  • Accessed: 2026-07-26
  • Jurisdiction: not jurisdiction-specific
  • Evidence role: sourced definition, maintained implementation, or bounded historical context
  • Supports: Shows seed and recursive update behavior in the maintained library.
  • Limitations: This package independently freezes a different first-row alignment.

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.

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}}

function period(v:number,n="period"){if(!Number.isInteger(v)||v<1)throw new Error(`${n} must be a positive integer`)}

export function average_true_range(high:number[],low:number[],close:number[],p=14){period(p);const tr=true_range(high,low,close).true_range,atr:(number|null)[]=Array(tr.length).fill(null);if(tr.length>=p){let value=tr.slice(0,p).reduce((a,b)=>a+b,0)/p;atr[p-1]=value;for(let i=p;i<tr.length;i++){value+=(tr[i]-value)/p;atr[i]=value}}return{true_range:tr,atr}}
Full-height labatr labOpen full screen