Skip to content
All library documents

Combining Three Supertrend Signals for Entries and Exits

Code Freqtrade

Summary

This Freqtrade strategy uses three Supertrend direction signals to confirm each trade. It enters a long position when all three buy-side indicators point up and volume is present, and marks an exit when all three sell-side indicators point down with volume present. Buy and sell indicators use separately configurable multipliers and periods, while the strategy also sets return targets, a stop loss, and trailing-stop parameters.

The supplied settings are described as outputs from hyperparameter optimization on hourly candles, and the code allows those parameters to be tuned. However, the document gives no performance results or comparison against a baseline. It explicitly cautions that its Supertrend implementation has not been validated against the original paper or another trusted reference. The strategy should therefore be treated as an example implementation whose indicator calculations and tuned settings need independent evaluation before use.

Key ideas

  • A long entry requires all three configured buy-side Supertrend signals to point up while volume is positive.
  • An exit signal requires all three sell-side Supertrend signals to point down while volume is positive.
  • Buy and sell signals use separate tunable multiplier and period settings.
  • The strategy includes return targets, a stop loss, and trailing-stop controls.
  • The author says the indicator implementation is unvalidated and supplies no performance evidence.

Tags

Full text
# Supertrend.py


```py
"""
Supertrend strategy:
* Description: Generate a 3 supertrend indicators for 'buy' strategies & 3 supertrend indicators for 'sell' strategies
               Buys if the 3 'buy' indicators are 'up'
               Sells if the 3 'sell' indicators are 'down'
* Author: @juankysoriano (Juan Carlos Soriano)
* github: https://github.com/juankysoriano/

*** NOTE: This Supertrend strategy is just one of many possible strategies using `Supertrend` as indicator. It should in any case be used at your own risk.
          It comes with at least a couple of caveats:
            1. The implementation for the `supertrend` indicator is based on the following discussion: https://github.com/freqtrade/freqtrade-strategies/issues/30 . Concretely https://github.com/freqtrade/freqtrade-strategies/issues/30#issuecomment-853042401
            2. The implementation for `supertrend` on this strategy is not validated; meaning this that is not proven to match the results by the paper where it was originally introduced or any other trusted academic resources
"""

import logging
from freqtrade.strategy import IStrategy, IntParameter
from pandas import DataFrame
import pandas as pd
import talib.abstract as ta
import numpy as np
import technical.indicators as ftt


class Supertrend(IStrategy):
    # Buy params, Sell params, ROI, Stoploss and Trailing Stop are values generated by 'freqtrade hyperopt --strategy Supertrend --hyperopt-loss ShortTradeDurHyperOptLoss --timerange=20210101- --timeframe=1h --spaces all'
    # It's encouraged that you find the values that better suit your needs and risk management strategies

    INTERFACE_VERSION: int = 3
    # Buy hyperspace params:
    buy_params = {
        "buy_m1": 4,
        "buy_m2": 7,
        "buy_m3": 1,
        "buy_p1": 8,
        "buy_p2": 9,
        "buy_p3": 8,
    }

    # Sell hyperspace params:
    sell_params = {
        "sell_m1": 1,
        "sell_m2": 3,
        "sell_m3": 6,
        "sell_p1": 16,
        "sell_p2": 18,
        "sell_p3": 18,
    }

    # ROI table:
    minimal_roi = {
        "0": 0.087,
        "372": 0.058,
        "861": 0.029,
        "2221": 0
    }

    # Stoploss:
    stoploss = -0.265

    # Trailing stop:
    trailing_stop = True
    trailing_stop_positive = 0.05
    trailing_stop_positive_offset = 0.144
    trailing_only_offset_is_reached = False

    timeframe = '1h'

    startup_candle_count = 199

    buy_m1 = IntParameter(1, 7, default=4)
    buy_m2 = IntParameter(1, 7, default=4)
    buy_m3 = IntParameter(1, 7, default=4)
    buy_p1 = IntParameter(7, 21, default=14)
    buy_p2 = IntParameter(7, 21, default=14)
    buy_p3 = IntParameter(7, 21, default=14)

    sell_m1 = IntParameter(1, 7, default=4)
    sell_m2 = IntParameter(1, 7, default=4)
    sell_m3 = IntParameter(1, 7, default=4)
    sell_p1 = IntParameter(7, 21, default=14)
    sell_p2 = IntParameter(7, 21, default=14)
    sell_p3 = IntParameter(7, 21, default=14)

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        new_cols = []
    
        for multiplier in self.buy_m1.range:
            for period in self.buy_p1.range:
                new_cols.append(self.supertrend_direction(
                    dataframe, multiplier, period, f'supertrend_1_buy_{multiplier}_{period}'))

        for multiplier in self.buy_m2.range:
            for period in self.buy_p2.range:
                new_cols.append(self.supertrend_direction(
                    dataframe, multiplier, period, f'supertrend_2_buy_{multiplier}_{period}'))

        for multiplier in self.buy_m3.range:
            for period in self.buy_p3.range:
                new_cols.append(self.supertrend_direction(
                    dataframe, multiplier, period, f'supertrend_3_buy_{multiplier}_{period}'))

        for multiplier in self.sell_m1.range:
            for period in self.sell_p1.range:
                new_cols.append(self.supertrend_direction(
                    dataframe, multiplier, period, f'supertrend_1_sell_{multiplier}_{period}'))

        for multiplier in self.sell_m2.range:
            for period in self.sell_p2.range:
                new_cols.append(self.supertrend_direction(
                    dataframe, multiplier, period, f'supertrend_2_sell_{multiplier}_{period}'))

        for multiplier in self.sell_m3.range:
            for period in self.sell_p3.range:
                new_cols.append(self.supertrend_direction(
                    dataframe, multiplier, period, f'supertrend_3_sell_{multiplier}_{period}'))

        if new_cols:
            dataframe = pd.concat([dataframe] + new_cols, axis=1)
    
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
               (dataframe[f'supertrend_1_buy_{self.buy_m1.value}_{self.buy_p1.value}'] == 'up') &
               (dataframe[f'supertrend_2_buy_{self.buy_m2.value}_{self.buy_p2.value}'] == 'up') &
               (dataframe[f'supertrend_3_buy_{self.buy_m3.value}_{self.buy_p3.value}'] == 'up') & # The three indicators are 'up' for the current candle
               (dataframe['volume'] > 0) # There is at least some trading volume
        ),
            'enter_long'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
               (dataframe[f'supertrend_1_sell_{self.sell_m1.value}_{self.sell_p1.value}'] == 'down') &
               (dataframe[f'supertrend_2_sell_{self.sell_m2.value}_{self.sell_p2.value}'] == 'down') &
               (dataframe[f'supertrend_3_sell_{self.sell_m3.value}_{self.sell_p3.value}'] == 'down') & # The three indicators are 'down' for the current candle
               (dataframe['volume'] > 0) # There is at least some trading volume
            ),
            'exit_long'] = 1

        return dataframe



    def supertrend_direction(self, dataframe: DataFrame, multiplier: int, period: int, name: str) -> pd.Series:
        """
        Supertrend direction ('up' / 'down') as a named series.
        `ftt.supertrend` returns a (value, direction) tuple - only the direction is used here.
        """
        _, stx = ftt.supertrend(dataframe, period=period, multiplier=multiplier)
        # 'stx' is None before the indicator has warmed up - keep the empty string
        return pd.Series(stx, index=dataframe.index, name=name).fillna('')


```

Shown in full with attribution under the source's licence. Licence: GPL-3.0

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.