Ejemplo n.º 1
0
class FrostAuraM5Strategy(IStrategy):
    """
    This is FrostAura's mark 5 strategy which aims to make purchase decisions
    based on the ADX and RSI indicators. A momentum-based strategy.
    
    Last Optimization:
        Profit %        : 4.03%
        Optimized for   : Last 45 days, 1h
        Avg             : 1d 2h 49m
    """
    # Strategy interface version - allow new iterations of the strategy interface.
    # Check the documentation or the Sample strategy to get the latest version.
    INTERFACE_VERSION = 2

    # Minimal ROI designed for the strategy.
    # This attribute will be overridden if the config file contains "minimal_roi".
    minimal_roi = {"0": 0.36, "298": 0.18, "731": 0.057, "1050": 0}

    stoploss = -0.271

    # Trailing stop:
    trailing_stop = False  # value loaded from strategy
    trailing_stop_positive = None  # value loaded from strategy
    trailing_stop_positive_offset = 0.0  # value loaded from strategy
    trailing_only_offset_is_reached = False  # value loaded from strategy

    # Optimal ticker interval for the strategy.
    timeframe = '1h'

    # Run "populate_indicators()" only for new candle.
    process_only_new_candles = False

    # These values can be overridden in the "ask_strategy" section in the config.
    use_sell_signal = True
    sell_profit_only = False
    ignore_roi_if_buy_signal = False

    # Number of candles the strategy requires before producing valid signals
    startup_candle_count: int = 50

    # Optional order type mapping.
    order_types = {
        'buy': 'limit',
        'sell': 'limit',
        'stoploss': 'market',
        'stoploss_on_exchange': False
    }

    # Optional order time in force.
    order_time_in_force = {'buy': 'gtc', 'sell': 'gtc'}

    plot_config = {
        'main_plot': {
            'tema': {},
            'sar': {
                'color': 'white'
            },
        },
        'subplots': {
            "MACD": {
                'macd': {
                    'color': 'blue'
                },
                'macdsignal': {
                    'color': 'orange'
                },
            },
            "RSI": {
                'rsi': {
                    'color': 'red'
                },
            }
        }
    }

    def informative_pairs(self):
        return []

    def populate_indicators(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        # ADX
        dataframe['adx'] = ta.ADX(dataframe)

        # Plus Directional Indicator / Movement
        dataframe['plus_di'] = ta.PLUS_DI(dataframe)

        # Minus Directional Indicator / Movement
        dataframe['minus_di'] = ta.MINUS_DI(dataframe)

        # RSI
        dataframe['rsi'] = ta.RSI(dataframe)

        return dataframe

    buy_adx = IntParameter([0, 50], default=50, space='buy')
    buy_di_direction = CategoricalParameter(['<', '>'],
                                            default='>',
                                            space='buy')

    def populate_buy_trend(self, dataframe: DataFrame,
                           metadata: dict) -> DataFrame:
        minimum_coin_price = 0.0000015

        dataframe.loc[((dataframe['adx'] > self.buy_adx.value) &
                       (dataframe['plus_di'] > dataframe['minus_di'] if self.
                        buy_di_direction.value ==
                        '>' else dataframe['plus_di'] < dataframe['minus_di'])
                       & (dataframe["close"] > minimum_coin_price)), 'buy'] = 1

        return dataframe

    sell_rsi = IntParameter([20, 80], default=29, space='sell')
    sell_adx = IntParameter([0, 50], default=45, space='sell')

    def populate_sell_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        dataframe.loc[((dataframe['rsi'] < self.sell_rsi.value) &
                       (dataframe['adx'] < self.sell_adx.value)), 'sell'] = 1
        return dataframe
Ejemplo n.º 2
0
class FrostAuraM3Strategy(IStrategy):
    """
    This is FrostAura's mark 3 strategy which aims to make purchase decisions
    based on the BB, RSI and Stochastic.
    
    Last Optimization:
        Profit %        : 7.04%
        Optimized for   : Last 45 days, 1h
        Avg             : 2d 5h 20m
    """
    # Strategy interface version - allow new iterations of the strategy interface.
    # Check the documentation or the Sample strategy to get the latest version.
    INTERFACE_VERSION = 2

    # Minimal ROI designed for the strategy.
    minimal_roi = {"0": 0.186, "226": 0.145, "809": 0.085, "2235": 0}

    # Stoploss:
    stoploss = -0.303

    # Trailing stop:
    trailing_stop = False  # value loaded from strategy
    trailing_stop_positive = None  # value loaded from strategy
    trailing_stop_positive_offset = 0.0  # value loaded from strategy
    trailing_only_offset_is_reached = False  # value loaded from strategy

    # Trailing stoploss
    trailing_stop = False

    # Optimal ticker interval for the strategy.
    timeframe = '1h'

    # Run "populate_indicators()" only for new candle.
    process_only_new_candles = False

    # These values can be overridden in the "ask_strategy" section in the config.
    use_sell_signal = True
    sell_profit_only = False
    ignore_roi_if_buy_signal = False

    # Number of candles the strategy requires before producing valid signals.
    startup_candle_count: int = 30

    # Optional order type mapping.
    order_types = {
        'buy': 'limit',
        'sell': 'limit',
        'stoploss': 'market',
        'stoploss_on_exchange': False
    }

    # Optional order time in force.
    order_time_in_force = {'buy': 'gtc', 'sell': 'gtc'}

    plot_config = {
        'main_plot': {
            'tema': {},
            'sar': {
                'color': 'white'
            },
        },
        'subplots': {
            "MACD": {
                'macd': {
                    'color': 'blue'
                },
                'macdsignal': {
                    'color': 'orange'
                },
            },
            "RSI": {
                'rsi': {
                    'color': 'red'
                },
            }
        }
    }

    def informative_pairs(self):
        return []

    def populate_indicators(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        # RSI
        dataframe['rsi'] = ta.RSI(dataframe)

        # Stochastic Slow
        stoch = ta.STOCH(dataframe)
        dataframe['slowd'] = stoch['slowd']
        dataframe['slowk'] = stoch['slowk']

        # Bollinger Bands
        bollinger1 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe),
                                             window=20,
                                             stds=1)
        dataframe['bb_lowerband1'] = bollinger1['lower']
        dataframe['bb_middleband1'] = bollinger1['mid']
        dataframe['bb_upperband1'] = bollinger1['upper']

        bollinger2 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe),
                                             window=20,
                                             stds=2)
        dataframe['bb_lowerband2'] = bollinger2['lower']
        dataframe['bb_middleband2'] = bollinger2['mid']
        dataframe['bb_upperband2'] = bollinger2['upper']

        bollinger3 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe),
                                             window=20,
                                             stds=3)
        dataframe['bb_lowerband3'] = bollinger3['lower']
        dataframe['bb_middleband3'] = bollinger3['mid']
        dataframe['bb_upperband3'] = bollinger3['upper']

        bollinger4 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe),
                                             window=20,
                                             stds=4)
        dataframe['bb_lowerband4'] = bollinger4['lower']
        dataframe['bb_middleband4'] = bollinger4['mid']
        dataframe['bb_upperband4'] = bollinger4['upper']

        return dataframe

    buy_rsi = IntParameter([20, 80], default=20, space='buy')
    buy_band = CategoricalParameter(['lower', 'middle', 'upper'],
                                    default='lower',
                                    space='buy')
    buy_std = CategoricalParameter(['1', '2', '3', '4'],
                                   default='2',
                                   space='buy')
    buy_slowd = IntParameter([20, 80], default=23, space='buy')
    buy_slowk = IntParameter([20, 80], default=22, space='buy')

    def populate_buy_trend(self, dataframe: DataFrame,
                           metadata: dict) -> DataFrame:
        minimum_coin_price = 0.0000015
        var_buy_rsi = self.buy_rsi.value
        var_buy_band = self.buy_band.value
        var_buy_std = self.buy_std.value
        var_buy_band_value = dataframe['bb_' + var_buy_band + 'band' +
                                       var_buy_std]
        var_buy_slowd = self.buy_slowd.value
        var_buy_slowk = self.buy_slowk.value

        dataframe.loc[((dataframe['slowd'] < var_buy_slowd) &
                       (dataframe['slowk'] < var_buy_slowk) &
                       (dataframe['rsi'] < var_buy_rsi) &
                       (dataframe["close"] < var_buy_band_value) &
                       (dataframe["close"] > minimum_coin_price)), 'buy'] = 1

        return dataframe

    sell_rsi = IntParameter([20, 80], default=77, space='sell')
    sell_band = CategoricalParameter(['lower', 'middle', 'upper'],
                                     default='lower',
                                     space='sell')
    sell_std = CategoricalParameter(['1', '2', '3', '4'],
                                    default='4',
                                    space='sell')
    sell_slowd = IntParameter([20, 80], default=30, space='sell')
    sell_slowk = IntParameter([20, 80], default=61, space='sell')

    def populate_sell_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        var_sell_rsi = self.sell_rsi.value
        var_sell_band = self.sell_band.value
        var_sell_std = self.sell_std.value
        var_sell_band_value = dataframe['bb_' + var_sell_band + 'band' +
                                        var_sell_std]
        var_sell_slowd = self.sell_slowd.value
        var_sell_slowk = self.sell_slowk.value

        dataframe.loc[((dataframe['slowd'] > var_sell_slowd) &
                       (dataframe['slowk'] > var_sell_slowk) &
                       (dataframe['rsi'] > var_sell_rsi) &
                       (dataframe["close"] < var_sell_band_value)), 'sell'] = 1

        return dataframe
class Trend_Strength_Directional(IStrategy):
    INTERFACE_VERSION = 2

    timeframe = '15m'

    # ROI table:
    minimal_roi = {"0": 0.383, "120": 0.082, "283": 0.045, "495": 0}

    # Stoploss:
    stoploss = -0.314

    # Trailing stop:
    trailing_stop = True
    trailing_stop_positive = 0.307
    trailing_stop_positive_offset = 0.364
    trailing_only_offset_is_reached = False

    # Hyperopt Buy Parameters
    buy_plusdi_enabled = CategoricalParameter([True, False],
                                              space='buy',
                                              optimize=True,
                                              default=False)
    buy_adx = IntParameter(low=1,
                           high=100,
                           default=12,
                           space='buy',
                           optimize=True,
                           load=True)
    buy_adx_timeframe = IntParameter(low=1,
                                     high=50,
                                     default=9,
                                     space='buy',
                                     optimize=True,
                                     load=True)
    buy_plusdi = IntParameter(low=1,
                              high=100,
                              default=44,
                              space='buy',
                              optimize=True,
                              load=True)
    buy_minusdi = IntParameter(low=1,
                               high=100,
                               default=74,
                               space='buy',
                               optimize=True,
                               load=True)

    # Hyperopt Sell Parameters
    sell_plusdi_enabled = CategoricalParameter([True, False],
                                               space='sell',
                                               optimize=True,
                                               default=True)
    sell_adx = IntParameter(low=1,
                            high=100,
                            default=3,
                            space='sell',
                            optimize=True,
                            load=True)
    sell_adx_timeframe = IntParameter(low=1,
                                      high=50,
                                      default=41,
                                      space='sell',
                                      optimize=True,
                                      load=True)
    sell_plusdi = IntParameter(low=1,
                               high=100,
                               default=49,
                               space='sell',
                               optimize=True,
                               load=True)
    sell_minusdi = IntParameter(low=1,
                                high=100,
                                default=11,
                                space='sell',
                                optimize=True,
                                load=True)

    def populate_indicators(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        # RSI
        dataframe['rsi'] = ta.RSI(dataframe)
        return dataframe

    def populate_buy_trend(self, dataframe: DataFrame,
                           metadata: dict) -> DataFrame:
        conditions = []

        # GUARDS
        if self.buy_plusdi_enabled.value:
            conditions.append(
                ta.PLUS_DI(dataframe, timeperiod=int(self.buy_plusdi.value)) >
                ta.MINUS_DI(dataframe, timeperiod=int(self.buy_minusdi.value)))

        # TRIGGERS
        try:
            conditions.append(
                ta.ADX(dataframe, timeperiod=int(self.buy_adx_timeframe.value))
                > self.buy_adx.value)
        except Exception:
            pass

        if conditions:
            dataframe.loc[reduce(lambda x, y: x & y, conditions), 'buy'] = 1

        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        conditions = []

        # GUARDS
        if self.sell_plusdi_enabled.value:
            conditions.append(
                ta.PLUS_DI(dataframe, timeperiod=int(self.sell_plusdi.value)) <
                ta.MINUS_DI(dataframe, timeperiod=int(self.sell_minusdi.value))
            )

        # TRIGGERS
        try:
            conditions.append(
                ta.ADX(dataframe,
                       timeperiod=int(self.sell_adx_timeframe.value)) <
                self.sell_adx.value)
        except Exception:
            pass

        if conditions:
            dataframe.loc[reduce(lambda x, y: x & y, conditions), 'sell'] = 1

        return dataframe
Ejemplo n.º 4
0
class SMAOG(IStrategy):
    INTERFACE_VERSION = 2
    buy_params = {
        "base_nb_candles_buy": 26,
        "buy_trigger": "SMA",
        "low_offset": 0.968,
        "pair_is_bad_0_threshold": 0.555,
        "pair_is_bad_1_threshold": 0.172,
        "pair_is_bad_2_threshold": 0.198,
    }
    sell_params = {
        "base_nb_candles_sell": 28,
        "high_offset": 0.985,
        "sell_trigger": "EMA",
    }
    base_nb_candles_buy = IntParameter(
        16,
        45,
        default=buy_params['base_nb_candles_buy'],
        space='buy',
        optimize=False,
        load=True)
    base_nb_candles_sell = IntParameter(
        16,
        45,
        default=sell_params['base_nb_candles_sell'],
        space='sell',
        optimize=False,
        load=True)
    low_offset = DecimalParameter(0.8,
                                  0.99,
                                  default=buy_params['low_offset'],
                                  space='buy',
                                  optimize=False,
                                  load=True)
    high_offset = DecimalParameter(0.8,
                                   1.1,
                                   default=sell_params['high_offset'],
                                   space='sell',
                                   optimize=False,
                                   load=True)
    buy_trigger = CategoricalParameter(ma_types.keys(),
                                       default=buy_params['buy_trigger'],
                                       space='buy',
                                       optimize=False,
                                       load=True)
    sell_trigger = CategoricalParameter(ma_types.keys(),
                                        default=sell_params['sell_trigger'],
                                        space='sell',
                                        optimize=False,
                                        load=True)
    pair_is_bad_0_threshold = DecimalParameter(0.0,
                                               0.600,
                                               default=0.220,
                                               space='buy',
                                               optimize=True,
                                               load=True)
    pair_is_bad_1_threshold = DecimalParameter(0.0,
                                               0.350,
                                               default=0.090,
                                               space='buy',
                                               optimize=True,
                                               load=True)
    pair_is_bad_2_threshold = DecimalParameter(0.0,
                                               0.200,
                                               default=0.060,
                                               space='buy',
                                               optimize=True,
                                               load=True)

    timeframe = '5m'
    stoploss = -0.23
    minimal_roi = {
        "0": 10,
    }
    trailing_stop = True
    trailing_only_offset_is_reached = True
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.02
    use_sell_signal = True
    sell_profit_only = False
    ignore_roi_if_buy_signal = False
    process_only_new_candles = True
    startup_candle_count = 400

    def populate_indicators(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        if not self.config['runmode'].value == 'hyperopt':
            dataframe['ma_offset_buy'] = ma_types[self.buy_trigger.value](
                dataframe, int(
                    self.base_nb_candles_buy.value)) * self.low_offset.value
            dataframe['ma_offset_sell'] = ma_types[self.sell_trigger.value](
                dataframe, int(
                    self.base_nb_candles_sell.value)) * self.high_offset.value
            dataframe['pair_is_bad'] = (
                (((dataframe['open'].rolling(144).min() - dataframe['close']) /
                  dataframe['close']) >= self.pair_is_bad_0_threshold.value) |
                (((dataframe['open'].rolling(12).min() - dataframe['close']) /
                  dataframe['close']) >= self.pair_is_bad_1_threshold.value) |
                (((dataframe['open'].rolling(2).min() - dataframe['close']) /
                  dataframe['close']) >= self.pair_is_bad_2_threshold.value)
            ).astype('int')
            dataframe['ema_50'] = ta.EMA(dataframe, timeperiod=50)
            dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200)
            dataframe['rsi_exit'] = ta.RSI(dataframe, timeperiod=2)
        return dataframe

    def populate_buy_trend(self, dataframe: DataFrame,
                           metadata: dict) -> DataFrame:
        if self.config['runmode'].value == 'hyperopt':
            dataframe['ma_offset_buy'] = ma_types[self.buy_trigger.value](
                dataframe, int(
                    self.base_nb_candles_buy.value)) * self.low_offset.value
            dataframe['pair_is_bad'] = (
                (((dataframe['open'].rolling(144).min() - dataframe['close']) /
                  dataframe['close']) >= self.pair_is_bad_0_threshold.value) |
                (((dataframe['open'].rolling(12).min() - dataframe['close']) /
                  dataframe['close']) >= self.pair_is_bad_1_threshold.value) |
                (((dataframe['open'].rolling(2).min() - dataframe['close']) /
                  dataframe['close']) >= self.pair_is_bad_2_threshold.value)
            ).astype('int')
            dataframe['ema_50'] = ta.EMA(dataframe, timeperiod=50)
            dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200)
        dataframe.loc[((dataframe['ema_50'] > dataframe['ema_200']) &
                       (dataframe['close'] > dataframe['ema_200']) &
                       (dataframe['pair_is_bad'] < 1) &
                       (dataframe['close'] < dataframe['ma_offset_buy']) &
                       (dataframe['volume'] > 0)), 'buy'] = 1
        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        if self.config['runmode'].value == 'hyperopt':
            dataframe['ma_offset_sell'] = ta.EMA(
                dataframe, int(
                    self.base_nb_candles_sell.value)) * self.high_offset.value
        dataframe.loc[((dataframe['close'] > dataframe['ma_offset_sell']) & (
            (dataframe['open'] < dataframe['open'].shift(1)) |
            (dataframe['rsi_exit'] < 50) |
            (dataframe['rsi_exit'] < dataframe['rsi_exit'].shift(1))) &
                       (dataframe['volume'] > 0)), 'sell'] = 1
        return dataframe
