This article describes a professional trading strategy implemented in Backtrader that identifies low-volatility “squeeze” periods using Bollinger Bands and Keltner Channels, trading breakouts with momentum confirmation and managing risk with ATR-based trailing stops.
The Bollinger-Keltner Squeeze Breakout Trading Strategy integrates the following components:
Below is the complete Backtrader code for the strategy, including the custom Keltner Channel indicator:
import backtrader as bt
class CustomKeltnerChannel(bt.Indicator):
"""A custom implementation of Keltner Channels."""
= ('KeltnerChannel',)
alias = ('mid', 'top', 'bot',)
lines = dict(subplot=False)
plotinfo = (
params 'period', 20),
('devfactor', 1.5), # Multiplier for the ATR
('movav', bt.indicators.SimpleMovingAverage),
(
)
def __init__(self):
self.lines.mid = self.p.movav(self.data, period=self.p.period)
= self.p.devfactor * bt.indicators.AverageTrueRange(self.data, period=self.p.period)
atr self.lines.top = self.lines.mid + atr
self.lines.bot = self.lines.mid - atr
class SqueezeBreakoutStrategy(bt.Strategy):
"""
A professional strategy that trades breakouts from periods of
low-volatility consolidation (a "squeeze").
"""
= (
params # Squeeze detection
'bb_period', 7),
('bb_devfactor', 1.0),
('kc_period', 30),
('kc_devfactor', 1.0),
(# Momentum confirmation
'macd_fast', 7),
('macd_slow', 30),
('macd_signal', 14),
(# Risk management
'atr_period', 7),
('atr_stop_multiplier', 3.0),
(
)
def __init__(self):
self.order = None
# --- Indicators ---
self.bband = bt.indicators.BollingerBands(period=self.p.bb_period, devfactor=self.p.bb_devfactor)
self.keltner = CustomKeltnerChannel(period=self.p.kc_period, devfactor=self.p.kc_devfactor)
# MACD
self.macd = bt.indicators.MACD(
=self.p.macd_fast,
period_me1=self.p.macd_slow,
period_me2=self.p.macd_signal
period_signal
)self.atr = bt.indicators.AverageTrueRange(period=self.p.atr_period)
# --- Trailing Stop State Variables ---
self.stop_price = None
self.highest_price_since_entry = None
self.lowest_price_since_entry = None
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
return
if order.status in [order.Completed]:
if self.position and self.stop_price is None:
if order.isbuy():
self.highest_price_since_entry = self.data.high[0]
self.stop_price = self.highest_price_since_entry - (self.atr[0] * self.p.atr_stop_multiplier)
elif order.issell():
self.lowest_price_since_entry = self.data.low[0]
self.stop_price = self.lowest_price_since_entry + (self.atr[0] * self.p.atr_stop_multiplier)
elif not self.position:
self.stop_price = None
self.highest_price_since_entry = None
self.lowest_price_since_entry = None
self.order = None
def next(self):
if self.order:
return
# --- Squeeze Identification ---
= (self.bband.top < self.keltner.top and self.bband.bot > self.keltner.bot)
is_squeeze
if not self.position and is_squeeze:
# --- Breakout and Momentum Confirmation ---
= self.data.close[0] > self.bband.top[0]
price_breaks_up = self.data.close[0] < self.bband.bot[0]
price_breaks_down
= self.macd.macd[0] > self.macd.signal[0]
macd_is_bullish = self.macd.macd[0] < self.macd.signal[0]
macd_is_bearish
if price_breaks_up and macd_is_bullish:
self.order = self.buy()
elif price_breaks_down and macd_is_bearish:
self.order = self.sell()
elif self.position:
# --- Manual ATR Trailing Stop Logic ---
if self.position.size > 0: # Long
self.highest_price_since_entry = max(self.highest_price_since_entry, self.data.high[0])
= self.highest_price_since_entry - (self.atr[0] * self.p.atr_stop_multiplier)
new_stop self.stop_price = max(self.stop_price, new_stop)
if self.data.close[0] < self.stop_price:
self.order = self.close()
elif self.position.size < 0: # Short
self.lowest_price_since_entry = min(self.lowest_price_since_entry, self.data.low[0])
= self.lowest_price_since_entry + (self.atr[0] * self.p.atr_stop_multiplier)
new_stop self.stop_price = min(self.stop_price, new_stop)
if self.data.close[0] > self.stop_price:
self.order = self.close()
The custom Keltner Channel indicator defines a volatility-based channel:
The strategy identifies low-volatility squeezes and trades breakouts with momentum confirmation:
Indicators:
Trading Logic (next
):
Order Management
(notify_order
):
## Potential Improvements
bb_period
,
kc_period
, macd_fast
, macd_slow
,
macd_signal
, or atr_stop_multiplier
to
optimize for specific assets or market conditions.This strategy is designed for markets with periodic low-volatility consolidations followed by strong breakouts, suitable for assets like forex, stocks, or cryptocurrencies, and can be backtested to evaluate its effectiveness across various timeframes and assets.