Skip to content
All library documents

Three Supertrend Signals for Long and Short Entries

Code Freqtrade

Summary

This Freqtrade strategy combines three Supertrend direction readings for each side of the market. It enters long when all three configured buy readings are up and volume is positive; it enters short when all three sell readings are down and volume is positive. A separate middle signal from the opposing set can trigger exits. The implementation exposes multiplier and period parameters for optimization and includes ROI, stop-loss, and trailing-stop settings for a one-hour timeframe.

The code describes parameter values as hyperopt-generated, but it provides no performance results or validation evidence. Its author explicitly cautions that the indicator implementation has not been verified against the original paper or trusted academic sources. The settings therefore illustrate one configurable trend-following rule set, not proof of profitability; users would need to validate indicator calculations and test robustness on suitable data.

Key ideas

  • Long entries require three configured Supertrend directions to be up and positive volume.
  • Short entries require three configured Supertrend directions to be down and positive volume.
  • The strategy uses a single opposing Supertrend reading to trigger exits.
  • Multiplier and period parameters are exposed for optimization, alongside ROI and stop controls.
  • The document warns that its Supertrend implementation is unvalidated and reports no strategy performance.

Tags

Full text
# FSupertrendStrategy.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 on any case 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 . Concretelly 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 FSupertrendStrategy(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 encourage you find the values that better suites 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.1, "30": 0.75, "60": 0.05, "120": 0.025}
    # minimal_roi = {"0": 1}

    # Stoploss:
    stoploss = -0.265

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

    timeframe = "1h"

    startup_candle_count = 18

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

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

    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
            ),
            "enter_long",
        ] = 1

        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
            ),
            "enter_short",
        ] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                dataframe[
                    f"supertrend_2_sell_{self.sell_m2.value}_{self.sell_p2.value}"
                ]
                == "down"
            ),
            "exit_long",
        ] = 1

        dataframe.loc[
            (
                dataframe[f"supertrend_2_buy_{self.buy_m2.value}_{self.buy_p2.value}"]
                == "up"
            ),
            "exit_short",
        ] = 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.