Calculate position size based on current volatility (ATR) and a fixed percentage of account equity.
Professional traders never risk a fixed dollar amount or a fixed number of points per trade. They size positions based on the current volatility of the instrument — because volatility defines how much room a trade needs to breathe, and how much damage is done when the stop is hit.
The fixed-point problem is one of the most underappreciated issues in strategy development. Consider the S&P 500: a 100-point stop loss in the year 2000 represented roughly 10% of the index's value (S&P 500 near 1,000). At that level, a 100-point stop would be hit on many ordinary trading days — it was a wide, generous stop for its era. In 2024, with the S&P 500 above 5,000, a 100-point move is less than 2% of the index's value and is comfortably within normal intraday noise. The same 100-point stop that was wide in 2000 is now dangerously tight.
A strategy built with a fixed 100-point stop in 2000 and backtested across 20 years of data is broken by design. The stops and targets that worked in the early period become progressively miscalibrated as the instrument's price level and volatility evolve. ATR fixes this by anchoring every distance — stop, target, trailing stop — to how much the instrument is actually moving right now.
To make the scale problem concrete, here is a comparison of what a 100-point stop has meant on the S&P 500 at different points in history:
| Year | S&P 500 Level | 100-point stop = | ATR(14) approx. | |---|---|---|---| | 2000 | ~1,000 | ~10% of price | ~20–30 pts | | 2008 | ~1,200 | ~8% of price | ~30–60 pts | | 2015 | ~2,000 | ~5% of price | ~20–30 pts | | 2020 | ~3,000 | ~3% of price | ~80–120 pts | | 2024 | ~5,000 | ~2% of price | ~50–80 pts |
In 2000, a 100-point stop was roughly 3–5 ATR — very generous, rarely hit on normal days. In 2024, the same 100-point stop is 1–2 ATR — well within normal daily noise. A system optimised with a 100-point stop in 2000 will generate far more stop-outs in 2024 than it was designed for, not because the strategy logic changed, but because the instrument's scale changed.
The same issue applies to any instrument that trends upward over time: stocks, indices, gold, Bitcoin. A fixed-point stop progressively tightens (relative to the instrument) as price rises.
The formula is simple:
You automatically trade half the size in volatile conditions, protecting the account. In calmer conditions, the stop shrinks and the position size grows — you take larger positions when the market is offering cleaner, lower-noise conditions.
The goal of ATR-based sizing is consistent cash risk per trade, not consistent position size. Every trade risks the same percentage of equity, regardless of:
This is what makes the strategy scale-invariant. It can be applied to different instruments and different time periods without recalibration. The math adjusts automatically.
Markets cycle through volatility regimes. After a major news event or a financial crisis, daily ATR can triple or quadruple compared to normal conditions. During quiet summer trading, ATR might compress to a fraction of its normal value.
A fixed-point strategy treats all these conditions identically. ATR-based sizing responds:
This is why professional systematic traders almost universally use some form of volatility-based sizing. It is not just about being precise — it is about building a strategy that remains valid across the full range of market conditions it will encounter.
When running a backtest with ATR-based sizing, the `strategy.equity` variable gives you the current account value as it evolves. This means position sizes will also evolve as the account grows or shrinks during the backtest — which gives you a realistic simulation of how the strategy would behave in live trading with compounding.
The key inputs to tune are the ATR length (standard is 14), the ATR multiplier for stops (typically 1.5–3.0), and the risk percentage per trade (commonly 0.5–2.0% for systematic strategies).
//@version=6
strategy("ATR Position Sizing", overlay=true, default_qty_type=strategy.fixed, default_qty_value=1)
// Risk parameters
riskPercent = input.float(1.0, "Risk Per Trade (%)", step=0.1)
atrLength = input.int(14, "ATR Length")
atrMult = input.float(2.0, "ATR Stop Multiplier")
// Account info
accountBalance = strategy.equity
// ATR calculation
atrValue = ta.atr(atrLength)
stopDistance = atrValue * atrMult
// Position size calculation
riskAmount = accountBalance * (riskPercent / 100)
positionSize = math.floor(riskAmount / stopDistance)
// Simple entry logic
longCondition = ta.crossover(ta.ema(close, 9), ta.ema(close, 21))
shortCondition = ta.crossunder(ta.ema(close, 9), ta.ema(close, 21))
if (longCondition)
strategy.entry("Long", strategy.long, qty=positionSize)
strategy.exit("Long SL", "Long", stop=close - stopDistance, limit=close + stopDistance * 2)
if (shortCondition)
strategy.entry("Short", strategy.short, qty=positionSize)
strategy.exit("Short SL", "Short", stop=close + stopDistance, limit=close - stopDistance * 2)