Why fixed SL/TP values break over time on indices — and three methods for building adaptive, future-proof targets.
Fixed stop loss and take profit values are among the most common and damaging mistakes in strategy development — particularly on indices, which trend upward over long periods and change their volatility characteristics significantly over time.
The core problem: a fixed point value represents a different fraction of an instrument's value at different price levels. A 50-point stop on the S&P 500 represented roughly 5% of the index in 2000. Today it represents less than 1%. A strategy using a 50-point stop was calibrated for an instrument near 1,000. Running that same strategy on an instrument near 5,500 means the stop is five times tighter relative to price — it will be hit on ordinary intraday noise that the 2000 version of the strategy would have survived.
This isn't a theoretical problem. It is why strategies that "worked great in the 2000s" often collapse when forward-tested. The instrument changed; the strategy did not adapt.
The most widely used approach. Stop and target distances are set as multiples of the current ATR(14). Since ATR measures how much the instrument is actually moving, a 2× ATR stop always means "two average bar ranges" — it scales with both the price level and the current volatility regime.
`stopDistance = ta.atr(14) * 2.0` `takeProfitDistance = ta.atr(14) * 4.0`
This is robust because it adapts to two things simultaneously: price level (ATR is denominated in price units, so it naturally grows as the instrument's price grows) and current volatility (a market in crisis has a much larger ATR than the same market during a quiet summer).
When to use it: general-purpose stops and targets, especially for strategies that will be run across long backtests or multiple instruments.
The distance between Bollinger Band® upper and lower bands at entry represents the current volatility range of the instrument — a measure of how much price swings within its "normal" range. Using this as the basis for SL/TP anchors the distances to a statistically derived volatility measure.
`bbRange = bbUpper - bbLower` `sl = close - bbRange * 0.75` `tp = close + bbRange * 1.5`
The Bollinger Band® width scales with price (since the underlying SMA scales with price) and with volatility (since the band width is standard deviation based). This makes it naturally self-adjusting across different market environments.
When to use it: mean-reversion strategies where Bollinger Bands® are already part of the signal logic — the SL/TP are then dimensionally consistent with the indicator generating the signal.
The simplest adaptive method: stops and targets are defined as a percentage of the entry price. A 1.5% stop on a $100 stock is $1.50; on a $5,000 index it's $75. The absolute dollar amount scales automatically with the instrument's price.
`sl = close * (1 - stopPercent / 100)` `tp = close * (1 + targetPercent / 100)`
When to use it: cross-instrument strategies where simplicity is more important than precise volatility calibration. Less precise than ATR but far more robust than fixed points.
| Method | Adapts to price level | Adapts to volatility regime | Complexity | |---|---|---|---| | ATR-based | Yes (ATR grows with price) | Yes (ATR tracks current ranges) | Low | | Indicator range | Yes (BB width scales with price) | Yes (BB width is StdDev based) | Medium | | Percentage | Yes (% of current price) | No (fixed % regardless of regime) | Very low |
For most strategies, ATR is the recommended starting point. It is conceptually simple, well-understood in the trading community, and gives genuinely good results across different instruments and time periods.
The clearest test of whether your SL/TP approach is adaptive is to split your backtest into time periods and look at how the stop and target distances behave:
A well-calibrated dynamic strategy will show consistent risk-to-reward ratios and consistent profit factors across time periods. A fixed-point strategy will show degradation over time on any upward-trending instrument.
When a strategy uses fixed stops and targets, optimisation will find the values that worked best over the historical data set. But those values worked because the instrument happened to be in a particular price and volatility range during that test period. They are not reliable going forward.
Dynamic approaches remove this problem by definition — the values are derived from current conditions, not from an historical optimisation. This is why they are fundamentally more durable, even if the backtest results look less spectacular than a curve-fitted fixed-value system.
//@version=6
strategy("Dynamic SL/TP — Price Action Range", overlay=true)
// === Method 1: Indicator-Based Range ===
// Use the distance between Bollinger Band® upper and lower
// as a volatility-aware range for SL and TP
bbLen = input.int(20, "BB Length")
bbMult = input.float(2.0, "BB Multiplier")
tpMult = input.float(1.5, "TP Multiplier (x range)")
slMult = input.float(0.75, "SL Multiplier (x range)")
basis = ta.sma(close, bbLen)
dev = bbMult * ta.stdev(close, bbLen)
upper = basis + dev
lower = basis - dev
// Dynamic range in points — scales with price level
bbRange = upper - lower
// Entry logic (simple EMA cross for demonstration)
fast = ta.ema(close, 9)
slow = ta.ema(close, 21)
longSignal = ta.crossover(fast, slow)
shortSignal = ta.crossunder(fast, slow)
if longSignal and strategy.opentrades == 0
sl = close - bbRange * slMult
tp = close + bbRange * tpMult
strategy.entry("Long", strategy.long)
strategy.exit("Long Exit", "Long", stop=sl, limit=tp)
if shortSignal and strategy.opentrades == 0
sl = close + bbRange * slMult
tp = close - bbRange * tpMult
strategy.entry("Short", strategy.short)
strategy.exit("Short Exit", "Short", stop=sl, limit=tp)
// Visualise the dynamic range
plot(upper, "BB Upper", color=color.new(color.gray, 60))
plot(lower, "BB Lower", color=color.new(color.gray, 60))