KeltnerChannels — Keltner Channels
An EMA centreline with bands a multiple of ATR away. Against Bollinger: ATR is gap-aware and smoother than a standard deviation of closes, so Keltner bands don’t balloon on one outlier close. The squeeze setup (Bollinger inside Keltner) needs both.
Signature
KeltnerChannels(period, atr_period, multiplier, on=<source>)| Argument | Type | Required | Notes |
|---|---|---|---|
period |
int Param or literal | Yes | EMA length. Default 20, bounds 1–400. |
atr_period |
int Param or literal | Yes | ATR length. Default 10, bounds 1–100. |
multiplier |
float Param or literal | Yes | Band width in ATRs. Default 2.0, bounds 0.1–10.0. |
on= |
symbol | No | Source symbol. Defaults to the trade symbol. Reads high, low, close. |
Formula
$$ \text{mid} = \text{EMA}(C, n), \quad \text{upper/lower} = \text{mid} \pm m,\text{ATR}(n_{atr}) $$
Warmup
The EMA seeds from the first close; the ATR seeds as a cumulative average over its first
atr_period bars, then Wilder smoothing. Both converge over roughly 3n bars.
Reading the value
kc.upperkc.midkc.lowerself.trade.close > kc.upperMulti-value: cannot be chained onto or crossed.
Example
class KeltnerRide(Strategy): """Enter on a close above the upper channel; exit at the midline.""" trade = Symbol() sizing = FixedCapital()
def execute(self): kc = KeltnerChannels(20, 10, 2.0, on=self.trade) if self.trade.close > kc.upper and self.trade.is_flat: self.buy(self.trade, reason="Keltner breakout") if self.trade.close < kc.mid and self.trade.is_long: self.sell(self.trade, self.trade.position, reason="Back to midline")Notes
- Chester Keltner’s channel in the modern EMA+ATR form (Linda Raschke’s variant).
- Proven by an assert-via-trade run on SPY daily (midline and band width converged to an independent EMA/ATR replication within 0.05 once warm; zero alarm trades).
- The band is symmetric by construction:
upper − mid == mid − lower. - The recursion seeds from the first bar rather than from a warm-up window, so early values
are not comparable with TA-Lib EMA(20) ± 2 × ATR(10), which seeds from an average of the first
n; measured on a 300-bar tape the two agree to 1e-6 from bar 111 onward.