Ejemplo n.º 5
0
class FrostAuraM4Strategy(IStrategy):
    """
    This is FrostAura's mark 4 stretagy with RSI and MACD.
    
    Last Optimization:
        Profit %        : 7.07%
        Optimized for   : Last 45 days, 4h
        Avg             : 5d 18h 30m
    """
    # Strategy interface version - allow new iterations of the strategy interface.
    # Check the documentation or the Sample strategy to get the latest version.
    INTERFACE_VERSION = 2

    # Minimal ROI designed for the strategy.
    minimal_roi = {
        "0": 0.578,
        "1617": 0.167,
        "2673": 0.105,
        "6903": 0
    }

    # Optimal stoploss designed for the strategy.
    stoploss = -0.307

    # Trailing stop:
    trailing_stop = False  # value loaded from strategy
    trailing_stop_positive = None  # value loaded from strategy
    trailing_stop_positive_offset = 0.0  # value loaded from strategy
    trailing_only_offset_is_reached = False  # value loaded from strategy

    # Trailing stoploss
    trailing_stop = False

    # Optimal ticker interval for the strategy.
    timeframe = '4h'

    # Run "populate_indicators()" only for new candle.
    process_only_new_candles = False

    # These values can be overridden in the "ask_strategy" section in the config.
    use_sell_signal = True
    sell_profit_only = False
    ignore_roi_if_buy_signal = False

    # Number of candles the strategy requires before producing valid signals.
    startup_candle_count: int = 30

    # Optional order type mapping.
    order_types = {
        'buy': 'limit',
        'sell': 'limit',
        'stoploss': 'market',
        'stoploss_on_exchange': False
    }

    # Optional order time in force.
    order_time_in_force = {
        'buy': 'gtc',
        'sell': 'gtc'
    }

    plot_config = {
        'main_plot': {
            'tema': {},
            'sar': {'color': 'white'},
        },
        'subplots': {
            "MACD": {
                'macd': {'color': 'blue'},
                'macdsignal': {'color': 'orange'},
            },
            "RSI": {
                'rsi': {'color': 'red'},
            }
        }
    }

    def informative_pairs(self):
        return []

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # RSI
        dataframe['rsi'] = ta.RSI(dataframe)
        
        # MACD
        macd = ta.MACD(dataframe)
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']
        dataframe['macdhist'] = macd['macdhist']

        return dataframe

    buy_rsi = IntParameter([20, 80], default=24, space='buy')
    buy_rsi_direction = CategoricalParameter(['<', '>'], default='>', space='buy')
    buy_macd_direction = CategoricalParameter(['<', '>'], default='<', space='buy')

    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        minimum_coin_price = 0.0000015
        
        dataframe.loc[
            (
                (dataframe['rsi'] < self.buy_rsi.value if self.buy_rsi_direction.value == '<' else dataframe['rsi'] > self.buy_rsi.value) &
                (dataframe['macd'] > dataframe['macdsignal'] if self.buy_macd_direction.value == '<' else dataframe['macd'] < dataframe['macdsignal']) &
                (dataframe["close"] > minimum_coin_price)
            ),
            'buy'] = 1

        return dataframe

    sell_rsi = IntParameter([20, 80], default=57, space='sell')
    sell_rsi_direction = CategoricalParameter(['<', '>'], default='<', space='sell')
    sell_macd_direction = CategoricalParameter(['<', '>'], default='>', space='sell')

    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                (dataframe['rsi'] < self.sell_rsi.value if self.sell_rsi_direction.value == '<' else dataframe['rsi'] > self.sell_rsi.value) &
                (dataframe['macd'] > dataframe['macdsignal'] if self.sell_macd_direction.value == '<' else dataframe['macd'] < dataframe['macdsignal'])
            ),
            'sell'] = 1
        
        return dataframe
