This article describes a trading strategy implemented in Backtrader that capitalizes on periods of low volatility followed by price breakouts. The strategy uses Bollinger Bands to identify a “squeeze” (low volatility) and enters trades when the price breaks out of the bands, employing trailing stops to manage risk and lock in profits.
The Bollinger Band Squeeze Breakout Trading Strategy integrates the following components:
Below is the complete Backtrader code for the strategy:
import backtrader as bt
class BollingerBandSqueezeStrategy(bt.Strategy):
"""
A strategy that enters on a breakout after a period of low volatility
and uses a trailing stop for risk management.
1. Identify Squeeze: Bollinger Bandwidth is at a multi-period low.
2. Enter on Breakout: Price closes outside the bands.
3. Exit: A trailing stop-loss order is placed upon entry.
"""
= (
params 'bband_period', 7),
('bband_devfactor', 1.0),
('squeeze_period', 30),
('trail_percent', 0.02), # Trailing stop loss percentage (e.g., 2%)
(
)
def __init__(self):
self.order = None
self.dataclose = self.datas[0].close
# Add Bollinger Bands indicator
self.bband = bt.indicators.BollingerBands(
self.datas[0],
=self.p.bband_period,
period=self.p.bband_devfactor
devfactor
)
# Calculate Bollinger Bandwidth
= (self.bband.lines.top - self.bband.lines.bot) / self.bband.lines.mid
bb_bandwidth
# Find the lowest bandwidth over the squeeze_period
self.lowest_bb_width = bt.indicators.Lowest(
=self.p.squeeze_period
bb_bandwidth, period
)
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# An order has been submitted/accepted - nothing to do
return
# Check if an order has been completed
# Attention: broker could reject order if not enough cash
if order.status in [order.Completed]:
if order.isbuy():
# --- Trailing Stop for LONG position ---
# The stop price will be calculated based on the trail_percent
self.sell(exectype=bt.Order.StopTrail, trailpercent=self.p.trail_percent)
elif order.issell():
# --- Trailing Stop for SHORT position ---
self.buy(exectype=bt.Order.StopTrail, trailpercent=self.p.trail_percent)
self.order = None
def next(self):
# Check for pending orders and sufficient data
if self.order or len(self) < self.p.squeeze_period:
return
# Check if a squeeze is happening by comparing the current bandwidth to its historic low
= self.lowest_bb_width[-1] == ((self.bband.top[-1] - self.bband.bot[-1]) / self.bband.mid[-1])
is_squeeze
# Only enter if not already in a position
if not self.position:
if is_squeeze:
# Breakout to the upside
if self.dataclose[0] > self.bband.top[0]:
self.order = self.buy()
# Breakout to the downside
elif self.dataclose[0] < self.bband.bot[0]:
self.order = self.sell()
The strategy identifies low-volatility periods and trades breakouts with risk management:
Indicators:
Trading Logic (next
):
notify_order
).Order Management
(notify_order
):
bband_period
,
bband_devfactor
, squeeze_period
, or
trail_percent
to optimize for specific assets or market
conditions.This strategy is designed for markets with periodic low-volatility periods followed by strong breakouts, such as stocks, forex, or cryptocurrencies, and can be backtested to evaluate its effectiveness across various timeframes and assets.