Skip to content
Open RaamTrade

ATRPercent — ATR Percent

Wilder’s ATR divided by price, times 100. Thirty points of ATR means nothing across instruments; 1.2% means the same thing everywhere — which makes this the right input to volatility-scaled position sizing and cross-instrument comparisons.

Signature

ATRPercent(period, on=<source>)
Argument Type Required Notes
period int Param or literal Yes ATR length. Default 14, bounds 1–100.
on= symbol No Source symbol. Defaults to the trade symbol. Reads high, low, close.

Formula

$$ \text{ATR%} = 100,\frac{\text{ATR}(n)}{C} $$

Warmup

The ATR seeds as a cumulative average over the first period bars, then Wilder smoothing; converges over roughly 3n bars.

Reading the value

atp.value
atp.value > 2.0

Example

class VolScaledEntry(Strategy):
"""Only trade when daily volatility is inside a chosen band."""
period = Param(14, min=1, max=100)
trade = Symbol()
sizing = FixedCapital()
def execute(self):
atp = ATRPercent(self.period, on=self.trade)
sma = SMA(50, on=self.trade)
if atp.value < 2.0 and self.trade.close > sma.value and self.trade.is_flat:
self.buy(self.trade, reason=f"Calm tape, ATR {atp.value:.2f} pct")
if atp.value > 3.0 and self.trade.is_long:
self.sell(self.trade, self.trade.position, reason="Volatility spike")

Notes

  • Wilder’s ATR (1978), normalised by the close — the same construction TA-Lib calls NATR (which lands in Tier 2 under that name for members who search for it).
  • Proven by an assert-via-trade run on SPY daily (converged to an independent ATR replication within 0.02 once warm; zero alarm trades).
  • The recursion seeds from the first bar rather than from a warm-up window, so early values are not comparable with pandas_ta’s natr(14), which seeds from an average of the first n; measured on a 300-bar tape the two agree to 1e-6 from bar 149 onward.