Ejemplo n.º 6
0
class Solipsis4(IStrategy):

    ## Buy Space Hyperopt Variables

    # Base Pair Params
    base_mp = IntParameter(10, 50, default=30, space='buy')
    base_rmi_max = IntParameter(30, 60, default=50, space='buy')
    base_rmi_min = IntParameter(0, 30, default=20, space='buy')
    base_ma_streak = IntParameter(1, 4, default=1, space='buy')
    base_rmi_streak = IntParameter(3, 8, default=3, space='buy')
    base_trigger = CategoricalParameter(['pcc', 'rmi', 'none'],
                                        default='rmi',
                                        space='buy',
                                        optimize=False)
    inf_pct_adr = DecimalParameter(0.70, 0.99, default=0.80, space='buy')
    # BTC Informative
    xbtc_guard = CategoricalParameter(['strict', 'lazy', 'none'],
                                      default='lazy',
                                      space='buy',
                                      optimize=True)
    xbtc_base_rmi = IntParameter(20, 70, default=40, space='buy')
    # BTC / ETH Stake Parameters
    xtra_base_stake_rmi = IntParameter(10, 50, default=50, space='buy')
    xtra_base_fiat_rmi = IntParameter(30, 70, default=50, space='buy')

    ## Sell Space Params are being "hijacked" for custom_stoploss and dynamic_roi

    # Dynamic ROI
    droi_trend_type = CategoricalParameter(['rmi', 'ssl', 'candle', 'any'],
                                           default='any',
                                           space='sell',
                                           optimize=True)
    droi_pullback = CategoricalParameter([True, False],
                                         default=True,
                                         space='sell',
                                         optimize=True)
    droi_pullback_amount = DecimalParameter(0.005,
                                            0.02,
                                            default=0.005,
                                            space='sell')
    droi_pullback_respect_table = CategoricalParameter([True, False],
                                                       default=False,
                                                       space='sell',
                                                       optimize=True)

    # Custom Stoploss
    cstp_threshold = DecimalParameter(-0.05, 0, default=-0.03, space='sell')
    cstp_bail_how = CategoricalParameter(['roc', 'time', 'any'],
                                         default='roc',
                                         space='sell',
                                         optimize=True)
    cstp_bail_roc = DecimalParameter(-0.05, -0.01, default=-0.03, space='sell')
    cstp_bail_time = IntParameter(720, 1440, default=720, space='sell')

    timeframe = '5m'
    inf_timeframe = '1h'

    buy_params = {
        'base_ma_streak': 1,
        'base_mp': 12,
        'base_rmi_max': 50,
        'base_rmi_min': 20,
        'base_rmi_streak': 3,
        'inf_pct_adr': 950,
        'xbtc_base_rmi': 20,
        'xbtc_guard': 'none',
        'xtra_base_fiat_rmi': 45,
        'xtra_base_stake_rmi': 13
    }

    sell_params = {
        'droi_pullback': True,
        'droi_pullback_amount': 0.006,
        'droi_pullback_respect_table': False,
        'droi_trend_type': 'any'
    }

    minimal_roi = {"0": 0.01, "1440": 0}

    # Enable or disable these as desired
    # Must be enabled when hyperopting the respective spaces
    use_dynamic_roi = True
    use_custom_stoploss = True

    stoploss = -0.10

    # Recommended
    use_sell_signal = False
    sell_profit_only = False
    ignore_roi_if_buy_signal = False

    # Required
    startup_candle_count: int = 233
    process_only_new_candles = False

    # Strategy Specific Variable Storage
    custom_trade_info = {}
    custom_fiat = "USD"  # Only relevant if stake is BTC or ETH
    custom_btc_inf = False  # Don't change this.
    """
    Informative Pair Definitions
    """
    def informative_pairs(self):
        # add all whitelisted pairs on informative timeframe
        pairs = self.dp.current_whitelist()
        informative_pairs = [(pair, self.inf_timeframe) for pair in pairs]

        # add extra informative pairs if the stake is BTC or ETH
        if self.config['stake_currency'] in ('BTC', 'ETH'):
            for pair in pairs:
                coin, stake = pair.split('/')
                coin_fiat = f"{coin}/{self.custom_fiat}"
                informative_pairs += [(coin_fiat, self.timeframe)]

            stake_fiat = f"{self.config['stake_currency']}/{self.custom_fiat}"
            informative_pairs += [(stake_fiat, self.timeframe)]
        # if BTC/STAKE is not in whitelist, add it as an informative pair on both timeframes
        else:
            btc_stake = f"BTC/{self.config['stake_currency']}"
            if not btc_stake in pairs:
                informative_pairs += [(btc_stake, self.timeframe)]

        return informative_pairs

    """
    Indicator Definitions
    """

    def populate_indicators(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        if not metadata['pair'] in self.custom_trade_info:
            self.custom_trade_info[metadata['pair']] = {}

        ## Base Timeframe / Pair

        dataframe['kama'] = ta.KAMA(dataframe, length=233)

        # RMI: https://www.tradingview.com/script/kwIt9OgQ-Relative-Momentum-Index/
        dataframe['rmi'] = cta.RMI(dataframe, length=24, mom=5)

        # Momentum Pinball: https://www.tradingview.com/script/fBpVB1ez-Momentum-Pinball-Indicator/
        dataframe['roc-mp'] = ta.ROC(dataframe, timeperiod=1)
        dataframe['mp'] = ta.RSI(dataframe['roc-mp'], timeperiod=3)

        # MA Streak: https://www.tradingview.com/script/Yq1z7cIv-MA-Streak-Can-Show-When-a-Run-Is-Getting-Long-in-the-Tooth/
        dataframe['mastreak'] = cta.mastreak(dataframe, period=4)

        # Percent Change Channel: https://www.tradingview.com/script/6wwAWXA1-MA-Streak-Change-Channel/
        upper, mid, lower = cta.pcc(dataframe, period=40, mult=3)
        dataframe['pcc-lowerband'] = lower
        dataframe['pcc-upperband'] = upper

        lookup_idxs = dataframe.index.values - (
            abs(dataframe['mastreak'].values) + 1)
        valid_lookups = lookup_idxs >= 0
        dataframe['sbc'] = np.nan
        dataframe.loc[valid_lookups, 'sbc'] = dataframe['close'].to_numpy()[
            lookup_idxs[valid_lookups].astype(int)]

        dataframe['streak-roc'] = 100 * (dataframe['close'] -
                                         dataframe['sbc']) / dataframe['sbc']

        # Trends, Peaks and Crosses
        dataframe['candle-up'] = np.where(
            dataframe['close'] >= dataframe['close'].shift(), 1, 0)
        dataframe['candle-up-trend'] = np.where(
            dataframe['candle-up'].rolling(5).sum() >= 3, 1, 0)

        dataframe['rmi-up'] = np.where(
            dataframe['rmi'] >= dataframe['rmi'].shift(), 1, 0)
        dataframe['rmi-up-trend'] = np.where(
            dataframe['rmi-up'].rolling(5).sum() >= 3, 1, 0)

        dataframe['rmi-dn'] = np.where(
            dataframe['rmi'] <= dataframe['rmi'].shift(), 1, 0)
        dataframe['rmi-dn-count'] = dataframe['rmi-dn'].rolling(8).sum()

        dataframe['streak-bo'] = np.where(
            dataframe['streak-roc'] < dataframe['pcc-lowerband'], 1, 0)
        dataframe['streak-bo-count'] = dataframe['streak-bo'].rolling(8).sum()

        # Indicators used only for ROI and Custom Stoploss
        ssldown, sslup = cta.SSLChannels_ATR(dataframe, length=21)
        dataframe['sroc'] = cta.SROC(dataframe,
                                     roclen=21,
                                     emalen=13,
                                     smooth=21)
        dataframe['ssl-dir'] = np.where(sslup > ssldown, 'up', 'down')

        # Base pair informative timeframe indicators
        informative = self.dp.get_pair_dataframe(pair=metadata['pair'],
                                                 timeframe=self.inf_timeframe)

        # Get the "average day range" between the 1d high and 1d low to set up guards
        informative['1d-high'] = informative['close'].rolling(24).max()
        informative['1d-low'] = informative['close'].rolling(24).min()
        informative['adr'] = informative['1d-high'] - informative['1d-low']

        dataframe = merge_informative_pair(dataframe,
                                           informative,
                                           self.timeframe,
                                           self.inf_timeframe,
                                           ffill=True)

        # Other stake specific informative indicators
        # e.g if stake is BTC and current coin is XLM (pair: XLM/BTC)
        if self.config['stake_currency'] in ('BTC', 'ETH'):
            coin, stake = metadata['pair'].split('/')
            fiat = self.custom_fiat
            coin_fiat = f"{coin}/{fiat}"
            stake_fiat = f"{stake}/{fiat}"

            # Informative COIN/FIAT e.g. XLM/USD - Base Timeframe
            coin_fiat_tf = self.dp.get_pair_dataframe(pair=coin_fiat,
                                                      timeframe=self.timeframe)
            dataframe[f"{fiat}_rmi"] = cta.RMI(coin_fiat_tf, length=55, mom=5)

            # Informative STAKE/FIAT e.g. BTC/USD - Base Timeframe
            stake_fiat_tf = self.dp.get_pair_dataframe(
                pair=stake_fiat, timeframe=self.timeframe)
            dataframe[f"{stake}_rmi"] = cta.RMI(stake_fiat_tf,
                                                length=55,
                                                mom=5)

        # Informatives for BTC/STAKE if not in whitelist
        else:
            pairs = self.dp.current_whitelist()
            btc_stake = f"BTC/{self.config['stake_currency']}"
            if not btc_stake in pairs:
                self.custom_btc_inf = True
                # BTC/STAKE - Base Timeframe
                btc_stake_tf = self.dp.get_pair_dataframe(
                    pair=btc_stake, timeframe=self.timeframe)
                dataframe['BTC_rmi'] = cta.RMI(btc_stake_tf, length=55, mom=5)
                dataframe['BTC_close'] = btc_stake_tf['close']
                dataframe['BTC_kama'] = ta.KAMA(btc_stake_tf, length=144)

        # Slam some indicators into the trade_info dict so we can dynamic roi and custom stoploss in backtest
        if self.dp.runmode.value in ('backtest', 'hyperopt'):
            self.custom_trade_info[metadata['pair']]['sroc'] = dataframe[[
                'date', 'sroc'
            ]].copy().set_index('date')
            self.custom_trade_info[metadata['pair']]['ssl-dir'] = dataframe[[
                'date', 'ssl-dir'
            ]].copy().set_index('date')
            self.custom_trade_info[
                metadata['pair']]['rmi-up-trend'] = dataframe[[
                    'date', 'rmi-up-trend'
                ]].copy().set_index('date')
            self.custom_trade_info[
                metadata['pair']]['candle-up-trend'] = dataframe[[
                    'date', 'candle-up-trend'
                ]].copy().set_index('date')

        return dataframe

    """
    Buy Signal
    """

    def populate_buy_trend(self, dataframe: DataFrame,
                           metadata: dict) -> DataFrame:
        conditions = []

        # Informative Timeframe Guards
        conditions.append((
            dataframe['close'] <= dataframe[f"1d-low_{self.inf_timeframe}"] +
            (self.inf_pct_adr.value * dataframe[f"adr_{self.inf_timeframe}"])))

        # Base Timeframe Guards
        conditions.append(
            (dataframe['rmi-dn-count'] >= self.base_rmi_streak.value)
            & (dataframe['streak-bo-count'] >= self.base_ma_streak.value)
            & (dataframe['rmi'] <= self.base_rmi_max.value)
            & (dataframe['rmi'] >= self.base_rmi_min.value)
            & (dataframe['mp'] <= self.base_mp.value))

        # Base Timeframe Trigger
        if self.base_trigger.value == 'pcc':
            conditions.append(
                qtpylib.crossed_above(dataframe['streak-roc'],
                                      dataframe['pcc-lowerband']))

        if self.base_trigger.value == 'rmi':
            conditions.append(dataframe['rmi-up-trend'] == 1)

        # Extra conditions for */BTC and */ETH stakes on additional informative pairs
        if self.config['stake_currency'] in ('BTC', 'ETH'):
            conditions.append((
                dataframe[f"{self.custom_fiat}_rmi"] > self.xtra_base_fiat_rmi.
                value) | (dataframe[f"{self.config['stake_currency']}_rmi"] <
                          self.xtra_base_stake_rmi.value))
        # Extra conditions for BTC/STAKE if not in whitelist
        else:
            if self.custom_btc_inf:
                if self.xbtc_guard.value == 'strict':
                    conditions.append(
                        ((dataframe['BTC_rmi'] > self.xbtc_base_rmi.value) &
                         (dataframe['BTC_close'] > dataframe['BTC_kama'])))
                if self.xbtc_guard.value == 'lazy':
                    conditions.append(
                        (dataframe['close'] > dataframe['kama'])
                        | ((dataframe['BTC_rmi'] > self.xbtc_base_rmi.value)
                           & (dataframe['BTC_close'] > dataframe['BTC_kama'])))

        conditions.append(dataframe['volume'].gt(0))

        if conditions:
            dataframe.loc[reduce(lambda x, y: x & y, conditions), 'buy'] = 1

        return dataframe

    """
    Sell Signal
    """

    def populate_sell_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:

        dataframe['sell'] = 0

        return dataframe

    """
    Custom Stoploss
    """

    def custom_stoploss(self, pair: str, trade: 'Trade',
                        current_time: datetime, current_rate: float,
                        current_profit: float, **kwargs) -> float:

        trade_dur = int(
            (current_time.timestamp() - trade.open_date_utc.timestamp()) // 60)

        if self.config['runmode'].value in ('live', 'dry_run'):
            dataframe, last_updated = self.dp.get_analyzed_dataframe(
                pair=pair, timeframe=self.timeframe)
            sroc = dataframe['sroc'].iat[-1]
        # If in backtest or hyperopt, get the indicator values out of the trades dict (Thanks @JoeSchr!)
        else:
            sroc = self.custom_trade_info[
                trade.pair]['sroc'].loc[current_time]['sroc']

        if current_profit < self.cstp_threshold.value:
            if self.cstp_bail_how.value == 'roc' or self.cstp_bail_how.value == 'any':
                # Dynamic bailout based on rate of change
                if (sroc / 100) <= self.cstp_bail_roc.value:
                    return 0.001
            if self.cstp_bail_how.value == 'time' or self.cstp_bail_how.value == 'any':
                # Dynamic bailout based on time
                if trade_dur > self.cstp_bail_time.value:
                    return 0.001

        return 1

    """
    Freqtrade ROI Overload for dynamic ROI functionality
    """

    def min_roi_reached_dynamic(
            self, trade: Trade, current_profit: float, current_time: datetime,
            trade_dur: int) -> Tuple[Optional[int], Optional[float]]:

        minimal_roi = self.minimal_roi
        _, table_roi = self.min_roi_reached_entry(trade_dur)

        # see if we have the data we need to do this, otherwise fall back to the standard table
        if self.custom_trade_info and trade and trade.pair in self.custom_trade_info:
            if self.config['runmode'].value in ('live', 'dry_run'):
                dataframe, last_updated = self.dp.get_analyzed_dataframe(
                    pair=trade.pair, timeframe=self.timeframe)
                rmi_trend = dataframe['rmi-up-trend'].iat[-1]
                candle_trend = dataframe['candle-up-trend'].iat[-1]
                ssl_dir = dataframe['ssl-dir'].iat[-1]
            # If in backtest or hyperopt, get the indicator values out of the trades dict (Thanks @JoeSchr!)
            else:
                rmi_trend = self.custom_trade_info[trade.pair][
                    'rmi-up-trend'].loc[current_time]['rmi-up-trend']
                candle_trend = self.custom_trade_info[trade.pair][
                    'candle-up-trend'].loc[current_time]['candle-up-trend']
                ssl_dir = self.custom_trade_info[
                    trade.pair]['ssl-dir'].loc[current_time]['ssl-dir']

            min_roi = table_roi
            max_profit = trade.calc_profit_ratio(trade.max_rate)
            pullback_value = (max_profit - self.droi_pullback_amount.value)
            in_trend = False

            if self.droi_trend_type.value == 'rmi' or self.droi_trend_type.value == 'any':
                if rmi_trend == 1:
                    in_trend = True
            if self.droi_trend_type.value == 'ssl' or self.droi_trend_type.value == 'any':
                if ssl_dir == 'up':
                    in_trend = True
            if self.droi_trend_type.value == 'candle' or self.droi_trend_type.value == 'any':
                if candle_trend == 1:
                    in_trend = True

            # Force the ROI value high if in trend
            if (in_trend == True):
                min_roi = 100
                # If pullback is enabled, allow to sell if a pullback from peak has happened regardless of trend
                if self.droi_pullback.value == True and (current_profit <
                                                         pullback_value):
                    if self.droi_pullback_respect_table.value == True:
                        min_roi = table_roi
                    else:
                        min_roi = current_profit / 2

        else:
            min_roi = table_roi

        return trade_dur, min_roi

    # Change here to allow loading of the dynamic_roi settings
    def min_roi_reached(self, trade: Trade, current_profit: float,
                        current_time: datetime) -> bool:
        trade_dur = int(
            (current_time.timestamp() - trade.open_date_utc.timestamp()) // 60)

        if self.use_dynamic_roi:
            _, roi = self.min_roi_reached_dynamic(trade, current_profit,
                                                  current_time, trade_dur)
        else:
            _, roi = self.min_roi_reached_entry(trade_dur)
        if roi is None:
            return False
        else:
            return current_profit > roi

    """
    Trade Timeout Overloads
    """

    def check_buy_timeout(self, pair: str, trade: Trade, order: dict,
                          **kwargs) -> bool:
        bid_strategy = self.config.get('bid_strategy', {})
        ob = self.dp.orderbook(pair, 1)
        current_price = ob[f"{bid_strategy['price_side']}s"][0][0]
        if current_price > order['price'] * 1.01:
            return True
        return False

    def check_sell_timeout(self, pair: str, trade: Trade, order: dict,
                           **kwargs) -> bool:
        ask_strategy = self.config.get('ask_strategy', {})
        ob = self.dp.orderbook(pair, 1)
        current_price = ob[f"{ask_strategy['price_side']}s"][0][0]
        if current_price < order['price'] * 0.99:
            return True
        return False
Ejemplo n.º 7
0
class Strategy005(IStrategy):
    """
    Strategy 005
    author@: Gerald Lonlas
    github@: https://github.com/freqtrade/freqtrade-strategies

    How to use it?
    > python3 ./freqtrade/main.py -s Strategy005
    """
    INTERFACE_VERSION = 2

    # Minimal ROI designed for the strategy.
    # This attribute will be overridden if the config file contains "minimal_roi"
    minimal_roi = {
        "0": 0.154,
        "15": 0.084,
        "67": 0.028,
        "125": 0
        # "1440": 0.01,
        # "80": 0.02,
        # "40": 0.03,
        # "20": 0.04,
        # "0":  0.05
    }

    # Optimal stoploss designed for the strategy
    # This attribute will be overridden if the config file contains "stoploss"
    stoploss = -0.05

    # Optimal timeframe for the strategy
    timeframe = '5m'

    # trailing stoploss
    trailing_stop = False
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.02

    # run "populate_indicators" only for new candle
    process_only_new_candles = False

    # Experimental settings (configuration will overide these if set)
    use_sell_signal = True
    sell_profit_only = True
    ignore_roi_if_buy_signal = False

    # Optional order type mapping
    order_types = {
        'buy': 'limit',
        'sell': 'limit',
        'stoploss': 'market',
        'stoploss_on_exchange': False
    }

    buy_volumeAVG = IntParameter(low=50,
                                 high=300,
                                 default=70,
                                 space='buy',
                                 optimize=True)
    buy_rsi = IntParameter(low=1,
                           high=100,
                           default=30,
                           space='buy',
                           optimize=True)
    buy_fastd = IntParameter(low=1,
                             high=100,
                             default=30,
                             space='buy',
                             optimize=True)
    buy_fishRsiNorma = IntParameter(low=1,
                                    high=100,
                                    default=30,
                                    space='buy',
                                    optimize=True)

    sell_rsi = IntParameter(low=1,
                            high=100,
                            default=70,
                            space='sell',
                            optimize=True)
    sell_minusDI = IntParameter(low=1,
                                high=100,
                                default=50,
                                space='sell',
                                optimize=True)
    sell_fishRsiNorma = IntParameter(low=1,
                                     high=100,
                                     default=50,
                                     space='sell',
                                     optimize=True)
    sell_trigger = CategoricalParameter(["rsi-macd-minusdi", "sar-fisherRsi"],
                                        default=30,
                                        space='sell',
                                        optimize=True)

    # Buy hyperspace params:
    buy_params = {
        # "buy_fastd": 1,
        # "buy_fishRsiNorma": 5,
        # "buy_rsi": 26,
        # "buy_volumeAVG": 150,
        "buy_fastd": 92,
        "buy_fishRsiNorma": 39,
        "buy_rsi": 37,
        "buy_volumeAVG": 185,
    }

    # Sell hyperspace params:
    sell_params = {
        # "sell_fishRsiNorma": 30,
        # "sell_minusDI": 4,
        # "sell_rsi": 74,
        # "sell_trigger": "rsi-macd-minusdi",
        "sell_fishRsiNorma": 63,
        "sell_minusDI": 35,
        "sell_rsi": 41,
        "sell_trigger": "rsi-macd-minusdi",
    }

    def informative_pairs(self):
        """
        Define additional, informative pair/interval combinations to be cached from the exchange.
        These pair/interval combinations are non-tradeable, unless they are part
        of the whitelist as well.
        For more information, please consult the documentation
        :return: List of tuples in the format (pair, interval)
            Sample: return [("ETH/USDT", "5m"),
                            ("BTC/USDT", "15m"),
                            ]
        """
        return []

    def populate_indicators(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        """
        Adds several different TA indicators to the given DataFrame

        Performance Note: For the best performance be frugal on the number of indicators
        you are using. Let uncomment only the indicator you are using in your strategies
        or your hyperopt configuration, otherwise you will waste your memory and CPU usage.
        """

        # MACD
        macd = ta.MACD(dataframe)
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']

        # Minus Directional Indicator / Movement
        dataframe['minus_di'] = ta.MINUS_DI(dataframe)

        # RSI
        dataframe['rsi'] = ta.RSI(dataframe)

        # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy)
        rsi = 0.1 * (dataframe['rsi'] - 50)
        dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) -
                                   1) / (numpy.exp(2 * rsi) + 1)
        # Inverse Fisher transform on RSI normalized, value [0.0, 100.0] (https://goo.gl/2JGGoy)
        dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1)

        # Stoch fast
        stoch_fast = ta.STOCHF(dataframe)
        dataframe['fastd'] = stoch_fast['fastd']
        dataframe['fastk'] = stoch_fast['fastk']

        # Overlap Studies
        # ------------------------------------

        # SAR Parabol
        dataframe['sar'] = ta.SAR(dataframe)

        # SMA - Simple Moving Average
        dataframe['sma'] = ta.SMA(dataframe, timeperiod=40)

        return dataframe

    def populate_buy_trend(self, dataframe: DataFrame,
                           metadata: dict) -> DataFrame:
        """
        Based on TA indicators, populates the buy signal for the given dataframe
        :param dataframe: DataFrame
        :return: DataFrame with buy column
        """
        dataframe.loc[
            # Prod
            ((dataframe['close'] > 0.00000200) &
             (dataframe['volume'] > dataframe['volume'].
              rolling(self.buy_volumeAVG.value).mean() * 4) &
             (dataframe['close'] < dataframe['sma']) &
             (dataframe['fastd'] > dataframe['fastk']) &
             (dataframe['rsi'] > self.buy_rsi.value) &
             (dataframe['fastd'] > self.buy_fastd.value) &
             (dataframe['fisher_rsi_norma'] < self.buy_fishRsiNorma.value)),
            'buy'] = 1

        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        """
        Based on TA indicators, populates the sell signal for the given dataframe
        :param dataframe: DataFrame
        :return: DataFrame with buy column
        """

        conditions = []
        if self.sell_trigger.value == 'rsi-macd-minusdi':
            conditions.append(
                qtpylib.crossed_above(dataframe['rsi'], self.sell_rsi.value))
            conditions.append(dataframe['macd'] < 0)
            conditions.append(dataframe['minus_di'] > self.sell_minusDI.value)
        if self.sell_trigger.value == 'sar-fisherRsi':
            conditions.append(dataframe['sar'] > dataframe['close'])
            conditions.append(
                dataframe['fisher_rsi'] > self.sell_fishRsiNorma.value)

        if conditions:
            dataframe.loc[reduce(lambda x, y: x & y, conditions), 'sell'] = 1

        return dataframe
Ejemplo n.º 8
0
class BigZ04_TSL4(IStrategy):
    INTERFACE_VERSION = 2

    minimal_roi = {"0": 100.0}

    stoploss = -0.99  # effectively disabled.

    timeframe = '5m'
    inf_1h = '1h'

    # Sell signal
    use_sell_signal = True
    sell_profit_only = False
    sell_profit_offset = 0.001  # it doesn't meant anything, just to guarantee there is a minimal profit.
    ignore_roi_if_buy_signal = False

    # Trailing stoploss
    trailing_stop = False
    trailing_only_offset_is_reached = False
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.025

    # Custom stoploss
    use_custom_stoploss = True

    # Run "populate_indicators()" only for new candle.
    process_only_new_candles = True

    # Number of candles the strategy requires before producing valid signals
    startup_candle_count: int = 400

    # Optional order type mapping.
    order_types = {
        'buy': 'market',
        'sell': 'market',
        'stoploss': 'market',
        'stoploss_on_exchange': False
    }

    buy_params = {
        #############
        # Enable/Disable conditions
        "buy_condition_0_enable": True,
        "buy_condition_1_enable": True,
        "buy_condition_2_enable": True,
        "buy_condition_3_enable": True,
        "buy_condition_4_enable": True,
        "buy_condition_5_enable": True,
        "buy_condition_6_enable": True,
        "buy_condition_7_enable": True,
        "buy_condition_8_enable": True,
        "buy_condition_9_enable": True,
        "buy_condition_10_enable": True,
        "buy_condition_11_enable": True,
        "buy_condition_12_enable": True,
        "buy_condition_13_enable": False,
    }

    # V1 original
    # Sell hyperspace params:
    sell_params = {
        "base_nb_candles_sell": 49,
        "high_offset": 1.006,
        "pHSL": -0.08,
        "pPF_1": 0.016,
        "pSL_1": 0.011,
        "pPF_2": 0.080,
        "pSL_2": 0.040,
    }

    ############################################################################

    # Buy

    buy_condition_0_enable = CategoricalParameter([True, False],
                                                  default=True,
                                                  space='buy',
                                                  optimize=False,
                                                  load=True)
    buy_condition_1_enable = CategoricalParameter([True, False],
                                                  default=True,
                                                  space='buy',
                                                  optimize=False,
                                                  load=True)
    buy_condition_2_enable = CategoricalParameter([True, False],
                                                  default=True,
                                                  space='buy',
                                                  optimize=False,
                                                  load=True)
    buy_condition_3_enable = CategoricalParameter([True, False],
                                                  default=True,
                                                  space='buy',
                                                  optimize=False,
                                                  load=True)
    buy_condition_4_enable = CategoricalParameter([True, False],
                                                  default=True,
                                                  space='buy',
                                                  optimize=False,
                                                  load=True)
    buy_condition_5_enable = CategoricalParameter([True, False],
                                                  default=True,
                                                  space='buy',
                                                  optimize=False,
                                                  load=True)
    buy_condition_6_enable = CategoricalParameter([True, False],
                                                  default=True,
                                                  space='buy',
                                                  optimize=False,
                                                  load=True)
    buy_condition_7_enable = CategoricalParameter([True, False],
                                                  default=True,
                                                  space='buy',
                                                  optimize=False,
                                                  load=True)
    buy_condition_8_enable = CategoricalParameter([True, False],
                                                  default=True,
                                                  space='buy',
                                                  optimize=False,
                                                  load=True)
    buy_condition_9_enable = CategoricalParameter([True, False],
                                                  default=True,
                                                  space='buy',
                                                  optimize=False,
                                                  load=True)
    buy_condition_10_enable = CategoricalParameter([True, False],
                                                   default=True,
                                                   space='buy',
                                                   optimize=False,
                                                   load=True)
    buy_condition_11_enable = CategoricalParameter([True, False],
                                                   default=True,
                                                   space='buy',
                                                   optimize=False,
                                                   load=True)
    buy_condition_12_enable = CategoricalParameter([True, False],
                                                   default=True,
                                                   space='buy',
                                                   optimize=False,
                                                   load=True)
    buy_condition_13_enable = CategoricalParameter([True, False],
                                                   default=True,
                                                   space='buy',
                                                   optimize=False,
                                                   load=True)

    buy_bb20_close_bblowerband_safe_1 = DecimalParameter(0.950,
                                                         1.050,
                                                         default=0.989,
                                                         decimals=3,
                                                         space='buy',
                                                         optimize=False,
                                                         load=True)
    buy_bb20_close_bblowerband_safe_2 = DecimalParameter(0.700,
                                                         1.100,
                                                         default=0.982,
                                                         decimals=2,
                                                         space='buy',
                                                         optimize=False,
                                                         load=True)

    buy_volume_pump_1 = DecimalParameter(0.1,
                                         0.9,
                                         default=0.4,
                                         space='buy',
                                         decimals=1,
                                         optimize=False,
                                         load=True)
    buy_volume_drop_1 = DecimalParameter(1,
                                         10,
                                         default=3.8,
                                         space='buy',
                                         decimals=1,
                                         optimize=False,
                                         load=True)
    buy_volume_drop_2 = DecimalParameter(1,
                                         10,
                                         default=3,
                                         space='buy',
                                         decimals=1,
                                         optimize=False,
                                         load=True)
    buy_volume_drop_3 = DecimalParameter(1,
                                         10,
                                         default=2.7,
                                         space='buy',
                                         decimals=1,
                                         optimize=False,
                                         load=True)

    buy_rsi_1h_0 = DecimalParameter(55.0,
                                    85.0,
                                    default=71.0,
                                    space='buy',
                                    decimals=1,
                                    optimize=False,
                                    load=True)
    buy_rsi_1h_1a = DecimalParameter(65.0,
                                     78.0,
                                     default=69.0,
                                     space='buy',
                                     decimals=1,
                                     optimize=False,
                                     load=True)
    buy_rsi_1h_1 = DecimalParameter(10.0,
                                    40.0,
                                    default=16.5,
                                    space='buy',
                                    decimals=1,
                                    optimize=False,
                                    load=True)
    buy_rsi_1h_2 = DecimalParameter(10.0,
                                    40.0,
                                    default=15.0,
                                    space='buy',
                                    decimals=1,
                                    optimize=False,
                                    load=True)
    buy_rsi_1h_3 = DecimalParameter(10.0,
                                    40.0,
                                    default=20.0,
                                    space='buy',
                                    decimals=1,
                                    optimize=True,
                                    load=True)
    buy_rsi_1h_4 = DecimalParameter(10.0,
                                    40.0,
                                    default=35.0,
                                    space='buy',
                                    decimals=1,
                                    optimize=False,
                                    load=True)
    buy_rsi_1h_5 = DecimalParameter(10.0,
                                    60.0,
                                    default=39.0,
                                    space='buy',
                                    decimals=1,
                                    optimize=False,
                                    load=True)

    buy_rsi_0 = DecimalParameter(10.0,
                                 40.0,
                                 default=30.0,
                                 space='buy',
                                 decimals=1,
                                 optimize=False,
                                 load=True)
    buy_rsi_1 = DecimalParameter(10.0,
                                 40.0,
                                 default=28.0,
                                 space='buy',
                                 decimals=1,
                                 optimize=True,
                                 load=True)
    buy_rsi_2 = DecimalParameter(7.0,
                                 40.0,
                                 default=10.0,
                                 space='buy',
                                 decimals=1,
                                 optimize=False,
                                 load=True)
    buy_rsi_3 = DecimalParameter(7.0,
                                 40.0,
                                 default=14.2,
                                 space='buy',
                                 decimals=1,
                                 optimize=False,
                                 load=True)

    buy_macd_1 = DecimalParameter(0.01,
                                  0.09,
                                  default=0.02,
                                  space='buy',
                                  decimals=2,
                                  optimize=False,
                                  load=True)
    buy_macd_2 = DecimalParameter(0.01,
                                  0.09,
                                  default=0.03,
                                  space='buy',
                                  decimals=2,
                                  optimize=False,
                                  load=True)

    buy_dip_0 = DecimalParameter(1.015,
                                 1.040,
                                 default=1.024,
                                 space='buy',
                                 decimals=3,
                                 optimize=False,
                                 load=True)

    # hyperopt parameters for custom_stoploss()
    trade_time = IntParameter(25,
                              65,
                              default=35,
                              space='sell',
                              optimize=False,
                              load=True)
    rsi_1h_val = IntParameter(25,
                              45,
                              default=32,
                              space='sell',
                              optimize=False,
                              load=True)
    narrow_stop = DecimalParameter(1.005,
                                   1.030,
                                   default=1.020,
                                   space='sell',
                                   decimals=3,
                                   optimize=False,
                                   load=True)
    wide_stop = DecimalParameter(1.010,
                                 1.045,
                                 default=1.035,
                                 space='sell',
                                 decimals=3,
                                 optimize=False,
                                 load=True)

    # hyperopt parameters for SMAOffsetProtectOptV1 sell signal
    base_nb_candles_sell = IntParameter(5,
                                        80,
                                        default=49,
                                        space='sell',
                                        optimize=False,
                                        load=True)
    high_offset = DecimalParameter(0.99,
                                   1.1,
                                   default=1.006,
                                   space='sell',
                                   optimize=False,
                                   load=True)

    # trailing stoploss hyperopt parameters
    # hard stoploss profit
    pHSL = DecimalParameter(-0.200,
                            -0.040,
                            default=-0.08,
                            decimals=3,
                            space='sell',
                            optimize=False,
                            load=True)
    # profit threshold 1, trigger point, SL_1 is used
    pPF_1 = DecimalParameter(0.008,
                             0.020,
                             default=0.016,
                             decimals=3,
                             space='sell',
                             optimize=False,
                             load=True)
    pSL_1 = DecimalParameter(0.008,
                             0.020,
                             default=0.011,
                             decimals=3,
                             space='sell',
                             optimize=False,
                             load=True)

    # profit threshold 2, SL_2 is used
    pPF_2 = DecimalParameter(0.040,
                             0.100,
                             default=0.080,
                             decimals=3,
                             space='sell',
                             optimize=False,
                             load=True)
    pSL_2 = DecimalParameter(0.020,
                             0.070,
                             default=0.040,
                             decimals=3,
                             space='sell',
                             optimize=False,
                             load=True)

    def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str,
                           amount: float, rate: float, time_in_force: str,
                           sell_reason: str, **kwargs) -> bool:
        return True

    def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime',
                    current_rate: float, current_profit: float, **kwargs):
        return False

    # new custom stoploss, both hard and trailing functions. Trailing stoploss first rises at a slower
    # rate than the current rate until a profit threshold is reached, after which it rises at a constant
    # percentage as per a normal trailing stoploss. This allows more margin for pull-backs during a rise.
    def custom_stoploss(self, pair: str, trade: 'Trade',
                        current_time: datetime, current_rate: float,
                        current_profit: float, **kwargs) -> float:

        # hard stoploss profit
        HSL = self.pHSL.value
        PF_1 = self.pPF_1.value
        SL_1 = self.pSL_1.value
        PF_2 = self.pPF_2.value
        SL_2 = self.pSL_2.value

        # For profits between PF_1 and PF_2 the stoploss (sl_profit) used is linearly interpolated
        # between the values of SL_1 and SL_2. For all profits above PL_2 the sl_profit value
        # rises linearly with current profit, for profits below PF_1 the hard stoploss profit is used.

        if (current_profit > PF_2):
            sl_profit = SL_2 + (current_profit - PF_2)
        elif (current_profit > PF_1):
            sl_profit = SL_1 + ((current_profit - PF_1) * (SL_2 - SL_1) /
                                (PF_2 - PF_1))
        else:
            sl_profit = HSL

        return stoploss_from_open(sl_profit, current_profit)

    def informative_pairs(self):
        pairs = self.dp.current_whitelist()
        informative_pairs = [(pair, '1h') for pair in pairs]
        return informative_pairs

    def informative_1h_indicators(self, dataframe: DataFrame,
                                  metadata: dict) -> DataFrame:
        assert self.dp, "DataProvider is required for multiple timeframes."
        # Get the informative pair
        informative_1h = self.dp.get_pair_dataframe(pair=metadata['pair'],
                                                    timeframe=self.inf_1h)
        # EMA
        informative_1h['ema_50'] = ta.SMA(informative_1h, timeperiod=50)
        informative_1h['ema_200'] = ta.SMA(informative_1h, timeperiod=200)
        # RSI
        informative_1h['rsi'] = ta.RSI(informative_1h, timeperiod=14)

        bollinger = qtpylib.bollinger_bands(
            qtpylib.typical_price(informative_1h), window=20, stds=2)
        informative_1h['bb_lowerband'] = bollinger['lower']
        informative_1h['bb_middleband'] = bollinger['mid']
        informative_1h['bb_upperband'] = bollinger['upper']

        return informative_1h

    def normal_tf_indicators(self, dataframe: DataFrame,
                             metadata: dict) -> DataFrame:

        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe),
                                            window=20,
                                            stds=2)
        dataframe['bb_lowerband'] = bollinger['lower']
        dataframe['bb_middleband'] = bollinger['mid']
        dataframe['bb_upperband'] = bollinger['upper']

        dataframe['volume_mean_slow'] = dataframe['volume'].rolling(
            window=48).mean()

        # EMA
        dataframe['ema_200'] = ta.SMA(dataframe, timeperiod=200)

        dataframe['ema_26'] = ta.EMA(dataframe, timeperiod=26)
        dataframe['ema_12'] = ta.EMA(dataframe, timeperiod=12)

        # MACD
        dataframe['macd'], dataframe['signal'], dataframe['hist'] = ta.MACD(
            dataframe['close'], fastperiod=12, slowperiod=26, signalperiod=9)

        # SMA
        dataframe['sma_5'] = ta.EMA(dataframe, timeperiod=5)

        # RSI
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)

        # ------ ATR stuff
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)

        # Calculate all ma_sell values
        for val in self.base_nb_candles_sell.range:
            dataframe[f'ma_sell_{val}'] = ta.EMA(dataframe, timeperiod=val)

        return dataframe

    def populate_indicators(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        # The indicators for the 1h informative timeframe
        informative_1h = self.informative_1h_indicators(dataframe, metadata)
        dataframe = merge_informative_pair(dataframe,
                                           informative_1h,
                                           self.timeframe,
                                           self.inf_1h,
                                           ffill=True)

        # The indicators for the normal (5m) timeframe
        dataframe = self.normal_tf_indicators(dataframe, metadata)

        return dataframe

    def populate_buy_trend(self, dataframe: DataFrame,
                           metadata: dict) -> DataFrame:

        conditions = []

        conditions.append(
            (self.buy_condition_12_enable.value &
             (dataframe['close'] > dataframe['ema_200']) &
             (dataframe['close'] > dataframe['ema_200_1h']) &
             (dataframe['close'] < dataframe['bb_lowerband'] * 0.993) &
             (dataframe['low'] < dataframe['bb_lowerband'] * 0.985) &
             (dataframe['close'].shift() > dataframe['bb_lowerband']) &
             (dataframe['rsi_1h'] < 72.8) &
             (dataframe['open'] > dataframe['close']) &
             (dataframe['volume_mean_slow'] >
              dataframe['volume_mean_slow'].shift(48) *
              self.buy_volume_pump_1.value) &
             (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value <
              dataframe['volume_mean_slow'].shift(48)) &
             (dataframe['volume'] <
              (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) &
             ((dataframe['open'] - dataframe['close']) <
              dataframe['bb_upperband'].shift(2) -
              dataframe['bb_lowerband'].shift(2)) & (dataframe['volume'] > 0)))

        conditions.append((
            self.buy_condition_11_enable.value &
            (dataframe['close'] > dataframe['ema_200']) &
            (dataframe['hist'] > 0) & (dataframe['hist'].shift() > 0) &
            (dataframe['hist'].shift(2) > 0) & (dataframe['hist'].shift(3) > 0)
            & (dataframe['hist'].shift(5) > 0) &
            (dataframe['bb_middleband'] - dataframe['bb_middleband'].shift(5) >
             dataframe['close'] / 200) &
            (dataframe['bb_middleband'] - dataframe['bb_middleband'].shift(10)
             > dataframe['close'] / 100) &
            ((dataframe['bb_upperband'] - dataframe['bb_lowerband']) <
             (dataframe['close'] * 0.1)) &
            ((dataframe['open'].shift() - dataframe['close'].shift()) <
             (dataframe['close'] * 0.018)) & (dataframe['rsi'] > 51) &
            (dataframe['open'] < dataframe['close']) &
            (dataframe['open'].shift() > dataframe['close'].shift()) &
            (dataframe['close'] > dataframe['bb_middleband']) &
            (dataframe['close'].shift() < dataframe['bb_middleband'].shift()) &
            (dataframe['low'].shift(2) > dataframe['bb_middleband'].shift(2)) &
            (dataframe['volume'] > 0)  # Make sure Volume is not 0
        ))

        conditions.append((
            self.buy_condition_0_enable.value &
            (dataframe['close'] > dataframe['ema_200']) &
            (dataframe['rsi'] < self.buy_rsi_0.value) &
            ((dataframe['close'] * self.buy_dip_0.value <
              dataframe['open'].shift(3)) |
             (dataframe['close'] * self.buy_dip_0.value <
              dataframe['open'].shift(2)) |
             (dataframe['close'] * self.buy_dip_0.value <
              dataframe['open'].shift(1))) &
            (dataframe['rsi_1h'] < self.buy_rsi_1h_0.value) &
            (dataframe['volume_mean_slow'] >
             dataframe['volume_mean_slow'].shift(48) *
             self.buy_volume_pump_1.value) &
            (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value <
             dataframe['volume_mean_slow'].shift(48)) &
            (dataframe['volume'] > 0)  # Make sure Volume is not 0
        ))

        conditions.append(
            (self.buy_condition_1_enable.value &
             (dataframe['close'] > dataframe['ema_200']) &
             (dataframe['close'] > dataframe['ema_200_1h']) &
             (dataframe['close'] < dataframe['bb_lowerband'] *
              self.buy_bb20_close_bblowerband_safe_1.value) &
             (dataframe['rsi_1h'] < self.buy_rsi_1h_1a.value) &
             (dataframe['open'] > dataframe['close']) &
             (dataframe['volume_mean_slow'] >
              dataframe['volume_mean_slow'].shift(48) *
              self.buy_volume_pump_1.value) &
             (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value <
              dataframe['volume_mean_slow'].shift(48)) &
             (dataframe['volume'] <
              (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) &
             ((dataframe['open'] - dataframe['close']) <
              dataframe['bb_upperband'].shift(2) -
              dataframe['bb_lowerband'].shift(2)) & (dataframe['volume'] > 0)))

        conditions.append(
            (self.buy_condition_2_enable.value &
             (dataframe['close'] > dataframe['ema_200']) &
             (dataframe['close'] < dataframe['bb_lowerband'] *
              self.buy_bb20_close_bblowerband_safe_2.value) &
             (dataframe['volume_mean_slow'] >
              dataframe['volume_mean_slow'].shift(48) *
              self.buy_volume_pump_1.value) &
             (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value <
              dataframe['volume_mean_slow'].shift(48)) &
             (dataframe['volume'] <
              (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) &
             (dataframe['open'] - dataframe['close'] <
              dataframe['bb_upperband'].shift(2) -
              dataframe['bb_lowerband'].shift(2)) & (dataframe['volume'] > 0)))

        conditions.append(
            (self.buy_condition_3_enable.value &
             (dataframe['close'] > dataframe['ema_200_1h']) &
             (dataframe['close'] < dataframe['bb_lowerband']) &
             (dataframe['rsi'] < self.buy_rsi_3.value) &
             (dataframe['volume_mean_slow'] >
              dataframe['volume_mean_slow'].shift(48) *
              self.buy_volume_pump_1.value) &
             (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value <
              dataframe['volume_mean_slow'].shift(48)) &
             (dataframe['volume'] <
              (dataframe['volume'].shift() * self.buy_volume_drop_3.value)) &
             (dataframe['volume'] > 0)))

        conditions.append(
            (self.buy_condition_4_enable.value &
             (dataframe['rsi_1h'] < self.buy_rsi_1h_1.value) &
             (dataframe['close'] < dataframe['bb_lowerband']) &
             (dataframe['volume_mean_slow'] >
              dataframe['volume_mean_slow'].shift(48) *
              self.buy_volume_pump_1.value) &
             (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value <
              dataframe['volume_mean_slow'].shift(48)) &
             (dataframe['volume'] <
              (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) &
             (dataframe['volume'] > 0)))

        conditions.append((
            self.buy_condition_5_enable.value &
            (dataframe['close'] > dataframe['ema_200']) &
            (dataframe['close'] > dataframe['ema_200_1h']) &
            (dataframe['ema_26'] > dataframe['ema_12']) &
            ((dataframe['ema_26'] - dataframe['ema_12']) >
             (dataframe['open'] * self.buy_macd_1.value)) &
            ((dataframe['ema_26'].shift() - dataframe['ema_12'].shift()) >
             (dataframe['open'] / 100)) & (dataframe['close'] <
                                           (dataframe['bb_lowerband'])) &
            (dataframe['volume'] <
             (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) &
            (dataframe['volume_mean_slow'] >
             dataframe['volume_mean_slow'].shift(48) *
             self.buy_volume_pump_1.value) &
            (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value <
             dataframe['volume_mean_slow'].shift(48)) &
            (dataframe['volume'] > 0)  # Make sure Volume is not 0
        ))

        conditions.append(
            (self.buy_condition_6_enable.value &
             (dataframe['rsi_1h'] < self.buy_rsi_1h_5.value) &
             (dataframe['ema_26'] > dataframe['ema_12']) &
             ((dataframe['ema_26'] - dataframe['ema_12']) >
              (dataframe['open'] * self.buy_macd_2.value)) &
             ((dataframe['ema_26'].shift() - dataframe['ema_12'].shift()) >
              (dataframe['open'] / 100)) & (dataframe['close'] <
                                            (dataframe['bb_lowerband'])) &
             (dataframe['volume_mean_slow'] >
              dataframe['volume_mean_slow'].shift(48) *
              self.buy_volume_pump_1.value) &
             (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value <
              dataframe['volume_mean_slow'].shift(48)) &
             (dataframe['volume'] <
              (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) &
             (dataframe['volume'] > 0)))

        conditions.append(
            (self.buy_condition_7_enable.value &
             (dataframe['rsi_1h'] < self.buy_rsi_1h_2.value) &
             (dataframe['ema_26'] > dataframe['ema_12']) &
             ((dataframe['ema_26'] - dataframe['ema_12']) >
              (dataframe['open'] * self.buy_macd_1.value)) &
             ((dataframe['ema_26'].shift() - dataframe['ema_12'].shift()) >
              (dataframe['open'] / 100)) &
             (dataframe['volume'] <
              (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) &
             (dataframe['volume_mean_slow'] >
              dataframe['volume_mean_slow'].shift(48) *
              self.buy_volume_pump_1.value) &
             (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value <
              dataframe['volume_mean_slow'].shift(48)) &
             (dataframe['volume'] > 0)))

        conditions.append(
            (self.buy_condition_8_enable.value &
             (dataframe['rsi_1h'] < self.buy_rsi_1h_3.value) &
             (dataframe['rsi'] < self.buy_rsi_1.value) &
             (dataframe['volume'] <
              (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) &
             (dataframe['volume'] > 0)))

        conditions.append(
            (self.buy_condition_9_enable.value &
             (dataframe['rsi_1h'] < self.buy_rsi_1h_4.value) &
             (dataframe['rsi'] < self.buy_rsi_2.value) &
             (dataframe['volume'] <
              (dataframe['volume'].shift() * self.buy_volume_drop_1.value)) &
             (dataframe['volume_mean_slow'] >
              dataframe['volume_mean_slow'].shift(48) *
              self.buy_volume_pump_1.value) &
             (dataframe['volume_mean_slow'] * self.buy_volume_pump_1.value <
              dataframe['volume_mean_slow'].shift(48)) &
             (dataframe['volume'] > 0)))

        conditions.append(
            (self.buy_condition_10_enable.value &
             (dataframe['rsi_1h'] < self.buy_rsi_1h_4.value) &
             (dataframe['close_1h'] < dataframe['bb_lowerband_1h']) &
             (dataframe['hist'] > 0) & (dataframe['hist'].shift(2) < 0) &
             (dataframe['rsi'] < 40.5) &
             (dataframe['hist'] > dataframe['close'] * 0.0012) &
             (dataframe['open'] < dataframe['close']) &
             (dataframe['volume'] > 0)))

        if conditions:
            dataframe.loc[reduce(lambda x, y: x | y, conditions), 'buy'] = 1

        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        dataframe.loc[((dataframe['close'] > dataframe['bb_middleband'] * 1.01)
                       &  # Don't be gready, sell fast
                       (dataframe['volume'] > 0)  # Make sure Volume is not 0
                       ), 'sell'] = 0

        return dataframe
Ejemplo n.º 9
0
class Bandtastic(IStrategy):
    INTERFACE_VERSION = 2

    timeframe = '15m'

    # ROI table:
    minimal_roi = {"0": 0.162, "69": 0.097, "229": 0.061, "566": 0}

    # Stoploss:
    stoploss = -0.345

    # Trailing stop:
    trailing_stop = True
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.058
    trailing_only_offset_is_reached = False

    # Hyperopt Buy Parameters
    buy_fastema = IntParameter(low=1,
                               high=236,
                               default=211,
                               space='buy',
                               optimize=True,
                               load=True)
    buy_slowema = IntParameter(low=1,
                               high=126,
                               default=364,
                               space='buy',
                               optimize=True,
                               load=True)
    buy_rsi = IntParameter(low=15,
                           high=70,
                           default=52,
                           space='buy',
                           optimize=True,
                           load=True)
    buy_mfi = IntParameter(low=15,
                           high=70,
                           default=30,
                           space='buy',
                           optimize=True,
                           load=True)

    buy_rsi_enabled = CategoricalParameter([True, False],
                                           space='buy',
                                           optimize=True,
                                           default=False)
    buy_mfi_enabled = CategoricalParameter([True, False],
                                           space='buy',
                                           optimize=True,
                                           default=False)
    buy_ema_enabled = CategoricalParameter([True, False],
                                           space='buy',
                                           optimize=True,
                                           default=False)
    buy_trigger = CategoricalParameter(
        ["bb_lower1", "bb_lower2", "bb_lower3", "bb_lower4"],
        default="bb_lower1",
        space="buy")

    # Hyperopt Sell Parameters
    sell_fastema = IntParameter(low=1,
                                high=365,
                                default=7,
                                space='sell',
                                optimize=True,
                                load=True)
    sell_slowema = IntParameter(low=1,
                                high=365,
                                default=6,
                                space='sell',
                                optimize=True,
                                load=True)
    sell_rsi = IntParameter(low=30,
                            high=100,
                            default=57,
                            space='sell',
                            optimize=True,
                            load=True)
    sell_mfi = IntParameter(low=30,
                            high=100,
                            default=46,
                            space='sell',
                            optimize=True,
                            load=True)

    sell_rsi_enabled = CategoricalParameter([True, False],
                                            space='sell',
                                            optimize=True,
                                            default=False)
    sell_mfi_enabled = CategoricalParameter([True, False],
                                            space='sell',
                                            optimize=True,
                                            default=True)
    sell_ema_enabled = CategoricalParameter([True, False],
                                            space='sell',
                                            optimize=True,
                                            default=False)
    sell_trigger = CategoricalParameter([
        "sell-bb_upper1", "sell-bb_upper2", "sell-bb_upper3", "sell-bb_upper4"
    ],
                                        default="sell-bb_upper2",
                                        space="sell")

    def populate_indicators(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        # RSI
        dataframe['rsi'] = ta.RSI(dataframe)
        dataframe['mfi'] = ta.MFI(dataframe)

        # Bollinger Bands 1,2,3 and 4
        bollinger1 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe),
                                             window=20,
                                             stds=1)
        dataframe['bb_lowerband1'] = bollinger1['lower']
        dataframe['bb_middleband1'] = bollinger1['mid']
        dataframe['bb_upperband1'] = bollinger1['upper']

        bollinger2 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe),
                                             window=20,
                                             stds=2)
        dataframe['bb_lowerband2'] = bollinger2['lower']
        dataframe['bb_middleband2'] = bollinger2['mid']
        dataframe['bb_upperband2'] = bollinger2['upper']

        bollinger3 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe),
                                             window=20,
                                             stds=3)
        dataframe['bb_lowerband3'] = bollinger3['lower']
        dataframe['bb_middleband3'] = bollinger3['mid']
        dataframe['bb_upperband3'] = bollinger3['upper']

        bollinger4 = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe),
                                             window=20,
                                             stds=4)
        dataframe['bb_lowerband4'] = bollinger4['lower']
        dataframe['bb_middleband4'] = bollinger4['mid']
        dataframe['bb_upperband4'] = bollinger4['upper']
        return dataframe

    def populate_buy_trend(self, dataframe: DataFrame,
                           metadata: dict) -> DataFrame:
        conditions = []

        # GUARDS
        if self.buy_rsi_enabled.value:
            conditions.append(dataframe['rsi'] < self.buy_rsi.value)
        if self.buy_mfi_enabled.value:
            conditions.append(dataframe['mfi'] < self.buy_mfi.value)
        if self.buy_ema_enabled.value:
            try:
                conditions.append(
                    ta.EMA(dataframe, timeperiod=int(self.buy_fastema.value)) >
                    ta.EMA(dataframe, timeperiod=int(self.buy_slowema.value)))
            except Exception:
                pass

        # TRIGGERS
        if self.buy_trigger.value == 'bb_lower1':
            conditions.append(dataframe["close"] < dataframe['bb_lowerband1'])
        if self.buy_trigger.value == 'bb_lower2':
            conditions.append(dataframe["close"] < dataframe['bb_lowerband2'])
        if self.buy_trigger.value == 'bb_lower3':
            conditions.append(dataframe["close"] < dataframe['bb_lowerband3'])
        if self.buy_trigger.value == 'bb_lower4':
            conditions.append(dataframe["close"] < dataframe['bb_lowerband4'])

        # Check that volume is not 0
        conditions.append(dataframe['volume'] > 0)

        if conditions:
            dataframe.loc[reduce(lambda x, y: x & y, conditions), 'buy'] = 1

        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        conditions = []

        # GUARDS
        if self.sell_rsi_enabled.value:
            conditions.append(dataframe['rsi'] > self.sell_rsi.value)
        if self.sell_mfi_enabled.value:
            conditions.append(dataframe['mfi'] > self.sell_mfi.value)
        if self.sell_ema_enabled.value:
            try:
                conditions.append(
                    ta.EMA(dataframe, timeperiod=int(self.sell_fastema.value))
                    < ta.EMA(dataframe,
                             timeperiod=int(self.sell_slowema.value)))
            except Exception:
                pass

        # TRIGGERS
        if self.sell_trigger.value == 'sell-bb_upper1':
            conditions.append(dataframe["close"] > dataframe['bb_upperband1'])
        if self.sell_trigger.value == 'sell-bb_upper2':
            conditions.append(dataframe["close"] > dataframe['bb_upperband2'])
        if self.sell_trigger.value == 'sell-bb_upper3':
            conditions.append(dataframe["close"] > dataframe['bb_upperband3'])
        if self.sell_trigger.value == 'sell-bb_upper4':
            conditions.append(dataframe["close"] > dataframe['bb_upperband4'])

        # Check that volume is not 0
        conditions.append(dataframe['volume'] > 0)

        if conditions:
            dataframe.loc[reduce(lambda x, y: x & y, conditions), 'sell'] = 1

        return dataframe
Ejemplo n.º 10
0
class Solipsis5(IStrategy):

    ## Buy Space Hyperopt Variables

    # Base Pair Params
    base_mp = IntParameter(10, 50, default=30, space='buy', load=True, optimize=True)
    base_rmi_max = IntParameter(30, 60, default=50, space='buy', load=True, optimize=True)
    base_rmi_min = IntParameter(0, 30, default=20, space='buy', load=True, optimize=True)
    base_ma_streak = IntParameter(1, 4, default=1, space='buy', load=True, optimize=True)
    base_rmi_streak = IntParameter(3, 8, default=3, space='buy', load=True, optimize=True)
    base_trigger = CategoricalParameter(['pcc', 'rmi', 'none'], default='rmi', space='buy', load=True, optimize=True)
    inf_pct_adr = DecimalParameter(0.70, 0.99, default=0.80, space='buy', load=True, optimize=True)
    # BTC Informative
    xbtc_guard = CategoricalParameter(['strict', 'lazy', 'none'], default='lazy', space='buy', optimize=True)
    xbtc_base_rmi = IntParameter(20, 70, default=40, space='buy', load=True, optimize=True)
    # BTC / ETH Stake Parameters
    xtra_base_stake_rmi = IntParameter(10, 50, default=50, space='buy', load=True, optimize=True)
    xtra_base_fiat_rmi = IntParameter(30, 70, default=50, space='buy', load=True, optimize=True)

    ## Sell Space Params are being used for both custom_stoploss and custom_sell

    # Custom Sell Profit (formerly Dynamic ROI)
    csell_roi_type = CategoricalParameter(['static', 'decay', 'step'], default='step', space='sell', load=True, optimize=True)
    csell_roi_time = IntParameter(720, 1440, default=720, space='sell', load=True, optimize=True)
    csell_roi_start = DecimalParameter(0.01, 0.05, default=0.01, space='sell', load=True, optimize=True)
    csell_roi_end = DecimalParameter(0.0, 0.01, default=0, space='sell', load=True, optimize=True)
    csell_trend_type = CategoricalParameter(['rmi', 'ssl', 'candle', 'any', 'none'], default='any', space='sell', load=True, optimize=True)
    csell_pullback = CategoricalParameter([True, False], default=True, space='sell', load=True, optimize=True)
    csell_pullback_amount = DecimalParameter(0.005, 0.03, default=0.01, space='sell', load=True, optimize=True)
    csell_pullback_respect_roi = CategoricalParameter([True, False], default=False, space='sell', load=True, optimize=True)
    csell_endtrend_respect_roi = CategoricalParameter([True, False], default=False, space='sell', load=True, optimize=True)

    # Custom Stoploss
    cstop_loss_threshold = DecimalParameter(-0.05, -0.01, default=-0.03, space='sell', load=True, optimize=True)
    cstop_bail_how = CategoricalParameter(['roc', 'time', 'any', 'none'], default='none', space='sell', load=True, optimize=True)
    cstop_bail_roc = DecimalParameter(-5.0, -1.0, default=-3.0, space='sell', load=True, optimize=True)
    cstop_bail_time = IntParameter(60, 1440, default=720, space='sell', load=True, optimize=True)
    cstop_bail_time_trend = CategoricalParameter([True, False], default=True, space='sell', load=True, optimize=True)
    
    timeframe = '5m'
    inf_timeframe = '1h'

    buy_params = {}

    sell_params = {}

    minimal_roi = {
        "0": 100
    }

    stoploss = -0.99
    use_custom_stoploss = True

    # Recommended
    use_sell_signal = True
    sell_profit_only = True
    ignore_roi_if_buy_signal = True

    # Required
    startup_candle_count: int = 233
    process_only_new_candles = False

    # Strategy Specific Variable Storage
    custom_trade_info = {}
    custom_fiat = "USD" # Only relevant if stake is BTC or ETH
    custom_btc_inf = False # Don't change this.
    
    """
    Informative Pair Definitions
    """
    def informative_pairs(self):
        # add all whitelisted pairs on informative timeframe
        pairs = self.dp.current_whitelist()
        informative_pairs = [(pair, self.inf_timeframe) for pair in pairs]
        
        # add extra informative pairs if the stake is BTC or ETH
        if self.config['stake_currency'] in ('BTC', 'ETH'):
            for pair in pairs:
                coin, stake = pair.split('/')
                coin_fiat = f"{coin}/{self.custom_fiat}"
                informative_pairs += [(coin_fiat, self.timeframe)]

            stake_fiat = f"{self.config['stake_currency']}/{self.custom_fiat}"
            informative_pairs += [(stake_fiat, self.timeframe)]
        # if BTC/STAKE is not in whitelist, add it as an informative pair on both timeframes
        else:
            btc_stake = f"BTC/{self.config['stake_currency']}"
            if not btc_stake in pairs:
                informative_pairs += [(btc_stake, self.timeframe)]
        
        return informative_pairs

    """
    Indicator Definitions
    """ 
    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        if not metadata['pair'] in self.custom_trade_info:
            self.custom_trade_info[metadata['pair']] = {}
            if not 'had-trend' in self.custom_trade_info[metadata["pair"]]:
                self.custom_trade_info[metadata['pair']]['had-trend'] = False

        ## Base Timeframe / Pair

        # Kaufmann Adaptive Moving Average
        dataframe['kama'] = ta.KAMA(dataframe, length=233)
    
        # RMI: https://www.tradingview.com/script/kwIt9OgQ-Relative-Momentum-Index/
        dataframe['rmi'] = cta.RMI(dataframe, length=24, mom=5)

        # Momentum Pinball: https://www.tradingview.com/script/fBpVB1ez-Momentum-Pinball-Indicator/
        dataframe['roc-mp'] = ta.ROC(dataframe, timeperiod=1)
        dataframe['mp']  = ta.RSI(dataframe['roc-mp'], timeperiod=3)

        # MA Streak: https://www.tradingview.com/script/Yq1z7cIv-MA-Streak-Can-Show-When-a-Run-Is-Getting-Long-in-the-Tooth/
        dataframe['mastreak'] = cta.mastreak(dataframe, period=4)

        # Percent Change Channel: https://www.tradingview.com/script/6wwAWXA1-MA-Streak-Change-Channel/
        upper, mid, lower = cta.pcc(dataframe, period=40, mult=3)
        dataframe['pcc-lowerband'] = lower
        dataframe['pcc-upperband'] = upper

        lookup_idxs = dataframe.index.values - (abs(dataframe['mastreak'].values) + 1)
        valid_lookups = lookup_idxs >= 0
        dataframe['sbc'] = np.nan
        dataframe.loc[valid_lookups, 'sbc'] = dataframe['close'].to_numpy()[lookup_idxs[valid_lookups].astype(int)]

        dataframe['streak-roc'] = 100 * (dataframe['close'] - dataframe['sbc']) / dataframe['sbc']

        # Trends, Peaks and Crosses
        dataframe['candle-up'] = np.where(dataframe['close'] >= dataframe['open'],1,0)
        dataframe['candle-up-trend'] = np.where(dataframe['candle-up'].rolling(5).sum() >= 3,1,0)

        dataframe['rmi-up'] = np.where(dataframe['rmi'] >= dataframe['rmi'].shift(),1,0)      
        dataframe['rmi-up-trend'] = np.where(dataframe['rmi-up'].rolling(5).sum() >= 3,1,0)      

        dataframe['rmi-dn'] = np.where(dataframe['rmi'] <= dataframe['rmi'].shift(),1,0)
        dataframe['rmi-dn-count'] = dataframe['rmi-dn'].rolling(8).sum()

        dataframe['streak-bo'] = np.where(dataframe['streak-roc'] < dataframe['pcc-lowerband'],1,0)
        dataframe['streak-bo-count'] = dataframe['streak-bo'].rolling(8).sum()

        # Indicators used only for ROI and Custom Stoploss
        ssldown, sslup = cta.SSLChannels_ATR(dataframe, length=21)
        dataframe['sroc'] = cta.SROC(dataframe, roclen=21, emalen=13, smooth=21)
        dataframe['ssl-dir'] = np.where(sslup > ssldown,'up','down')

        # Base pair informative timeframe indicators
        informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.inf_timeframe)
        
        # Get the "average day range" between the 1d high and 1d low to set up guards
        informative['1d-high'] = informative['close'].rolling(24).max()
        informative['1d-low'] = informative['close'].rolling(24).min()
        informative['adr'] = informative['1d-high'] - informative['1d-low']

        dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.inf_timeframe, ffill=True)

        # Other stake specific informative indicators
        # e.g if stake is BTC and current coin is XLM (pair: XLM/BTC)
        if self.config['stake_currency'] in ('BTC', 'ETH'):
            coin, stake = metadata['pair'].split('/')
            fiat = self.custom_fiat
            coin_fiat = f"{coin}/{fiat}"
            stake_fiat = f"{stake}/{fiat}"

            # Informative COIN/FIAT e.g. XLM/USD - Base Timeframe
            coin_fiat_tf = self.dp.get_pair_dataframe(pair=coin_fiat, timeframe=self.timeframe)
            dataframe[f"{fiat}_rmi"] = cta.RMI(coin_fiat_tf, length=55, mom=5)

            # Informative STAKE/FIAT e.g. BTC/USD - Base Timeframe
            stake_fiat_tf = self.dp.get_pair_dataframe(pair=stake_fiat, timeframe=self.timeframe)
            dataframe[f"{stake}_rmi"] = cta.RMI(stake_fiat_tf, length=55, mom=5)

        # Informatives for BTC/STAKE if not in whitelist
        else:
            pairs = self.dp.current_whitelist()
            btc_stake = f"BTC/{self.config['stake_currency']}"
            if not btc_stake in pairs:
                self.custom_btc_inf = True
                # BTC/STAKE - Base Timeframe
                btc_stake_tf = self.dp.get_pair_dataframe(pair=btc_stake, timeframe=self.timeframe)
                dataframe['BTC_rmi'] = cta.RMI(btc_stake_tf, length=55, mom=5)
                dataframe['BTC_close'] = btc_stake_tf['close']
                dataframe['BTC_kama'] = ta.KAMA(btc_stake_tf, length=144)

        return dataframe

    """
    Buy Signal
    """ 
    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []

        # Informative Timeframe Guards
        conditions.append(
            (dataframe['close'] <= dataframe[f"1d-low_{self.inf_timeframe}"] + 
            (self.inf_pct_adr.value * dataframe[f"adr_{self.inf_timeframe}"]))
        )

        # Base Timeframe Guards
        conditions.append(
            (dataframe['rmi-dn-count'] >= self.base_rmi_streak.value) &
            (dataframe['streak-bo-count'] >= self.base_ma_streak.value) &
            (dataframe['rmi'] <= self.base_rmi_max.value) &
            (dataframe['rmi'] >= self.base_rmi_min.value) &
            (dataframe['mp'] <= self.base_mp.value)        
        )

        # Base Timeframe Trigger
        if self.base_trigger.value == 'pcc':
            conditions.append(qtpylib.crossed_above(dataframe['streak-roc'], dataframe['pcc-lowerband']))

        if self.base_trigger.value == 'rmi':
            conditions.append(dataframe['rmi-up-trend'] == 1)

        # Extra conditions for */BTC and */ETH stakes on additional informative pairs
        if self.config['stake_currency'] in ('BTC', 'ETH'):
            conditions.append(
                (dataframe[f"{self.custom_fiat}_rmi"] > self.xtra_base_fiat_rmi.value) |
                (dataframe[f"{self.config['stake_currency']}_rmi"] < self.xtra_base_stake_rmi.value)
            )
        # Extra conditions for BTC/STAKE if not in whitelist
        else:
            if self.custom_btc_inf:
                if self.xbtc_guard.value == 'strict':
                    conditions.append(
                        (
                            (dataframe['BTC_rmi'] > self.xbtc_base_rmi.value) &
                            (dataframe['BTC_close'] > dataframe['BTC_kama'])
                        )
                    )
                if self.xbtc_guard.value == 'lazy':
                    conditions.append(
                        (dataframe['close'] > dataframe['kama']) |
                        (
                            (dataframe['BTC_rmi'] > self.xbtc_base_rmi.value) &
                            (dataframe['BTC_close'] > dataframe['BTC_kama'])
                        )
                    )

        conditions.append(dataframe['volume'].gt(0))

        if conditions:
            dataframe.loc[
                reduce(lambda x, y: x & y, conditions),
                'buy'] = 1

        return dataframe

    """
    Sell Signal
    """
    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
  
        dataframe['sell'] = 0

        return dataframe

    """
    Custom Stoploss
    """
    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float:

        dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()
        trade_dur = int((current_time.timestamp() - trade.open_date_utc.timestamp()) // 60)
        in_trend = self.custom_trade_info[trade.pair]['had-trend']

        # Determine how we sell when we are in a loss
        if current_profit < self.cstop_loss_threshold.value:
            if self.cstop_bail_how.value == 'roc' or self.cstop_bail_how.value == 'any':
                # Dynamic bailout based on rate of change
                if last_candle['sroc'] <= self.cstop_bail_roc.value:
                    return 0.01
            if self.cstop_bail_how.value == 'time' or self.cstop_bail_how.value == 'any':
                # Dynamic bailout based on time, unless time_trend is true and there is a potential reversal
                if trade_dur > self.cstop_bail_time.value:
                    if self.cstop_bail_time_trend.value == True and in_trend == True:
                        return 1
                    else:
                        return 0.01
        return 1

    """
    Custom Sell
    """
    def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
                    current_profit: float, **kwargs):
                    
        dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()

        trade_dur = int((current_time.timestamp() - trade.open_date_utc.timestamp()) // 60)
        max_profit = max(0, trade.calc_profit_ratio(trade.max_rate))
        pullback_value = max(0, (max_profit - self.csell_pullback_amount.value))
        in_trend = False

        # Determine our current ROI point based on the defined type
        if self.csell_roi_type.value == 'static':
            min_roi = self.csell_roi_start.value
        elif self.csell_roi_type.value == 'decay':
            min_roi = cta.linear_decay(self.csell_roi_start.value, self.csell_roi_end.value, 0, self.csell_roi_time.value, trade_dur)
        elif self.csell_roi_type.value == 'step':
            if trade_dur < self.csell_roi_time.value:
                min_roi = self.csell_roi_start.value
            else:
                min_roi = self.csell_roi_end.value

        # Determine if there is a trend
        if self.csell_trend_type.value == 'rmi' or self.csell_trend_type.value == 'any':
            if last_candle['rmi-up-trend'] == 1:
                in_trend = True
        if self.csell_trend_type.value == 'ssl' or self.csell_trend_type.value == 'any':
            if last_candle['ssl-dir'] == 'up':
                in_trend = True
        if self.csell_trend_type.value == 'candle' or self.csell_trend_type.value == 'any':
            if last_candle['candle-up-trend'] == 1:
                in_trend = True

        # Don't sell if we are in a trend unless the pullback threshold is met
        if in_trend == True and current_profit > 0:
            # Record that we were in a trend for this trade/pair for a more useful sell message later
            self.custom_trade_info[trade.pair]['had-trend'] = True
            # If pullback is enabled and profit has pulled back allow a sell, maybe
            if self.csell_pullback.value == True and (current_profit <= pullback_value):
                if self.csell_pullback_respect_roi.value == True and current_profit > min_roi:
                    return 'intrend_pullback_roi' 
                elif self.csell_pullback_respect_roi.value == False: 
                    if current_profit > min_roi:
                        return 'intrend_pullback_roi'
                    else:
                        return 'intrend_pullback_noroi'
            # We are in a trend and pullback is disabled or has not happened or various criteria were not met, hold
            return None
        # If we are not in a trend, just use the roi value
        elif in_trend == False:
            if self.custom_trade_info[trade.pair]['had-trend']:
                if current_profit > min_roi:
                    self.custom_trade_info[trade.pair]['had-trend'] = False
                    return 'trend_roi'
                elif self.csell_endtrend_respect_roi.value == False:
                    self.custom_trade_info[trade.pair]['had-trend'] = False
                    return 'trend_noroi'
            elif current_profit > min_roi: 
                return 'notrend_roi'
        else:
            return None
Ejemplo n.º 11
0
class Seb(IStrategy):
    """
    Strategy 001
    author@: Gerald Lonlas
    github@: https://github.com/freqtrade/freqtrade-strategies

    How to use it?
    > python3 ./freqtrade/main.py -s Strategy001
    """

    # Minimal ROI designed for the strategy.
    # This attribute will be overridden if the config file contains "minimal_roi"
    minimal_roi = {"60": 0.01, "30": 0.03, "20": 0.04, "0": 0.05}

    # Optimal stoploss designed for the strategy
    # This attribute will be overridden if the config file contains "stoploss"
    stoploss = -0.10

    # Optimal timeframe for the strategy
    timeframe = '5m'

    # trailing stoploss
    trailing_stop = False
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.02

    # run "populate_indicators" only for new candle
    process_only_new_candles = False

    # Experimental settings (configuration will overide these if set)
    use_sell_signal = True
    sell_profit_only = True
    ignore_roi_if_buy_signal = False

    # Optional order type mapping
    order_types = {
        'buy': 'limit',
        'sell': 'limit',
        'stoploss': 'market',
        'stoploss_on_exchange': False
    }

    buy_volumeAVG = IntParameter(low=50,
                                 high=300,
                                 default=70,
                                 space='buy',
                                 optimize=True)
    buy_rsi = IntParameter(low=1,
                           high=100,
                           default=30,
                           space='buy',
                           optimize=True)
    buy_fastd = IntParameter(low=1,
                             high=100,
                             default=30,
                             space='buy',
                             optimize=True)
    buy_fishRsiNorma = IntParameter(low=1,
                                    high=100,
                                    default=30,
                                    space='buy',
                                    optimize=True)

    sell_rsi = IntParameter(low=1,
                            high=100,
                            default=70,
                            space='sell',
                            optimize=True)
    sell_minusDI = IntParameter(low=1,
                                high=100,
                                default=50,
                                space='sell',
                                optimize=True)
    sell_fishRsiNorma = IntParameter(low=1,
                                     high=100,
                                     default=50,
                                     space='sell',
                                     optimize=True)
    sell_trigger = CategoricalParameter(["rsi-macd-minusdi", "sar-fisherRsi"],
                                        default=30,
                                        space='sell',
                                        optimize=True)

    # Buy hyperspace params:
    buy_params = {
        "buy_fastd": 1,
        "buy_fishRsiNorma": 5,
        "buy_rsi": 26,
        "buy_volumeAVG": 150,
    }

    # Sell hyperspace params:
    sell_params = {
        "sell_fishRsiNorma": 30,
        "sell_minusDI": 4,
        "sell_rsi": 74,
        "sell_trigger": "rsi-macd-minusdi",
    }

    def informative_pairs(self):
        """
        Define additional, informative pair/interval combinations to be cached from the exchange.
        These pair/interval combinations are non-tradeable, unless they are part
        of the whitelist as well.
        For more information, please consult the documentation
        :return: List of tuples in the format (pair, interval)
            Sample: return [("ETH/USDT", "5m"),
                            ("BTC/USDT", "15m"),
                            ]
        """
        return []

    def populate_indicators(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        """
        Adds several different TA indicators to the given DataFrame

        Performance Note: For the best performance be frugal on the number of indicators
        you are using. Let uncomment only the indicator you are using in your strategies
        or your hyperopt configuration, otherwise you will waste your memory and CPU usage.
        """

        dataframe['ema20'] = ta.EMA(dataframe, timeperiod=20)
        dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
        dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)

        heikinashi = qtpylib.heikinashi(dataframe)
        dataframe['ha_open'] = heikinashi['open']
        dataframe['ha_close'] = heikinashi['close']

        dataframe['adx'] = ta.ADX(dataframe)
        dataframe['rsi'] = ta.RSI(dataframe)
        macd = ta.MACD(dataframe)
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']
        dataframe['macdhist'] = macd['macdhist']

        bollinger = ta.BBANDS(dataframe,
                              timeperiod=20,
                              nbdevup=2.0,
                              nbdevdn=2.0)
        dataframe['bb_lowerband'] = bollinger['lowerband']
        dataframe['bb_middleband'] = bollinger['middleband']
        dataframe['bb_upperband'] = bollinger['upperband']

        # Stoch
        stoch = ta.STOCH(dataframe)
        dataframe['slowk'] = stoch['slowk']

        # Commodity Channel Index: values Oversold:<-100, Overbought:>100
        dataframe['cci'] = ta.CCI(dataframe)

        # Stoch
        stoch = ta.STOCHF(dataframe, 5)
        dataframe['fastd'] = stoch['fastd']
        dataframe['fastk'] = stoch['fastk']
        dataframe['fastk-previous'] = dataframe.fastk.shift(1)
        dataframe['fastd-previous'] = dataframe.fastd.shift(1)

        # Slow Stoch
        slowstoch = ta.STOCHF(dataframe, 50)
        dataframe['slowfastd'] = slowstoch['fastd']
        dataframe['slowfastk'] = slowstoch['fastk']
        dataframe['slowfastk-previous'] = dataframe.slowfastk.shift(1)
        dataframe['slowfastd-previous'] = dataframe.slowfastd.shift(1)

        # RSI
        dataframe['rsi'] = ta.RSI(dataframe)

        # Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy)
        rsi = 0.1 * (dataframe['rsi'] - 50)
        dataframe['fisher_rsi'] = (numpy.exp(2 * rsi) -
                                   1) / (numpy.exp(2 * rsi) + 1)

        # Bollinger bands
        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe),
                                            window=20,
                                            stds=2)
        dataframe['bb_lowerband'] = bollinger['lower']

        # SAR Parabol
        dataframe['sar'] = ta.SAR(dataframe)

        # Hammer: values [0, 100]
        dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe)

        # SMA - Simple Moving Average
        dataframe['sma'] = ta.SMA(dataframe, timeperiod=40)

        return dataframe

    def populate_buy_trend(self, dataframe: DataFrame,
                           metadata: dict) -> DataFrame:
        """
        Based on TA indicators, populates the buy signal for the given dataframe
        :param dataframe: DataFrame
        :return: DataFrame with buy column
        """
        dataframe.loc[(
            qtpylib.crossed_above(dataframe['ema20'], dataframe['ema50']) &
            (dataframe['ha_close'] > dataframe['ema20']) &
            (dataframe['ha_open'] < dataframe['ha_close'])  # green bar
        ), 'buy'] = 1

        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        """
        Based on TA indicators, populates the sell signal for the given dataframe
        :param dataframe: DataFrame
        :return: DataFrame with buy column
        """
        dataframe.loc[(
            qtpylib.crossed_above(dataframe['ema50'], dataframe['ema100']) &
            (dataframe['ha_close'] < dataframe['ema20']) &
            (dataframe['ha_open'] > dataframe['ha_close'])  # red bar
        ), 'sell'] = 1
        return dataframe
