""" 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('')