ATR % Indicator: Formula, Settings and How to Read It
ATR % is Wilder's Average True Range divided by the current close and expressed as a percentage, which makes bar-to-bar volatility comparable between instruments trading at very different price levels. Senzoukria plots it in its own pane using a 14-period Wilder average by default.
Senzoukria · Indicators · Updated September 2026
ATR % ships with the Senzoukria desktop app, in the Volatility group of the indicator catalogue. It is drawn in its own panel below the chart.
What ATR % measures
Each bar's true range is the largest of three distances: its own high minus low, the gap between the high and the previous close, and the gap between the low and the previous close, so an overnight move between bars is counted instead of ignored. The very first bar of the loaded history has no predecessor, so its true range is simply its own span. Those true ranges are smoothed with Wilder's recursion, seeded on the arithmetic mean of the first N values, then divided by the bar's close and multiplied by 100. A reading of 0.35 therefore means the average bar covers roughly a third of one percent of price, whether the symbol is MNQ or 6E. The series returns nothing while the average is still warming up, and nothing when the close is zero or below, because a percentage of a non-positive base is not a scale.
The formula, as implemented
This is not a description of how the indicator is usually defined elsewhere — it is what the shipped code computes, documented next to the implementation:
True Range de Wilder : TR[0] = high − low ; TR[i] = max(high − low, |high − closeₚ|, |low − closeₚ|). */ function trueRanges(bars: readonly FootprintBar[]): number[] { const tr: number[] = new Array(bars.length); for (let i = 0; i < bars.length; i++) { const b = bars[i]; if (i === 0) { tr[i] = b.high - b.low; } else { const pc = bars[i - 1].close; tr[i] = Math.max( b.high - b.low, Math.abs(b.high - pc), Math.abs(b.low - pc), ); } } return tr; } /** ATR de Wilder (1978) : graine = moyenne des N premiers TR, puis ATR[i] = (ATR[i−1]·(N−1) + TR[i]) / N. Warm-up (< N barres) → null. Même récurrence que `atrSeries` d'averages.ts — recopiée pour que la famille reste autonome pendant la migration (cf. note d'en-tête). */ function wilderAtr( bars: readonly FootprintBar[], period: number, ): Array<number | null> { const n = Math.max(1, Math.floor(period)); const out: Array<number | null> = new Array(bars.length).fill(null); if (bars.length < n) return out; const tr = trueRanges(bars); let atr = 0; for (let i = 0; i < n; i++) atr += tr[i]; atr /= n; out[n - 1] = atr; for (let i = n; i < bars.length; i++) { atr = (atr * (n - 1) + tr[i]) / n; out[i] = atr; } return out; } /** σ de POPULATION fenêtré (÷N — la convention Bollinger, parité TradingView/ATAS). Two-pass autour de la moyenne de fenêtre (stabilité numérique, cf. en-tête). Warm-up (< N échantillons) → null. */ function stdPopSeries( values: readonly number[], period: number, ): Array<number | null> { const n = Math.max(1, Math.floor(period)); const means = smaSeries(values, n); const out: Array<number | null> = new Array(values.length).fill(null); for (let i = n - 1; i < values.length; i++) { const m = means[i]; if (m === null) continue; let acc = 0; for (let j = i - n + 1; j <= i; j++) { const d = values[j] - m; acc += d * d; } out[i] = Math.sqrt(acc / n); } return out; } // ── Defs ──────────────────────────────────────────────────────────────────── /** ATR % : ATR_N / close × 100 — l'ATR normalisé par le prix, comparable d'un instrument à l'autre (MNQ vs 6E). Même lissage Wilder que `atr`, défaut N=14. close ≤ 0 → null (pas d'échelle honnête) ; warm-up → null.
How to read it
- Rising ATR % means each bar is covering more ground relative to price; a falling line means ranges are contracting even while price keeps moving.
- Because the value is normalised by price, the same number read on two symbols describes a comparable amount of movement, which is what makes it usable for cross-instrument sizing.
- The measure has no direction: an expansion away from a low plateau reads identically whether the ranges widened on the way up or on the way down.
- Compare the current reading with the same time of day on previous sessions rather than against an absolute threshold — the open and the roll period have their own normal level.
- A single outsized bar is spread over N bars by the Wilder average, so the visible step in the line is smaller than the bar that caused it.
Parameters and defaults
Period defaults to 14, Wilder's original choice, and controls both the seed average and the weight given to each new true range. It accepts 1 to 200: a short setting such as 5 reacts within a few bars and is noisier, while 50 or more turns the line into a slow regime measure. At the minimum of 1 the output reduces to the raw true range over the close.
| Parameter | Type | Default | Range |
|---|---|---|---|
| Period | number | 14 | 1 – 200 |
What it does not show
ATR % says how much a bar moved, never in which direction and never who moved it — two bars with identical ranges can carry opposite delta and opposite outcomes. Wilder smoothing is lag by construction, so a change of regime appears gradually over roughly N bars. In thin sessions ranges shrink because little is printing, which lowers the reading without the market having genuinely calmed. The denominator is the bar's own close, so the percentage drifts slightly as price trends even when the absolute range is unchanged.
Using it in Senzoukria
Add it from the Indicators panel of any footprint chart or candle chart. It runs on futures data from Rithmic or Databento and on crypto pairs from Binance and Bybit, on the same engine — the calculation does not change with the venue, only the data feeding it does. Market data subscriptions are billed by the provider, separately from the app.
Related indicators
- True Range — Volatility
- Std Dev — Volatility
- Squeeze — Volatility
- Chaikin Volatility — Volatility
- Realized Vol — Volatility
- Skewness — Volatility
See the full indicator library, or start with the order flow guide if you are new to reading aggression, delta and absorption.
Frequently asked questions
- What is the difference between ATR and ATR %?
- ATR is expressed in the instrument's price units, so 12 points on one contract cannot be compared with 12 points on another. ATR % divides that same Wilder average by the current close and multiplies by 100, producing a unitless figure. The smoothing and the true-range definition are identical; only the scale changes.
- Why is ATR % blank at the start of my chart?
- Wilder's average is seeded on the mean of the first N true ranges, so no value exists before N bars have loaded. The indicator returns nothing for those bars rather than drawing a partial average from an incomplete sample. With the default period of 14, the line begins on the fourteenth bar.
- Can ATR % be compared between a futures chart and a crypto chart?
- The percentage form removes the price-level difference, so the numbers land on the same scale. What does not transfer is the trading context: session hours, liquidity and bar composition differ, and crypto symbols in Senzoukria are analysis only, with no order routing. Compare levels within similar liquidity conditions rather than across a quiet overnight window and a cash-session open.