class CryptoFrog(IStrategy):

    # ROI table - this strat REALLY benefits from roi and trailing hyperopt:
    minimal_roi = {"0": 0.213, "39": 0.103, "96": 0.037, "166": 0}

    # Stoploss:
    stoploss = -0.085

    # Trailing stop:
    trailing_stop = True
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.047
    trailing_only_offset_is_reached = False

    use_custom_stoploss = True
    custom_stop = {
        # Linear Decay Parameters
        'decay-time':
        166,  # minutes to reach end, I find it works well to match this to the final ROI value - default 1080
        'decay-delay': 0,  # minutes to wait before decay starts
        'decay-start':
        -0.085,  # -0.32118, # -0.07163,     # starting value: should be the same or smaller than initial stoploss - default -0.30
        'decay-end': -0.02,  # ending value - default -0.03
        # Profit and TA
        'cur-min-diff':
        0.03,  # diff between current and minimum profit to move stoploss up to min profit point
        'cur-threshold':
        -0.02,  # how far negative should current profit be before we consider moving it up based on cur/min or roc
        'roc-bail': -0.03,  # value for roc to use for dynamic bailout
        'rmi-trend': 50,  # rmi-slow value to pause stoploss decay
        'bail-how':
        'immediate',  # set the stoploss to the atr offset below current price, or immediate
        # Positive Trailing
        'pos-trail': True,  # enable trailing once positive  
        'pos-threshold': 0.005,  # trail after how far positive
        'pos-trail-dist': 0.015  # how far behind to place the trail
    }

    # Dynamic ROI
    droi_trend_type = CategoricalParameter(['rmi', 'ssl', 'candle', 'any'],
                                           default='any',
                                           space='sell',
                                           optimize=True)
    droi_pullback = CategoricalParameter([True, False],
                                         default=True,
                                         space='sell',
                                         optimize=True)
    droi_pullback_amount = DecimalParameter(0.005,
                                            0.02,
                                            default=0.005,
                                            space='sell')
    droi_pullback_respect_table = CategoricalParameter([True, False],
                                                       default=False,
                                                       space='sell',
                                                       optimize=True)

    # Custom Stoploss
    cstp_threshold = DecimalParameter(-0.05, 0, default=-0.03, space='sell')
    cstp_bail_how = CategoricalParameter(['roc', 'time', 'any'],
                                         default='roc',
                                         space='sell',
                                         optimize=True)
    cstp_bail_roc = DecimalParameter(-0.05, -0.01, default=-0.03, space='sell')
    cstp_bail_time = IntParameter(720, 1440, default=720, space='sell')

    stoploss = custom_stop['decay-start']

    custom_trade_info = {}
    custom_current_price_cache: TTLCache = TTLCache(maxsize=100,
                                                    ttl=300)  # 5 minutes

    # run "populate_indicators" only for new candle
    process_only_new_candles = False

    # Experimental settings (configuration will overide these if set)
    use_sell_signal = True
    sell_profit_only = False
    ignore_roi_if_buy_signal = False

    use_dynamic_roi = True

    timeframe = '5m'
    informative_timeframe = '1h'

    # Optional order type mapping
    order_types = {
        'buy': 'limit',
        'sell': 'limit',
        'stoploss': 'market',
        'stoploss_on_exchange': False
    }

    plot_config = {
        'main_plot': {
            'Smooth_HA_H': {
                'color': 'orange'
            },
            'Smooth_HA_L': {
                'color': 'yellow'
            },
        },
        'subplots': {
            "StochRSI": {
                'srsi_k': {
                    'color': 'blue'
                },
                'srsi_d': {
                    'color': 'red'
                },
            },
            "MFI": {
                'mfi': {
                    'color': 'green'
                },
            },
            "BBEXP": {
                'bbw_expansion': {
                    'color': 'orange'
                },
            },
            "FAST": {
                'fastd': {
                    'color': 'red'
                },
                'fastk': {
                    'color': 'blue'
                },
            },
            "SQZMI": {
                'sqzmi': {
                    'color': 'lightgreen'
                },
            },
            "VFI": {
                'vfi': {
                    'color': 'lightblue'
                },
            },
            "DMI": {
                'dmi_plus': {
                    'color': 'orange'
                },
                'dmi_minus': {
                    'color': 'yellow'
                },
            },
            "EMACO": {
                'emac_1h': {
                    'color': 'red'
                },
                'emao_1h': {
                    'color': 'blue'
                },
            },
        }
    }

    def informative_pairs(self):
        pairs = self.dp.current_whitelist()
        #pairs.append("BTC/USDT")
        #pairs.append("ETH/USDT")
        informative_pairs = [(pair, self.informative_timeframe)
                             for pair in pairs]
        return informative_pairs

    ## smoothed Heiken Ashi
    def HA(self, dataframe, smoothing=None):
        df = dataframe.copy()

        df['HA_Close'] = (df['open'] + df['high'] + df['low'] +
                          df['close']) / 4

        df.reset_index(inplace=True)

        ha_open = [(df['open'][0] + df['close'][0]) / 2]
        [
            ha_open.append((ha_open[i] + df['HA_Close'].values[i]) / 2)
            for i in range(0,
                           len(df) - 1)
        ]
        df['HA_Open'] = ha_open

        df.set_index('index', inplace=True)

        df['HA_High'] = df[['HA_Open', 'HA_Close', 'high']].max(axis=1)
        df['HA_Low'] = df[['HA_Open', 'HA_Close', 'low']].min(axis=1)

        if smoothing is not None:
            sml = abs(int(smoothing))
            if sml > 0:
                df['Smooth_HA_O'] = ta.EMA(df['HA_Open'], sml)
                df['Smooth_HA_C'] = ta.EMA(df['HA_Close'], sml)
                df['Smooth_HA_H'] = ta.EMA(df['HA_High'], sml)
                df['Smooth_HA_L'] = ta.EMA(df['HA_Low'], sml)

        return df

    def hansen_HA(self, informative_df, period=6):
        dataframe = informative_df.copy()

        dataframe['hhclose'] = (dataframe['open'] + dataframe['high'] +
                                dataframe['low'] + dataframe['close']) / 4
        dataframe['hhopen'] = (
            (dataframe['open'].shift(2) + dataframe['close'].shift(2)) / 2
        )  #it is not the same as real heikin ashi since I found that this is better.
        dataframe['hhhigh'] = dataframe[['open', 'close', 'high']].max(axis=1)
        dataframe['hhlow'] = dataframe[['open', 'close', 'low']].min(axis=1)

        dataframe['emac'] = ta.SMA(
            dataframe['hhclose'],
            timeperiod=period)  #to smooth out the data and thus less noise.
        dataframe['emao'] = ta.SMA(dataframe['hhopen'], timeperiod=period)

        return {'emac': dataframe['emac'], 'emao': dataframe['emao']}

    ## detect BB width expansion to indicate possible volatility
    def bbw_expansion(self, bbw_rolling, mult=1.1):
        bbw = list(bbw_rolling)

        m = 0.0
        for i in range(len(bbw) - 1):
            if bbw[i] > m:
                m = bbw[i]

        if (bbw[-1] > (m * mult)):
            return 1
        return 0

    ## do_indicator style a la Obelisk strategies
    def do_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # Stoch fast - mainly due to 5m timeframes
        stoch_fast = ta.STOCHF(dataframe)
        dataframe['fastd'] = stoch_fast['fastd']
        dataframe['fastk'] = stoch_fast['fastk']

        #StochRSI for double checking things
        period = 14
        smoothD = 3
        SmoothK = 3
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
        stochrsi = (dataframe['rsi'] - dataframe['rsi'].rolling(period).min()
                    ) / (dataframe['rsi'].rolling(period).max() -
                         dataframe['rsi'].rolling(period).min())
        dataframe['srsi_k'] = stochrsi.rolling(SmoothK).mean() * 100
        dataframe['srsi_d'] = dataframe['srsi_k'].rolling(smoothD).mean()

        # Bollinger Bands because obviously
        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe),
                                            window=20,
                                            stds=1)
        dataframe['bb_lowerband'] = bollinger['lower']
        dataframe['bb_middleband'] = bollinger['mid']
        dataframe['bb_upperband'] = bollinger['upper']

        # SAR Parabol - probably don't need this
        dataframe['sar'] = ta.SAR(dataframe)

        ## confirm wideboi variance signal with bbw expansion
        dataframe["bb_width"] = (
            (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) /
            dataframe["bb_middleband"])
        dataframe['bbw_expansion'] = dataframe['bb_width'].rolling(
            window=4).apply(self.bbw_expansion)

        # confirm entry and exit on smoothed HA
        dataframe = self.HA(dataframe, 4)

        # thanks to Hansen_Khornelius for this idea that I apply to the 1hr informative
        # https://github.com/hansen1015/freqtrade_strategy
        hansencalc = self.hansen_HA(dataframe, 6)
        dataframe['emac'] = hansencalc['emac']
        dataframe['emao'] = hansencalc['emao']

        # money flow index (MFI) for in/outflow of money, like RSI adjusted for vol
        dataframe['mfi'] = fta.MFI(dataframe)

        ## sqzmi to detect quiet periods
        dataframe['sqzmi'] = fta.SQZMI(dataframe)  #, MA=hansencalc['emac'])

        # Volume Flow Indicator (MFI) for volume based on the direction of price movement
        dataframe['vfi'] = fta.VFI(dataframe, period=14)

        dmi = fta.DMI(dataframe, period=14)
        dataframe['dmi_plus'] = dmi['DI+']
        dataframe['dmi_minus'] = dmi['DI-']
        dataframe['adx'] = fta.ADX(dataframe, period=14)

        ## for stoploss - all from Solipsis4
        ## simple ATR and ROC for stoploss
        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        dataframe['roc'] = ta.ROC(dataframe, timeperiod=9)
        dataframe['rmi'] = RMI(dataframe, length=24, mom=5)
        ssldown, sslup = SSLChannels_ATR(dataframe, length=21)
        dataframe['sroc'] = SROC(dataframe, roclen=21, emalen=13, smooth=21)
        dataframe['ssl-dir'] = np.where(sslup > ssldown, 'up', 'down')
        dataframe['rmi-up'] = np.where(
            dataframe['rmi'] >= dataframe['rmi'].shift(), 1, 0)
        dataframe['rmi-up-trend'] = np.where(
            dataframe['rmi-up'].rolling(5).sum() >= 3, 1, 0)
        dataframe['candle-up'] = np.where(
            dataframe['close'] >= dataframe['close'].shift(), 1, 0)
        dataframe['candle-up-trend'] = np.where(
            dataframe['candle-up'].rolling(5).sum() >= 3, 1, 0)

        return dataframe

    ## stolen from Obelisk's Ichi strat code and backtest blog post, and Solipsis4
    def populate_indicators(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        # Populate/update the trade data if there is any, set trades to false if not live/dry
        self.custom_trade_info[metadata['pair']] = self.populate_trades(
            metadata['pair'])

        if self.config['runmode'].value in ('backtest', 'hyperopt'):
            assert (timeframe_to_minutes(self.timeframe) <=
                    30), "Backtest this strategy in 5m or 1m timeframe."

        if self.timeframe == self.informative_timeframe:
            dataframe = self.do_indicators(dataframe, metadata)
        else:
            if not self.dp:
                return dataframe

            informative = self.dp.get_pair_dataframe(
                pair=metadata['pair'], timeframe=self.informative_timeframe)

            informative = self.do_indicators(informative.copy(), metadata)

            dataframe = merge_informative_pair(dataframe,
                                               informative,
                                               self.timeframe,
                                               self.informative_timeframe,
                                               ffill=True)

            skip_columns = [(s + "_" + self.informative_timeframe) for s in [
                'date', 'open', 'high', 'low', 'close', 'volume', 'emac',
                'emao'
            ]]
            dataframe.rename(columns=lambda s: s.replace(
                "_{}".format(self.informative_timeframe), "")
                             if (not s in skip_columns) else s,
                             inplace=True)

        # Slam some indicators into the trade_info dict so we can dynamic roi and custom stoploss in backtest
        if self.dp.runmode.value in ('backtest', 'hyperopt'):
            self.custom_trade_info[metadata['pair']]['roc'] = dataframe[[
                'date', 'roc'
            ]].copy().set_index('date')
            self.custom_trade_info[metadata['pair']]['atr'] = dataframe[[
                'date', 'atr'
            ]].copy().set_index('date')
            self.custom_trade_info[metadata['pair']]['sroc'] = dataframe[[
                'date', 'sroc'
            ]].copy().set_index('date')
            self.custom_trade_info[metadata['pair']]['ssl-dir'] = dataframe[[
                'date', 'ssl-dir'
            ]].copy().set_index('date')
            self.custom_trade_info[
                metadata['pair']]['rmi-up-trend'] = dataframe[[
                    'date', 'rmi-up-trend'
                ]].copy().set_index('date')
            self.custom_trade_info[
                metadata['pair']]['candle-up-trend'] = dataframe[[
                    'date', 'candle-up-trend'
                ]].copy().set_index('date')

        return dataframe

    ## cryptofrog signals
    def populate_buy_trend(self, dataframe: DataFrame,
                           metadata: dict) -> DataFrame:
        dataframe.loc[(
            (
                ## close ALWAYS needs to be lower than the heiken low at 5m
                (dataframe['close'] < dataframe['Smooth_HA_L'])
                &
                ## Hansen's HA EMA at informative timeframe
                (dataframe['emac_1h'] < dataframe['emao_1h']))
            &
            ((
                ## potential uptick incoming so buy
                (dataframe['bbw_expansion'] == 1) &
                (dataframe['sqzmi'] == False)
                & ((dataframe['mfi'] < 20)
                   | (dataframe['dmi_minus'] > 30)))
             | (
                 # this tries to find extra buys in undersold regions
                 (dataframe['close'] < dataframe['sar'])
                 &
                 ((dataframe['srsi_d'] >= dataframe['srsi_k']) &
                  (dataframe['srsi_d'] < 30))
                 &
                 ((dataframe['fastd'] > dataframe['fastk']) &
                  (dataframe['fastd'] < 23))
                 &
                 (dataframe['mfi'] < 30))
             |
             (
                 # find smaller temporary dips in sideways
                 (((dataframe['dmi_minus'] > 30) & qtpylib.crossed_above(
                     dataframe['dmi_minus'], dataframe['dmi_plus']))
                  &
                  (dataframe['close'] < dataframe['bb_lowerband']))
                 |
                 (
                     ## if nothing else is making a buy signal
                     ## just throw in any old SQZMI shit based fastd
                     ## this needs work!
                     (dataframe['sqzmi'] == True)
                     & ((dataframe['fastd'] > dataframe['fastk']) &
                        (dataframe['fastd'] < 20))))
             ## volume sanity checks
             & (dataframe['vfi'] < 0.0)
             & (dataframe['volume'] > 0))), 'buy'] = 1

        return dataframe

    ## more going on here
    def populate_sell_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        dataframe.loc[(
            (
                ## close ALWAYS needs to be higher than the heiken high at 5m
                (dataframe['close'] > dataframe['Smooth_HA_H'])
                &
                ## Hansen's HA EMA at informative timeframe
                (dataframe['emac_1h'] > dataframe['emao_1h']))
            & (
                ## try to find oversold regions with a corresponding BB expansion
                ((dataframe['bbw_expansion'] == 1)
                 & ((dataframe['mfi'] > 80)
                    | (dataframe['dmi_plus'] > 30)))
                ## volume sanity checks
                & (dataframe['vfi'] > 0.0)
                & (dataframe['volume'] > 0))), 'sell'] = 1
        return dataframe

    """
    Everything from here completely stolen from the godly work of @werkkrew
    
    Custom Stoploss 
    """

    def custom_stoploss(self, pair: str, trade: 'Trade',
                        current_time: datetime, current_rate: float,
                        current_profit: float, **kwargs) -> float:
        trade_dur = int(
            (current_time.timestamp() - trade.open_date_utc.timestamp()) // 60)

        if self.config['runmode'].value in ('live', 'dry_run'):
            dataframe, last_updated = self.dp.get_analyzed_dataframe(
                pair=pair, timeframe=self.timeframe)
            sroc = dataframe['sroc'].iat[-1]
        # If in backtest or hyperopt, get the indicator values out of the trades dict (Thanks @JoeSchr!)
        else:
            sroc = self.custom_trade_info[
                trade.pair]['sroc'].loc[current_time]['sroc']

        if current_profit < self.cstp_threshold.value:
            if self.cstp_bail_how.value == 'roc' or self.cstp_bail_how.value == 'any':
                # Dynamic bailout based on rate of change
                if (sroc / 100) <= self.cstp_bail_roc.value:
                    return 0.001
            if self.cstp_bail_how.value == 'time' or self.cstp_bail_how.value == 'any':
                # Dynamic bailout based on time
                if trade_dur > self.cstp_bail_time.value:
                    return 0.001

        return 1

    """
    Freqtrade ROI Overload for dynamic ROI functionality
    """

    def min_roi_reached_dynamic(
            self, trade: Trade, current_profit: float, current_time: datetime,
            trade_dur: int) -> Tuple[Optional[int], Optional[float]]:

        minimal_roi = self.minimal_roi
        _, table_roi = self.min_roi_reached_entry(trade_dur)

        # see if we have the data we need to do this, otherwise fall back to the standard table
        if self.custom_trade_info and trade and trade.pair in self.custom_trade_info:
            if self.config['runmode'].value in ('live', 'dry_run'):
                dataframe, last_updated = self.dp.get_analyzed_dataframe(
                    pair=trade.pair, timeframe=self.timeframe)
                rmi_trend = dataframe['rmi-up-trend'].iat[-1]
                candle_trend = dataframe['candle-up-trend'].iat[-1]
                ssl_dir = dataframe['ssl-dir'].iat[-1]
            # If in backtest or hyperopt, get the indicator values out of the trades dict (Thanks @JoeSchr!)
            else:
                rmi_trend = self.custom_trade_info[trade.pair][
                    'rmi-up-trend'].loc[current_time]['rmi-up-trend']
                candle_trend = self.custom_trade_info[trade.pair][
                    'candle-up-trend'].loc[current_time]['candle-up-trend']
                ssl_dir = self.custom_trade_info[
                    trade.pair]['ssl-dir'].loc[current_time]['ssl-dir']

            min_roi = table_roi
            max_profit = trade.calc_profit_ratio(trade.max_rate)
            pullback_value = (max_profit - self.droi_pullback_amount.value)
            in_trend = False

            if self.droi_trend_type.value == 'rmi' or self.droi_trend_type.value == 'any':
                if rmi_trend == 1:
                    in_trend = True
            if self.droi_trend_type.value == 'ssl' or self.droi_trend_type.value == 'any':
                if ssl_dir == 'up':
                    in_trend = True
            if self.droi_trend_type.value == 'candle' or self.droi_trend_type.value == 'any':
                if candle_trend == 1:
                    in_trend = True

            # Force the ROI value high if in trend
            if (in_trend == True):
                min_roi = 100
                # If pullback is enabled, allow to sell if a pullback from peak has happened regardless of trend
                if self.droi_pullback.value == True and (current_profit <
                                                         pullback_value):
                    if self.droi_pullback_respect_table.value == True:
                        min_roi = table_roi
                    else:
                        min_roi = current_profit / 2

        else:
            min_roi = table_roi

        return trade_dur, min_roi

    # Change here to allow loading of the dynamic_roi settings
    def min_roi_reached(self, trade: Trade, current_profit: float,
                        current_time: datetime) -> bool:
        trade_dur = int(
            (current_time.timestamp() - trade.open_date_utc.timestamp()) // 60)

        if self.use_dynamic_roi:
            _, roi = self.min_roi_reached_dynamic(trade, current_profit,
                                                  current_time, trade_dur)
        else:
            _, roi = self.min_roi_reached_entry(trade_dur)
        if roi is None:
            return False
        else:
            return current_profit > roi

    # Get the current price from the exchange (or local cache)
    def get_current_price(self, pair: str, refresh: bool) -> float:
        if not refresh:
            rate = self.custom_current_price_cache.get(pair)
            # Check if cache has been invalidated
            if rate:
                return rate

        ask_strategy = self.config.get('ask_strategy', {})
        if ask_strategy.get('use_order_book', False):
            ob = self.dp.orderbook(pair, 1)
            rate = ob[f"{ask_strategy['price_side']}s"][0][0]
        else:
            ticker = self.dp.ticker(pair)
            rate = ticker['last']

        self.custom_current_price_cache[pair] = rate
        return rate

    """
    Stripped down version from Schism, meant only to update the price data a bit
    more frequently than the default instead of getting all sorts of trade information
    """

    def populate_trades(self, pair: str) -> dict:
        # Initialize the trades dict if it doesn't exist, persist it otherwise
        if not pair in self.custom_trade_info:
            self.custom_trade_info[pair] = {}

        # init the temp dicts and set the trade stuff to false
        trade_data = {}
        trade_data['active_trade'] = False

        # active trade stuff only works in live and dry, not backtest
        if self.config['runmode'].value in ('live', 'dry_run'):

            # find out if we have an open trade for this pair
            active_trade = Trade.get_trades([
                Trade.pair == pair,
                Trade.is_open.is_(True),
            ]).all()

            # if so, get some information
            if active_trade:
                # get current price and update the min/max rate
                current_rate = self.get_current_price(pair, True)
                active_trade[0].adjust_min_max_rates(current_rate)

        return trade_data

    # nested hyperopt class
    class HyperOpt:

        # defining as dummy, so that no error is thrown about missing
        # sell indicator space when hyperopting for all spaces
        @staticmethod
        def indicator_space() -> List[Dimension]:
            return []
class SMAOffsetStrategy(IStrategy):
    INTERFACE_VERSION = 2

    # ROI table:
    minimal_roi = {"0": 1}

    # Stoploss:
    stoploss = -0.5

    base_nb_candles_buy = IntParameter(
        5, 80, default=buy_params['base_nb_candles_buy'], space='buy')
    base_nb_candles_sell = IntParameter(
        5, 80, default=sell_params['base_nb_candles_sell'], space='sell')
    low_offset = DecimalParameter(0.9,
                                  0.99,
                                  default=buy_params['low_offset'],
                                  space='buy')
    high_offset = DecimalParameter(0.99,
                                   1.1,
                                   default=sell_params['high_offset'],
                                   space='sell')
    buy_trigger = CategoricalParameter([SMA, EMA],
                                       default=buy_params['buy_trigger'],
                                       space='buy')
    sell_trigger = CategoricalParameter([SMA, EMA],
                                        default=sell_params['sell_trigger'],
                                        space='sell')

    # Trailing stop:
    trailing_stop = False
    trailing_stop_positive = 0.1
    trailing_stop_positive_offset = 0
    trailing_only_offset_is_reached = False

    # Optimal timeframe for the strategy
    timeframe = '5m'

    use_sell_signal = True
    sell_profit_only = False

    process_only_new_candles = True
    startup_candle_count = 30

    plot_config = {
        'main_plot': {
            'ma_offset_buy': {
                'color': 'orange'
            },
            'ma_offset_sell': {
                'color': 'orange'
            },
        },
    }

    use_custom_stoploss = False

    def populate_indicators(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        # uncomment for plotting

        #if self.buy_trigger.value == 'EMA':
        #    dataframe['ma_buy'] = ta.EMA(dataframe, timeperiod=self.base_nb_candles_buy.value)
        #else:
        #    dataframe['ma_buy'] = ta.SMA(dataframe, timeperiod=self.base_nb_candles_buy.value)

        #if self.sell_trigger.value == 'EMA':
        #    dataframe['ma_sell'] = ta.EMA(dataframe, timeperiod=self.base_nb_candles_sell.value)
        #else:
        #    dataframe['ma_sell'] = ta.SMA(dataframe, timeperiod=self.base_nb_candles_sell.value)

        #dataframe['ma_offset_buy'] = dataframe['ma_buy'] * self.low_offset.value
        #dataframe['ma_offset_sell'] = dataframe['ma_sell'] * self.high_offset.value

        return dataframe

    def populate_buy_trend(self, dataframe: DataFrame,
                           metadata: dict) -> DataFrame:
        if self.buy_trigger.value == EMA:
            dataframe['ma_buy'] = ta.EMA(dataframe,
                                         timeperiod=int(
                                             self.base_nb_candles_buy.value))
        else:
            dataframe['ma_buy'] = ta.SMA(dataframe,
                                         timeperiod=int(
                                             self.base_nb_candles_buy.value))

        dataframe[
            'ma_offset_buy'] = dataframe['ma_buy'] * self.low_offset.value

        dataframe.loc[((dataframe['close'] < dataframe['ma_offset_buy']) &
                       (dataframe['volume'] > 0)), 'buy'] = 1
        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        if self.sell_trigger.value == EMA:
            dataframe['ma_sell'] = ta.EMA(dataframe,
                                          timeperiod=int(
                                              self.base_nb_candles_sell.value))
        else:
            dataframe['ma_sell'] = ta.SMA(dataframe,
                                          timeperiod=int(
                                              self.base_nb_candles_sell.value))

        dataframe[
            'ma_offset_sell'] = dataframe['ma_sell'] * self.high_offset.value

        dataframe.loc[((dataframe['close'] > dataframe['ma_offset_sell']) &
                       (dataframe['volume'] > 0)), 'sell'] = 1
        return dataframe
Ejemplo n.º 14
0
class CombinedBinHAndClucV6H(IStrategy):

    minimal_roi = {"0": 0.0181}

    max_open_trades = 5

    # Using custom stoploss
    stoploss = -0.99
    use_custom_stoploss = True

    # Trailing stoploss
    trailing_stop = True
    trailing_only_offset_is_reached = True
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.025

    timeframe = '5m'
    informative_timeframe = '1h'

    # derived from EMA 200
    startup_candle_count: int = 200

    # Run "populate_indicators()" only for new candle.
    process_only_new_candles = False

    use_sell_signal = True
    sell_profit_only = True
    sell_profit_offset = 0.001
    ignore_roi_if_buy_signal = True

    # hyperspace default buy params
    buy_params = {
        'buy_bin_bbdelta_close': 0.031,
        'buy_bin_closedelta_close': 0.018,
        'buy_bin_tail_bbdelta': 0.233,
        'buy_bin_guard': True,
        'buy_cluc_close_bblowerband': 0.993,
        'buy_cluc_volume': 21,
        'buy_cluc_guard': True,
        'buy_long_rsi_diff': 43.276,
        'buy_bin_enable': True,
        'buy_cluc_enable': True,
        'buy_long_enable': True,
        'buy_minimum_conditions': 1
    }

    # hyperspace default sell params
    sell_params = {
        'sell_roi_override_rsi_threshold':
        50,  # to disable holding with high RSI set to 100
        'cstp_loss_threshold': 0,
        'cstp_bail_time': 5
    }

    # if you want to see which buy conditions were met
    # or if there is an trade exit override due to high RSI set to True
    # logger will output the buy and trade exit conditions
    cust_log_verbose = False

    ###########################################################################################################
    ## IMPORTANT NOTICE REGARDING HYPEROPT:                                                                  ##
    ##   Use the optimize dict below to enable optimization (set to True) of one or more                     ##
    ##   hyperspace parameters.                                                                              ##
    ##                                                                                                       ##
    ##   Please be aware: Enabling ALL at once will very likely produce very low results!                    ##
    ##   So please enable max. 1-2 parameters at once, you'll need to play around                            ##
    ##   what makes more or less sense. At the end it always depends on a lot more                           ##
    ##   parameters (stake, max trades, hyperopt-loss, etc.)                                                 ##
    ###########################################################################################################

    # use this switches to enable the optimization of hyperspace params
    cust_optimize = {
        'buy_bin_bbdelta_close': False,
        'buy_bin_closedelta_close': False,
        'buy_bin_tail_bbdelta': False,
        'buy_bin_guard': False,
        'buy_cluc_close_bblowerband': False,
        'buy_cluc_volume': False,
        'buy_cluc_guard': False,
        'buy_long_rsi_diff': False,
        'buy_bin_enable': False,
        'buy_cluc_enable': False,
        'buy_long_enable': False,
        'buy_minimum_conditions': False,
        'sell_roi_override_rsi_threshold': False,
        'cstp_bail_time': False,
        'cstp_loss_threshold': False
    }

    # bin buy parameters
    buy_bin_bbdelta_close = DecimalParameter(
        0.0,
        0.05,
        default=0.031,
        space='buy',
        optimize=cust_optimize['buy_bin_bbdelta_close'],
        load=True)
    buy_bin_closedelta_close = DecimalParameter(
        0.0,
        0.03,
        default=0.018,
        decimals=4,
        space='buy',
        optimize=cust_optimize['buy_bin_closedelta_close'],
        load=True)
    buy_bin_tail_bbdelta = DecimalParameter(
        0.0,
        1.0,
        default=0.233,
        decimals=3,
        space='buy',
        optimize=cust_optimize['buy_bin_tail_bbdelta'],
        load=True)
    buy_bin_guard = CategoricalParameter(
        [True, False],
        default=True,
        space='buy',
        optimize=cust_optimize['buy_bin_guard'],
        load=True)

    # cluc buy parameters
    buy_cluc_close_bblowerband = DecimalParameter(
        0.0,
        1.5,
        default=0.993,
        decimals=3,
        space='buy',
        optimize=cust_optimize['buy_cluc_close_bblowerband'],
        load=True)
    buy_cluc_volume = IntParameter(10,
                                   40,
                                   default=21,
                                   space='buy',
                                   optimize=cust_optimize['buy_cluc_volume'],
                                   load=True)
    buy_cluc_guard = CategoricalParameter(
        [True, False],
        default=True,
        space='buy',
        optimize=cust_optimize['buy_cluc_guard'],
        load=True)

    # log buy parameters
    buy_long_rsi_diff = DecimalParameter(
        40,
        45,
        default=43.276,
        decimals=3,
        space='buy',
        optimize=cust_optimize['buy_long_rsi_diff'],
        load=True)

    # enable bin, cluc or long
    buy_bin_enable = CategoricalParameter(
        [True, False],
        default=True,
        space='buy',
        optimize=cust_optimize['buy_bin_enable'],
        load=True)
    buy_cluc_enable = CategoricalParameter(
        [True, False],
        default=True,
        space='buy',
        optimize=cust_optimize['buy_cluc_enable'],
        load=True)
    buy_long_enable = CategoricalParameter(
        [True, False],
        default=True,
        space='buy',
        optimize=cust_optimize['buy_long_enable'],
        load=True)

    # minimum conditions to match in buy
    buy_minimum_conditions = IntParameter(
        1,
        2,
        default=1,
        space='buy',
        optimize=cust_optimize['buy_minimum_conditions'],
        load=True)

    # if RSI above threshold, override ROI
    sell_roi_override_rsi_threshold = IntParameter(
        40,
        70,
        default=50,
        space='sell',
        optimize=cust_optimize['sell_roi_override_rsi_threshold'],
        load=True)

    # custom stoploss, if trade open > x hours and loss > threshold then sell
    cstp_bail_time = IntParameter(1,
                                  36,
                                  default=5,
                                  space='sell',
                                  optimize=cust_optimize['cstp_bail_time'])
    cstp_loss_threshold = DecimalParameter(
        -0.25,
        0,
        default=0,
        decimals=2,
        space='sell',
        optimize=cust_optimize['cstp_loss_threshold'])
    """
    Informative Pairs
    """
    def informative_pairs(self):

        pairs = self.dp.current_whitelist()
        informative_pairs = [(pair, self.informative_timeframe)
                             for pair in pairs]

        return informative_pairs

    """
    Informative Timeframe Indicators
    """

    def get_informative_indicators(self, dataframe: DataFrame,
                                   metadata: dict) -> DataFrame:

        dataframe = self.dp.get_pair_dataframe(
            pair=metadata['pair'], timeframe=self.informative_timeframe)

        # EMA
        dataframe['ema_50'] = ta.EMA(dataframe, timeperiod=50)
        dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200)

        # RSI
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)

        # SSL Channels
        ssldown, sslup = SSLChannels_ATR(dataframe, 20)
        dataframe['ssl-up'] = sslup
        dataframe['ssl-down'] = ssldown
        dataframe['ssl-dir'] = np.where(sslup > ssldown, 'up', 'down')

        return dataframe

    """
    Main Timeframe Indicators
    """

    def get_main_indicators(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:

        # strategy BinHV45
        bb_40 = qtpylib.bollinger_bands(dataframe['close'], window=40, stds=2)
        dataframe['lower'] = bb_40['lower']
        dataframe['mid'] = bb_40['mid']
        dataframe['bbdelta'] = (bb_40['mid'] - dataframe['lower']).abs()
        dataframe['closedelta'] = (dataframe['close'] -
                                   dataframe['close'].shift()).abs()
        dataframe['tail'] = (dataframe['close'] - dataframe['low']).abs()

        # strategy ClucMay72018
        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe),
                                            window=20,
                                            stds=2)
        dataframe['bb_lowerband'] = bollinger['lower']
        dataframe['bb_middleband'] = bollinger['mid']
        dataframe['bb_upperband'] = bollinger['upper']

        dataframe['volume_mean_slow'] = dataframe['volume'].rolling(
            window=30).mean()

        # EMA
        dataframe['ema_50'] = ta.EMA(dataframe, timeperiod=50)
        dataframe['ema_200'] = ta.EMA(dataframe, timeperiod=200)

        # SMA
        dataframe['sma_5'] = ta.EMA(dataframe, timeperiod=5)

        # RSI
        dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)

        # drop NAN in hyperopt to fix "'<' not supported between instances of 'str' and 'int' error
        if self.config['runmode'].value == 'hyperopt':
            dataframe = dataframe.dropna()

        return dataframe

    """
    Populate Informative and Main Timeframe Indicators
    """

    def populate_indicators(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:

        if self.config['runmode'].value in ('backtest', 'hyperopt'):
            assert (timeframe_to_minutes(self.timeframe) <=
                    5), "Backtest this strategy in a timeframe of 5m or less."

        assert self.dp, "DataProvider is required for multiple timeframes."

        informative = self.get_informative_indicators(dataframe, metadata)
        dataframe = merge_informative_pair(dataframe,
                                           informative,
                                           self.timeframe,
                                           self.informative_timeframe,
                                           ffill=True)
        dataframe = self.get_main_indicators(dataframe, metadata)

        return dataframe

    """
    Buy Signal
    """

    def populate_buy_trend(self, dataframe: DataFrame,
                           metadata: dict) -> DataFrame:

        conditions = []

        # reset additional dataframe rows
        dataframe.loc[:, 'buy_cond_bin'] = False
        dataframe.loc[:, 'buy_cond_cluc'] = False
        dataframe.loc[:, 'buy_cond_long'] = False
        dataframe.loc[:, 'conditions_count'] = 0

        # strategy BinHV45 + guards by @iterativ
        dataframe.loc[(
            # @iterativ adition (guard)
            (((dataframe['close'] > dataframe['ema_200_1h']) &
              (dataframe['ema_50'] > dataframe['ema_200']) &
              (dataframe['ema_50_1h'] > dataframe['ema_200_1h']) &
              (self.buy_bin_guard.value == True)) |
             (self.buy_bin_guard.value == False)) &

            # strategy BinHV45
            dataframe['lower'].shift().gt(0)
            & dataframe['bbdelta'].gt(dataframe['close'] *
                                      self.buy_bin_bbdelta_close.value)
            & dataframe['closedelta'].gt(dataframe['close'] *
                                         self.buy_bin_closedelta_close.value)
            & dataframe['tail'].lt(dataframe['bbdelta'] *
                                   self.buy_bin_tail_bbdelta.value)
            & dataframe['close'].lt(dataframe['lower'].shift())
            & dataframe['close'].le(dataframe['close'].shift()) &
            (self.buy_bin_enable.value == True)), 'buy_cond_bin'] = 1

        # strategy ClucMay72018 + guards by @iterativ
        dataframe.loc[(
            # @iterativ addition (guard)
            (((dataframe['close'] > dataframe['ema_200']) &
              (dataframe['close'] > dataframe['ema_200_1h']) &
              (self.buy_cluc_guard.value == True)) |
             (self.buy_cluc_guard.value == False)) &

            # strategy ClucMay72018
            (dataframe['close'] < dataframe['ema_50']) &
            (dataframe['close'] < self.buy_cluc_close_bblowerband.value *
             dataframe['bb_lowerband']) &
            (dataframe['volume'] < (dataframe['volume_mean_slow'].shift(1) *
                                    self.buy_cluc_volume.value)) &
            (self.buy_cluc_enable.value == True)), 'buy_cond_cluc'] = 1

        # long strategy by @iterativ
        dataframe.loc[((dataframe['close'] < dataframe['sma_5']) &
                       (dataframe['ssl-dir_1h'] == 'up') &
                       (dataframe['ema_50'] > dataframe['ema_200']) &
                       (dataframe['ema_50_1h'] > dataframe['ema_200_1h']) &
                       (dataframe['rsi'] < dataframe['rsi_1h'] -
                        self.buy_long_rsi_diff.value) &
                       (self.buy_long_enable.value == True)),
                      'buy_cond_long'] = 1

        # count the amount of conditions met
        dataframe.loc[:,
                      'conditions_count'] = dataframe['buy_cond_bin'].astype(
                          int) + dataframe['buy_cond_cluc'].astype(
                              int) + dataframe['buy_cond_long'].astype(int)

        # append the minimum amount of conditions to be met
        conditions.append(
            dataframe['conditions_count'] >= self.buy_minimum_conditions.value)
        conditions.append(dataframe['volume'].gt(0))

        if conditions:
            dataframe.loc[reduce(lambda x, y: x & y, conditions), 'buy'] = 1

        # verbose logging enable only for verbose information or troubleshooting
        if self.cust_log_verbose == True:
            for index, row in dataframe.iterrows():
                if row['buy'] == 1:
                    buy_cond_details = f"count={int(row['conditions_count'])}/bin={int(row['buy_cond_bin'])}/cluc={int(row['buy_cond_cluc'])}/long={int(row['buy_cond_long'])}"
                    logger.info(
                        f"{metadata['pair']} - candle: {row['date']} - buy condition - details: {buy_cond_details}"
                    )

        return dataframe

    """
    Sell Signal
    """

    def populate_sell_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:

        conditions = []

        conditions.append(
            (dataframe['close'] > dataframe['bb_upperband']) &
            (dataframe['close'].shift(1) > dataframe['bb_upperband'].shift(1))
            & (dataframe['volume'] > 0)  # Make sure Volume is not 0
        )

        if conditions:
            dataframe.loc[reduce(lambda x, y: x & y, conditions), 'sell'] = 1

        return dataframe

    """
    Custom Stop Loss
    """

    def custom_stoploss(self, pair: str, trade: 'Trade',
                        current_time: datetime, current_rate: float,
                        current_profit: float, **kwargs) -> float:

        if (current_profit < self.cstp_loss_threshold.value) & (
                current_time - timedelta(hours=int(self.cstp_bail_time.value))
                > trade.open_date_utc):
            return 0.01

        return self.stoploss

    """
    Trade Exit Confirmation
    """

    def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str,
                           amount: float, rate: float, time_in_force: str,
                           sell_reason: str, **kwargs) -> bool:
        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()

        if self.cust_log_verbose == True:
            logger.info(
                f"{pair} - candle: {last_candle['date']} - exit trade {sell_reason} with profit {trade.calc_profit_ratio(rate)}"
            )

        # failsafe for user triggered forced sells > always have highest prio!
        if sell_reason == 'force_sell':
            return True

        # Prevent ROI trigger, if there is more potential, in order to maximize profit
        if last_candle is not None and ((sell_reason == 'roi')):
            rsi = 0
            if 'rsi' in last_candle.index:
                rsi = last_candle['rsi']

            if ((rsi >= self.sell_roi_override_rsi_threshold.value)):
                if self.cust_log_verbose == True:
                    logger.info(
                        f"{pair} - candle: {last_candle['date']} - not exiting trade with current profit {trade.calc_profit_ratio(rate)}, rsi = {rsi} which is > than {self.sell_roi_override_rsi_threshold.value}"
                    )
                return False
            else:
                return True

        return True