ATR-based position sizing
A fixed-point stop distance (chapter 71's entry_price - stop_price) doesn't adapt to how volatile the instrument currently is. ATR (Average True Range) sizes the stop — and therefore the position — to current volatility, so a calm stock and a wild one carry comparable *risk* per trade despite different price behavior.
Computing ATR
import pandas as pd
def compute_atr(df: pd.DataFrame, period: int = 14) -> pd.Series:
high, low, close = df["high"], df["low"], df["close"]
prev_close = close.shift(1)
tr = pd.concat([
high - low,
(high - prev_close).abs(),
(low - prev_close).abs(),
], axis=1).max(axis=1)
return tr.rolling(period).mean()
Sizing off a volatility-scaled stop
def atr_based_qty(account_equity: float, risk_pct: float, entry_price: float, atr: float, atr_multiplier: float = 2.0, lot_size: int = 1) -> int:
risk_amount = account_equity * (risk_pct / 100)
stop_distance = atr * atr_multiplier
if stop_distance == 0:
return 0
raw_qty = int(risk_amount / stop_distance)
return (raw_qty // lot_size) * lot_size
def atr_based_stop_price(entry_price: float, atr: float, atr_multiplier: float, side: str) -> float:
distance = atr * atr_multiplier
return entry_price - distance if side == "BUY" else entry_price + distance
df["atr14"] = compute_atr(df, 14)
latest_atr = df["atr14"].iloc[-1]
qty = atr_based_qty(account_equity=100000, risk_pct=1.0, entry_price=1470, atr=latest_atr, atr_multiplier=2.0)
stop = atr_based_stop_price(entry_price=1470, atr=latest_atr, atr_multiplier=2.0, side="BUY")
Why atr_multiplier matters as much as the risk percentage
A multiplier too small (e.g. 0.5x ATR) places the stop inside normal price noise — you get stopped out repeatedly by ordinary volatility, not by the thesis actually being wrong (this shows up as low win rate purely from noise, unrelated to signal quality). A multiplier too large (e.g. 5x ATR) makes stops so wide that position sizes shrink to near-zero under the fixed-risk formula, or risk becomes too large if position size isn't correctly capped. 1.5x-3x ATR is a common starting range — validate it empirically per instrument/timeframe via backtesting (chapter 82), don't assume a single multiplier works everywhere.
Recompute ATR fresh for every new trade, not once per strategy
Volatility regimes shift — an ATR computed at strategy start and reused for months will misjudge risk as the instrument's behavior changes. Recompute from the latest available candles each time you size a new entry.