The SuperTrend indicator is a powerful tool for identifying market
trends and potential reversal points. Unlike simpler moving averages, it
incorporates Average True Range (ATR) to adapt to market volatility,
providing more robust signals. This article delves into its
implementation within the backtrader
framework and
introduces a strategy that seeks confirmation before acting on
SuperTrend signals, always utilizing a trailing stop for risk
management, as per your preference.
The core of the SuperTrend indicator lies in its dynamic calculation of upper and lower bands, which then determine the trend line.
(High + Low) / 2 + (Multiplier * ATR)
.(High + Low) / 2 - (Multiplier * ATR)
.The actual SuperTrend line moves with the price, flipping its position when the trend changes. If the price closes above the previous SuperTrend, it suggests an uptrend, and the SuperTrend line will follow the lower band. Conversely, if the price closes below, it suggests a downtrend, and the SuperTrend line will follow the upper band. This creates a staircase-like appearance, clearly delineating trend direction.
SuperTrend
Indicator Implementation in backtrader
The provided SuperTrend
class inherits from
bt.Indicator
, making it a custom indicator within
backtrader
.
import backtrader as bt
class SuperTrend(bt.Indicator):
"""
SuperTrend Indicator
"""
= dict(subplot=False) # Plot on the main price chart
plotinfo = ('supertrend',) # Define the single output line
lines = (('period', 10), ('multiplier', 3.0),) # Customizable parameters
params
def __init__(self):
# ATR and Median Price are needed for the calculation
self.atr = bt.indicators.AverageTrueRange(period=self.p.period)
self.median_price = (self.data.high + self.data.low) / 2.0
# These bands are dynamic based on ATR and median price
self.basic_upper_band = self.median_price + (self.p.multiplier * self.atr)
self.basic_lower_band = self.median_price - (self.p.multiplier * self.atr)
def next(self):
# On the first bar, initialize SuperTrend with the close price
if len(self) == 1:
self.lines.supertrend[0] = self.data.close[0]
return
= self.lines.supertrend[-1]
prev_st = self.data.close[-1]
prev_close
# --- Calculate the current SuperTrend value ---
# If the previous trend was UP (previous close > previous SuperTrend)
if prev_close > prev_st:
# The new ST is the max of the previous ST and the current lower band
self.lines.supertrend[0] = max(self.basic_lower_band[0], prev_st)
else: # If the previous trend was DOWN
# The new ST is the min of the previous ST and the current upper band
self.lines.supertrend[0] = min(self.basic_upper_band[0], prev_st)
# --- Check for a flip in the trend and adjust the SuperTrend line ---
= self.data.close[0]
current_close if current_close > self.lines.supertrend[0]: # If price is now above the ST line
# We are in an uptrend, so the ST line should be based on the lower band
self.lines.supertrend[0] = self.basic_lower_band[0]
elif current_close < self.lines.supertrend[0]: # If price is now below the ST line
# We are in a downtrend, so the ST line should be based on the upper band
self.lines.supertrend[0] = self.basic_upper_band[0]
__init__
: Initializes the ATR
indicator and calculates the median_price
. It also sets up
the basic_upper_band
and basic_lower_band
based on the median price and ATR, multiplied by the user-defined
multiplier
.next()
: This method is called for each
new data point.
SuperTrendConfirmationStrategy
:
A Confirmed Trend-Following ApproachThis strategy, adhering to your preference for strategy names
including components and logic, is aptly named
SuperTrendConfirmationStrategy
. It aims to improve the
reliability of SuperTrend signals by requiring a “confirmation” candle
before entering a trade, and consistently uses trailing stops for
exits.
class SuperTrendConfirmationStrategy(bt.Strategy):
"""
Trades SuperTrend flips only after a confirmation candle.
1. Waits for the SuperTrend indicator to flip direction.
2. Waits for the next candle to close, confirming the new trend.
3. Exits with a trailing stop-loss.
"""
= (
params 'st_period', 7),
('st_multiplier', 3.0),
('trail_percent', 0.02), # Trailing stop percentage
(
)
def __init__(self):
self.order = None
self.st = SuperTrend(self.datas[0], period=self.p.st_period, multiplier=self.p.st_multiplier)
# State variables to manage the confirmation logic
self.waiting_for_buy_confirmation = False
self.waiting_for_sell_confirmation = False
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
return
if order.status in [order.Completed]:
if order.isbuy():
# On buy order completion, place a trailing sell stop
self.sell(exectype=bt.Order.StopTrail, trailpercent=self.p.trail_percent)
elif order.issell():
# On sell order completion, place a trailing buy stop
self.buy(exectype=bt.Order.StopTrail, trailpercent=self.p.trail_percent)
self.order = None # Reset order after completion or cancellation
def next(self):
if self.order: # If an order is pending, do nothing
return
# Determine current and previous trend based on SuperTrend line
= self.data.close[0] > self.st.supertrend[0]
is_uptrend = self.data.close[-1] > self.st.supertrend[-1]
was_uptrend
# --- Confirmation Logic ---
# If we were waiting for a buy confirmation...
if self.waiting_for_buy_confirmation:
self.waiting_for_buy_confirmation = False # Reset flag
if is_uptrend and not self.position: # Check if trend confirmed and not already in position
self.order = self.buy()
return
# If we were waiting for a sell confirmation...
if self.waiting_for_sell_confirmation:
self.waiting_for_sell_confirmation = False # Reset flag
if not is_uptrend and not self.position: # Check if trend confirmed and not already in position
self.order = self.sell()
return
# --- Flip Detection Logic ---
# A flip from downtrend to uptrend occurred on the previous bar
if is_uptrend and not was_uptrend:
# Only set the flag if not already waiting for a sell confirmation
if not self.waiting_for_sell_confirmation:
self.waiting_for_buy_confirmation = True
# A flip from uptrend to downtrend occurred on the previous bar
if not is_uptrend and was_uptrend:
# Only set the flag if not already waiting for a buy confirmation
if not self.waiting_for_buy_confirmation:
self.waiting_for_sell_confirmation = True
params
: Defines parameters for the
SuperTrend period, multiplier, and the trailing stop percentage.__init__
: Initializes the custom
SuperTrend
indicator and sets up boolean flags
(waiting_for_buy_confirmation
,
waiting_for_sell_confirmation
) to manage the confirmation
logic.notify_order
: This crucial method,
aligned with your instruction to always use trailing stops,
automatically places a trailing stop order immediately after a buy or
sell order is completed. For a buy, it places a trailing
sell
stop; for a sell (short), it places a trailing
buy
stop.next()
: This is the heart of the
strategy’s logic.
waiting_for_..._confirmation
flag to
True
, indicating that the strategy should wait for the next
candle to confirm the new trend before taking action.This refined strategy, named
SuperTrendConfirmationTrailingStopStrategy
(as it also
incorporates trailing stops), provides a more cautious approach to
SuperTrend signals, aiming to reduce whipsaws by requiring a
follow-through candle. The integrated trailing stops ensure disciplined
risk management once a position is established.