αAlphaware
QUANT6 min read

Using ATR to set stop distance and position size

How Wilder's True Range and smoothing produce ATR, and how to tie a volatility-based stop distance to a fixed risk budget per trade in a form you can test.

Using ATR to set stop distance and position size

Updated November 3, 2026. This article covers the definition and calculation of ATR and how it feeds stop-distance and position-size rules. Numbers below are illustrative examples of the arithmetic, not backtest or live trading results.

ATR measures the size of moves, not their direction

ATR (Average True Range) is a volatility indicator that J. Welles Wilder Jr. introduced in his 1978 book New Concepts in Technical Trading Systems. It says nothing about whether price will rise or fall. It only shows, in price units, how far a bar has typically moved recently.

That makes ATR a risk-management tool rather than an entry signal. The same number can link where a stop goes and how many units can be bought at that stop distance without exceeding the loss limit per trade. ATR rises in both rallies and selloffs, so a rising ATR is neither bullish nor bearish by itself.

True Range adds gaps to the intraday range

Using only high minus low misses days that open with a large gap from the previous close. Wilder’s True Range (TR) takes the largest of three values.

TR = max(High − Low, |High − Previous Close|, |Low − Previous Close|)

If the previous close was 100 and today traded only between 104 and 106, the intraday range is 2 but TR is 6. The 4 points moved through the gap count as risk.

Diagram of the three True Range candidates, the maximum taken as TR, and its smoothing into ATR(14)
Original calculation diagram. The largest of the three candidates becomes True Range, and smoothing it over 14 periods gives ATR.

Wilder’s smoothing is an exponential average with period 14

Wilder’s method, as documented by StockCharts ChartSchool, has two steps. The first ATR is the simple average of the first 14 TR values. After that, the prior ATR gets a weight of 13 and the new TR a weight of 1.

First ATR  = average of the first 14 TRs
Later ATR  = (Prior ATR × 13 + Current TR) / 14

This recursion is an exponential moving average with smoothing factor α = 1/14. The same “14” therefore reacts much more slowly than a conventional EMA with span 14 (α = 2/15). If a platform computes ATR with an SMA or a conventional EMA, or starts the calculation from a different amount of history, its values will differ slightly. Confirm that backtest and live trading use the same definition.

An ATR chart follows volatility changes with a lag

Animation of a synthetic price with its high-low range and close, above a panel showing True Range and 14-period ATR
Chart drawn from synthetic data. True Range jumps first when daily ranges or gaps widen; the smoothed ATR(14) follows more slowly.

Rising ATR means recent bars have been moving more; falling ATR means less. Because of the smoothing, ATR understates current risk right after a shock and stays elevated for a while after the shock has passed.

ATR is an absolute value in price units. An ATR of 2 means something different for an asset priced at 20 than for one priced at 200, so cross-asset comparison converts it to a ratio such as ATR ÷ Close × 100. Timeframes must also match. Daily and hourly ATR are not directly comparable; compute ATR on the bars on which stops and orders are managed.

Tie stop distance and size to one risk budget

ATR-based risk management rests on two formulas.

Stop distance = ATR × multiple
Units         = risk budget per trade ÷ stop distance

With a risk budget of 1,000, ATR of 2.5, and a multiple of 2, the stop distance is 5 and the size is 200 units. If ATR doubles to 5, the stop distance doubles and the size falls to 100. The loss when the stop is hit stays roughly the same as volatility changes.

The code below implements TR, Wilder ATR, and the size calculation with the standard library only, and checks the numbers above.

def true_range(high, low, prev_close):
    if prev_close is None:
        return high - low
    return max(high - low, abs(high - prev_close), abs(low - prev_close))

def wilder_atr(bars, n=14):
    """bars: list of (high, low, close). Returns ATR list (None until n TRs exist)."""
    trs, prev = [], None
    for high, low, close in bars:
        trs.append(true_range(high, low, prev))
        prev = close
    atr = [None] * len(trs)
    if len(trs) < n:
        return atr
    atr[n - 1] = sum(trs[:n]) / n
    for i in range(n, len(trs)):
        atr[i] = (atr[i - 1] * (n - 1) + trs[i]) / n
    return atr

def position_size(risk_budget, atr_value, multiple):
    stop_distance = atr_value * multiple
    return int(risk_budget // stop_distance), stop_distance

# Gap: previous close 100, today's range 104-106 -> TR uses the gap, not 2.
assert true_range(106, 104, 100) == 6
assert true_range(101, 99, 100) == 2

bars = [(11, 9, 10)] * 3 + [(14, 10, 13)]
atr = wilder_atr(bars, n=3)
assert atr[2] == 2                               # first value = simple mean of 3 TRs
assert abs(atr[3] - (2 * 2 + 4) / 3) < 1e-12     # then (prev * (n-1) + TR) / n

units, stop = position_size(risk_budget=1_000, atr_value=2.5, multiple=2)
assert (units, stop) == (200, 5.0)
assert position_size(1_000, 5.0, 2)[0] == 100    # ATR doubles -> size halves

The same structure serves trailing stops. Charles Le Beau’s Chandelier Exit for a long position uses the highest high of the last 22 periods minus 3 × ATR(22) as the exit line. The line rises as a trend continues, but in directionless markets it can produce repeated exits and re-entries.

Use Calculation What to check
Initial stop Entry ± ATR × multiple Whether size falls as the multiple grows
Position size Risk budget ÷ (ATR × multiple) Actual loss when a gap fills beyond the stop
Trailing stop 22-period high − 3 × ATR(22) Cost of repeated exits in sideways markets
Volatility breakout filter Reference price ± ATR × multiple A separate direction condition, since ATR has none

Period and multiple are parameters to test

Shortening the period to 7 makes ATR react faster but adds noise; lengthening it to 20–22 makes it steadier but slower. The stop multiple works the same way. It is not a constant that raises the win rate; it is a risk parameter to set together with holding period, gap risk, transaction costs, and the target reward-to-risk ratio.

ATR does not account for these risks:

  • Gaps and liquidity: A stop order can fill worse than its price during a gap or in a thin order book. The calculated loss limit and the realized loss can differ.
  • Regime change: The volatility of the last 14 bars does not guarantee the volatility of the next one.
  • Direction: A high ATR alone cannot decide between buying and selling.

If you tested several period and multiple combinations, do not adopt the best one as is. Re-check it with walk-forward methods that include the selection step, as described in Preventing backtest overfitting. For collecting the bar data that feeds ATR, see the exchange API data pipeline.

Sources and scope

Signal

Was this brief useful?

Subscribe

Get new briefs

Get an email when a new brief ships. RSS and X stay available.

Subscribe
More in this categoryQUANT →