← Back to Home
SuperTrend - A Volatility-Adjusted Trend Indicator with Backtrader

SuperTrend - A Volatility-Adjusted Trend Indicator with Backtrader

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.

Pasted image 20250729224751.png

The SuperTrend Indicator Explained

The core of the SuperTrend indicator lies in its dynamic calculation of upper and lower bands, which then determine the trend line.

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
    """
    plotinfo = dict(subplot=False) # Plot on the main price chart
    lines = ('supertrend',) # Define the single output line
    params = (('period', 10), ('multiplier', 3.0),) # Customizable parameters

    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

        prev_st = self.lines.supertrend[-1]
        prev_close = self.data.close[-1]

        # --- 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 ---
        current_close = self.data.close[0]
        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]

SuperTrendConfirmationStrategy: A Confirmed Trend-Following Approach

This 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
        is_uptrend = self.data.close[0] > self.st.supertrend[0]
        was_uptrend = self.data.close[-1] > self.st.supertrend[-1]

        # --- 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

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.