Example #1
0
    def fn(val: int):
        perc = 0.5

        low = math.floor(val * (1 - perc))
        high = math.floor(val * (1 + perc))

        return IntParameter(default=val,
                            low=low,
                            high=high,
                            space=space,
                            optimize=True,
                            load=True)
Example #2
0
class Ichess(IStrategy):

    # ROI table:
    minimal_roi = {"0": 0.642, "8834": 0.37, "25392": 0.118, "55146": 0}

    # Stoploss:
    stoploss = -0.314

    # Opt Timeframe
    timeframe = '1d'

    buy_fast_timeperiod = IntParameter(2, 50, default=9, space="buy")
    buy_slow_timeperiod = IntParameter(2, 50, default=10, space="buy")

    sell_fast_timeperiod = IntParameter(2, 50, default=15, space="sell")
    sell_slow_timeperiod = IntParameter(2, 50, default=16, space="sell")

    def populate_indicators(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        conversion_line_period = 9
        base_line_periods = 26
        laggin_span = 52
        displacement = 26

        df = dataframe.copy()

        # Heikin Ashi Strategy
        heikinashi = qtpylib.heikinashi(df)
        df['ha_open'] = heikinashi['open']
        df['ha_close'] = heikinashi['close']
        df['ha_high'] = heikinashi['high']
        df['ha_low'] = heikinashi['low']

        df['tenkan'] = (
            df['ha_high'].rolling(window=conversion_line_period).max() +
            df['ha_low'].rolling(window=conversion_line_period).min()) / 2
        df['kijun'] = (
            df['ha_high'].rolling(window=base_line_periods).max() +
            df['ha_low'].rolling(window=base_line_periods).min()) / 2
        df['senkou_leading_a'] = (df['tenkan'] + df['kijun']) / 2
        df['senkou_leading_b'] = (
            df['ha_high'].rolling(window=laggin_span).max() +
            df['ha_low'].rolling(window=laggin_span).min()) / 2
        df['senkou_span_a'] = df['senkou_leading_a'].shift(displacement)
        df['senkou_span_b'] = df['senkou_leading_b'].shift(displacement)
        df['chikou_span'] = df['ha_close'].shift(displacement)

        df['tenkan1'] = df['tenkan'].shift(+1)
        df['kijun1'] = df['kijun'].shift(+1)
        df['senkou_leading_a1'] = df['senkou_leading_a'].shift(+1)
        df['senkou_leading_b1'] = df['senkou_leading_b'].shift(+1)
        df['senkou_span_a1'] = df['senkou_span_a'].shift(+1)
        df['senkou_span_b1'] = df['senkou_span_b'].shift(+1)
        df['chikou_span1'] = df['chikou_span'].shift(+1)

        df['ha_close1'] = df['ha_close'].shift(+1)

        # // == Price and Kijun Sen (standard line) Cross ==

        def calcTkCross(
            tenkan,
            kijun,
            tenkan1,
            kijun1,
            senkou_span_a,
            senkou_span_b,
        ):
            if (tenkan > kijun) and (tenkan1 <= kijun1):
                intersect = (tenkan1 * (kijun - kijun1) - kijun1 *
                             (tenkan - tenkan1)) / ((kijun - kijun1) -
                                                    (tenkan - tenkan1))
                if (intersect > senkou_span_a) and (intersect > senkou_span_b):
                    return 2
                elif (intersect < senkou_span_a) and (intersect <
                                                      senkou_span_b):
                    return 0.5
                else:
                    return 1
            elif (tenkan < kijun) and (tenkan1 >= kijun1):
                intersect = (tenkan1 * (kijun - kijun1) - kijun1 *
                             (tenkan - tenkan1)) / ((kijun - kijun1) -
                                                    (tenkan - tenkan1))
                if (intersect > senkou_span_a) and (intersect > senkou_span_b):
                    return -0.5
                elif (intersect < senkou_span_a) and (intersect <
                                                      senkou_span_b):
                    return -2
                else:
                    return -1
            else:
                return 0

        # df['tkCrossScore'] = np.vectorize(calcTkCross)()
        df['tkCrossScore'] = np.vectorize(calcTkCross)(
            df['tenkan'],
            df['kijun'],
            df['tenkan1'],
            df['kijun1'],
            df['senkou_span_a'],
            df['senkou_span_b'],
        )

        # // == Price and Kijun Sen (standard line) Cross ==

        def calcPkCross(
            ha_close,
            kijun,
            ha_close1,
            kijun1,
            senkou_span_a,
            senkou_span_b,
        ):
            if (ha_close > kijun) and (ha_close1 <= kijun1):
                intersect = (ha_close1 * (kijun - kijun1) - kijun1 *
                             (ha_close - ha_close1)) / ((kijun - kijun1) -
                                                        (ha_close - ha_close1))
                if (intersect > senkou_span_a) and (intersect > senkou_span_b):
                    return 2
                elif (intersect < senkou_span_a) and (intersect <
                                                      senkou_span_b):
                    return 0.5
                else:
                    return 1
            elif (ha_close < kijun) and (ha_close1 >= kijun1):
                intersect = (ha_close1 * (kijun - kijun1) - kijun1 *
                             (ha_close - ha_close1)) / ((kijun - kijun1) -
                                                        (ha_close - ha_close1))
                if (intersect > senkou_span_a) and (intersect > senkou_span_b):
                    return -0.5
                elif (intersect < senkou_span_a) and (intersect <
                                                      senkou_span_b):
                    return -2
                else:
                    return -1
            else:
                return 0

        df['pkCrossScore'] = np.vectorize(calcPkCross)(
            df['ha_close'],
            df['kijun'],
            df['ha_close1'],
            df['kijun1'],
            df['senkou_span_a'],
            df['senkou_span_b'],
        )

        # // == Kumo Breakouts ==
        def calcKumoBreakout(
            ha_close,
            senkou_span_a,
            ha_close1,
            senkou_span_a1,
            senkou_span_b,
            senkou_span_b1,
        ):
            if (((ha_close > senkou_span_a) and
                 (ha_close1 <= senkou_span_a1) and
                 (senkou_span_a > senkou_span_b))
                    or ((ha_close > senkou_span_b) and
                        (ha_close1 <= senkou_span_b1) and
                        (senkou_span_a < senkou_span_b))):
                return 2
            elif (((ha_close < senkou_span_a) and
                   (ha_close1 >= senkou_span_a1) and
                   (senkou_span_a < senkou_span_b))
                  or ((ha_close < senkou_span_b) and
                      (ha_close1 >= senkou_span_b1) and
                      (senkou_span_a > senkou_span_b))):
                return -2
            else:
                return 0

        df['kumoBreakoutScore'] = np.vectorize(calcKumoBreakout)(
            df['ha_close'],
            df['senkou_span_a'],
            df['ha_close1'],
            df['senkou_span_a1'],
            df['senkou_span_b'],
            df['senkou_span_b1'],
        )

        # // == Senkou Span Cross ==
        def calcSenkouCross(
            senkou_leading_a,
            senkou_leading_b,
            senkou_leading_a1,
            senkou_leading_b1,
            ha_close,
            senkou_span_a,
            senkou_span_b,
        ):
            if (senkou_leading_a > senkou_leading_b) and (senkou_leading_a1 <=
                                                          senkou_leading_b1):
                if (ha_close > senkou_span_a) and (ha_close > senkou_span_b):
                    return 2
                elif (ha_close < senkou_span_a) and (ha_close < senkou_span_b):
                    return 0.5
                else:
                    return 1
            elif (senkou_leading_a < senkou_leading_b) and (
                    senkou_leading_a1 >= senkou_leading_b1):
                if (ha_close > senkou_span_a) and (ha_close > senkou_span_b):
                    return -0.5
                elif (ha_close < senkou_span_a) and (ha_close < senkou_span_b):
                    return -2
                else:
                    return -1
            else:
                return 0

        df['senkouCrossScore'] = np.vectorize(calcSenkouCross)(
            df['senkou_leading_a'],
            df['senkou_leading_b'],
            df['senkou_leading_a1'],
            df['senkou_leading_b1'],
            df['ha_close'],
            df['senkou_span_a'],
            df['senkou_span_b'],
        )

        # // == Chikou Span Cross ==

        def calcChikouCross(
            ha_close,
            chikou_span,
            ha_close1,
            chikou_span1,
            senkou_span_a,
            senkou_span_b,
        ):
            if (ha_close > chikou_span) and (ha_close1 <= chikou_span1):
                intersect = (ha_close1 *
                             (chikou_span - chikou_span1) - chikou_span1 *
                             (ha_close - ha_close1)) / (
                                 (chikou_span - chikou_span1) -
                                 (ha_close - ha_close1))
                if (intersect > senkou_span_a) and (intersect > senkou_span_b):
                    return 2
                elif (intersect < senkou_span_a) and (intersect <
                                                      senkou_span_b):
                    return 0.5
                else:
                    return 1
            elif (ha_close < chikou_span) and (ha_close1 >= chikou_span1):
                intersect = (ha_close1 *
                             (chikou_span - chikou_span1) - chikou_span1 *
                             (ha_close - ha_close1)) / (
                                 (chikou_span - chikou_span1) -
                                 (ha_close - ha_close1))
                if (intersect > senkou_span_a) and (intersect > senkou_span_b):
                    return -0.5
                elif (intersect < senkou_span_a) and (intersect <
                                                      senkou_span_b):
                    return -2
                else:
                    return -1
            else:
                return 0

        df['chikouCrossScore'] = np.vectorize(calcChikouCross)(
            df['ha_close'],
            df['chikou_span'],
            df['ha_close1'],
            df['chikou_span1'],
            df['senkou_span_a'],
            df['senkou_span_b'],
        )

        # // == price relative to cloud ==
        def calcPricePlacement(
            ha_close,
            senkou_span_a,
            senkou_span_b,
        ):
            if (ha_close > senkou_span_a) and (ha_close > senkou_span_b):
                return 2
            elif (ha_close < senkou_span_a) and (ha_close < senkou_span_b):
                return -2
            else:
                return 0

        df['pricePlacementScore'] = np.vectorize(calcPricePlacement)(
            df['ha_close'],
            df['senkou_span_a'],
            df['senkou_span_b'],
        )

        # // == lag line releative to cloud ==
        def calcChikouPlacement(
            ha_close,
            senkou_leading_a,
            senkou_leading_b,
        ):
            if (ha_close > senkou_leading_a) and (ha_close > senkou_leading_b):
                return 2
            elif (ha_close < senkou_leading_a) and (ha_close <
                                                    senkou_leading_b):
                return -2
            else:
                return 0

        df['chikouPlacementScore'] = np.vectorize(calcChikouPlacement)(
            df['ha_close'],
            df['senkou_leading_a'],
            df['senkou_leading_b'],
        )

        # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # //
        dataframe['Ichimoku_Score'] = (
            df['tkCrossScore'] + df['pkCrossScore'] + df['kumoBreakoutScore'] +
            df['senkouCrossScore'] +
            df['chikouCrossScore']).rolling(1).sum().cumsum()

        print(metadata['pair'], dataframe['Ichimoku_Score'].median())

        return dataframe

    def populate_buy_trend(self, dataframe: DataFrame,
                           metadata: dict) -> DataFrame:
        # exit()
        dataframe.loc[
            # (dataframe['Ichimoku_Score']>0)&
            qtpylib.crossed_above(
                ta.SMA(dataframe['Ichimoku_Score'], self.buy_fast_timeperiod.
                       value),
                ta.SMA(dataframe['Ichimoku_Score'], self.buy_slow_timeperiod.
                       value)), 'buy'] = 1
        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        dataframe.loc[
            # (dataframe['Ichimoku_Score']<0)|
            (qtpylib.crossed_below(
                ta.SMA(dataframe['Ichimoku_Score'], self.sell_fast_timeperiod.
                       value),
                ta.SMA(dataframe['Ichimoku_Score'], self.sell_slow_timeperiod.
                       value))), 'sell'] = 1

        return dataframe
class MIKUZEMA_v1(IStrategy):

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

    # generate signals from the 1h timeframe
    informative_timeframe = '1h'

    # WARNING: ichimoku is a long indicator, if you remove or use a 
    # shorter startup_candle_count your backtest results will be unreliable
    startup_candle_count = 500

    # NOTE: this strat only uses candle information, so processing between
    # new candles is a waste of resources as nothing will change
    process_only_new_candles = True

    # ROI table:
    minimal_roi = {
        "0": 0.078,
        "40": 0.062,
        "99": 0.039,
        "218": 0
    }

    stoploss = -0.294

    # Buy hyperspace params:
    buy_params = {
     'low_offset': 0.964, 'zema_len_buy': 51
    }

    # Sell hyperspace params:
    sell_params = {
     'high_offset': 1.004, 'zema_len_sell': 72
    }

    low_offset = DecimalParameter(0.80, 1.20, default=1.004, space='buy', optimize=True)
    high_offset = DecimalParameter(0.80, 1.20, default=0.964, space='sell', optimize=True)
    zema_len_buy = IntParameter(30, 90, default=72, space='buy', optimize=True)
    zema_len_sell = IntParameter(30, 90, default=51, space='sell', optimize=True)

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

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

        displacement = 30
        ichimoku = ftt.ichimoku(dataframe, 
            conversion_line_period=20, 
            base_line_periods=60,
            laggin_span=120, 
            displacement=displacement
            )

        dataframe['chikou_span'] = ichimoku['chikou_span']

        # cross indicators
        dataframe['tenkan_sen'] = ichimoku['tenkan_sen']
        dataframe['kijun_sen'] = ichimoku['kijun_sen']

        # cloud, green a > b, red a < b
        dataframe['senkou_a'] = ichimoku['senkou_span_a']
        dataframe['senkou_b'] = ichimoku['senkou_span_b']
        dataframe['leading_senkou_span_a'] = ichimoku['leading_senkou_span_a']
        dataframe['leading_senkou_span_b'] = ichimoku['leading_senkou_span_b']
        dataframe['cloud_green'] = ichimoku['cloud_green'] * 1
        dataframe['cloud_red'] = ichimoku['cloud_red'] * -1

        dataframe.loc[:, 'cloud_top'] = dataframe.loc[:, ['senkou_a', 'senkou_b']].max(axis=1)
        dataframe.loc[:, 'cloud_bottom'] = dataframe.loc[:, ['senkou_a', 'senkou_b']].min(axis=1)

        # DANGER ZONE START

        # NOTE: Not actually the future, present data that is normally shifted forward for display as the cloud
        dataframe['future_green'] = (dataframe['leading_senkou_span_a'] > dataframe['leading_senkou_span_b']).astype('int') * 2
        dataframe['future_red'] = (dataframe['leading_senkou_span_a'] < dataframe['leading_senkou_span_b']).astype('int') * 2

        # The chikou_span is shifted into the past, so we need to be careful not to read the
        # current value.  But if we shift it forward again by displacement it should be safe to use.
        # We're effectively "looking back" at where it normally appears on the chart.
        dataframe['chikou_high'] = (
                (dataframe['chikou_span'] > dataframe['cloud_top'])
            ).shift(displacement).fillna(0).astype('int')

        dataframe['chikou_low'] = (
                (dataframe['chikou_span'] < dataframe['cloud_bottom'])
            ).shift(displacement).fillna(0).astype('int')

        # DANGER ZONE END

        dataframe['atr'] = ta.ATR(dataframe, timeperiod=14)
        ssl_down, ssl_up = ssl_atr(dataframe, 10)
        dataframe['ssl_down'] = ssl_down
        dataframe['ssl_up'] = ssl_up
        dataframe['ssl_ok'] = (
                (ssl_up > ssl_down) 
            ).astype('int') * 3
        dataframe['ssl_bear'] = (
                (ssl_up < ssl_down) 
            ).astype('int') * 3

        dataframe['ichimoku_ok'] = (
                (dataframe['tenkan_sen'] > dataframe['kijun_sen'])
                & (dataframe['close'] > dataframe['cloud_top'])
                & (dataframe['future_green'] > 0) 
                & (dataframe['chikou_high'] > 0) 
            ).astype('int') * 4

        dataframe['ichimoku_bear'] = (
                (dataframe['tenkan_sen'] < dataframe['kijun_sen'])
                & (dataframe['close'] < dataframe['cloud_bottom'])
                & (dataframe['future_red'] > 0) 
                & (dataframe['chikou_low'] > 0) 
            ).astype('int') * 4

        dataframe['ichimoku_valid'] = (
                (dataframe['leading_senkou_span_b'] == dataframe['leading_senkou_span_b']) # not NaN
            ).astype('int') * 1

        dataframe['trend_pulse'] = (
                (dataframe['ichimoku_ok'] > 0) 
                & (dataframe['ssl_ok'] > 0)
            ).astype('int') * 2

        dataframe['bear_trend_pulse'] = (
                (dataframe['ichimoku_bear'] > 0) 
                & (dataframe['ssl_bear'] > 0)
            ).astype('int') * 2


        dataframe['trend_over'] = (
                (dataframe['ssl_ok'] == 0)
                | (dataframe['close'] < dataframe['cloud_top'])
            ).astype('int') * 1

        dataframe['bear_trend_over'] = (
                (dataframe['ssl_bear'] == 0)
                | (dataframe['close'] > dataframe['cloud_bottom'])
            ).astype('int') * 1

        dataframe.loc[ (dataframe['trend_pulse'] > 0), 'trending'] = 3
        dataframe.loc[ (dataframe['trend_over'] > 0) , 'trending'] = 0
        dataframe['trending'].fillna(method='ffill', inplace=True)

        dataframe.loc[ (dataframe['bear_trend_pulse'] > 0), 'bear_trending'] = 3
        dataframe.loc[ (dataframe['bear_trend_over'] > 0) , 'bear_trending'] = 0
        dataframe['bear_trending'].fillna(method='ffill', inplace=True)

        return dataframe

    def fast_tf_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        if self.config['runmode'].value == 'hyperopt':
            for len in range(30, 91):
                dataframe[f'zema_{len}'] = ftt.zema(dataframe, period=len)
        else:
            dataframe[f'zema_{self.zema_len_buy.value}'] = ftt.zema(dataframe, period=self.zema_len_buy.value)
            dataframe[f'zema_{self.zema_len_sell.value}'] = ftt.zema(dataframe, period=self.zema_len_sell.value)
            dataframe[f'zema_buy'] = ftt.zema(dataframe, period=self.zema_len_buy.value) * self.low_offset.value
            dataframe[f'zema_sell'] = ftt.zema(dataframe, period=self.zema_len_sell.value) * self.high_offset.value


        return dataframe

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

        assert (timeframe_to_minutes(self.timeframe) == 5), "Run this strategy at 5m."

        if self.timeframe == self.informative_timeframe:
            dataframe = self.slow_tf_indicators(dataframe, metadata)
        else:
            assert self.dp, "DataProvider is required for multiple timeframes."

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

            dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.informative_timeframe, ffill=True)
            # don't overwrite the base dataframe's OHLCV information
            skip_columns = [(s + "_" + self.informative_timeframe) for s in ['date', 'open', 'high', 'low', 'close', 'volume']]
            dataframe.rename(columns=lambda s: s.replace("_{}".format(self.informative_timeframe), "") if (not s in skip_columns) else s, inplace=True)

        dataframe = self.fast_tf_indicators(dataframe, metadata)

        return dataframe

    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        zema = f'zema_{self.zema_len_buy.value}'

        dataframe.loc[
            (dataframe['ichimoku_valid'] > 0)
            & (dataframe['bear_trending'] == 0)
            & (dataframe['close'] < (dataframe[zema] * self.low_offset.value))
        , 'buy'] = 1
        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        zema = f'zema_{self.zema_len_sell.value}'

        dataframe.loc[
            (
                (dataframe['close'] > (dataframe[zema] * self.high_offset.value))
            )
        , 'sell'] = 1

        return dataframe

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

        if sell_reason in ('roi',):
            dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
            current_candle = dataframe.iloc[-1]
            if current_candle is not None:
                current_candle = current_candle.squeeze()
                # don't sell during ichimoku uptrend
                if current_candle['trending'] > 0:
                    return False

        return True

    plot_config = {
        # Main plot indicators (Moving averages, ...)
        'main_plot': {
            'senkou_a': {
                'color': 'green',
                'fill_to': 'senkou_b',
                'fill_label': 'Ichimoku Cloud',
                'fill_color': 'rgba(0,0,0,0.2)',
            },
            # plot senkou_b, too. Not only the area to it.
            'senkou_b': {
                'color': 'red',
            },
            'tenkan_sen': { 'color': 'blue' },
            'kijun_sen': { 'color': 'orange' },

            # 'chikou_span': { 'color': 'lightgreen' },

            'ssl_up': { 'color': 'green' },
            # 'ssl_down': { 'color': 'red' },

            # 'ema50': { 'color': 'violet' },
            # 'ema200': { 'color': 'magenta' },

            'zema_buy': { 'color': 'blue' },
            'zema_sell': { 'color': 'orange' },
        },
        'subplots': {
            "Trend": {
                'trending': {'color': 'green'},
                'bear_trending': {'color': 'red'},
            },
            "Bull": {
                'trend_pulse': {'color': 'blue'},
                'trending': {'color': 'orange'},
                'trend_over': {'color': 'red'},
            },
            "Bull Signals": {
                'ichimoku_ok': {'color': 'green'},
                'ssl_ok': {'color': 'red'},
            },
            "Bear": {
                'bear_trend_pulse': {'color': 'blue'},
                'bear_trending': {'color': 'orange'},
                'bear_trend_over': {'color': 'red'},
            },
            "Bear Signals": {
                'ichimoku_bear': {'color': 'green'},
                'ssl_bear': {'color': 'red'},
            },
            "Misc": {
                'ichimoku_valid': {'color': 'green'},
            },
        }
    }
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
Example #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
Example #6
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
class HyperoptableStrategy(StrategyTestV2):
    """
    Default Strategy provided by freqtrade bot.
    Please do not modify this strategy, it's  intended for internal use only.
    Please look at the SampleStrategy in the user_data/strategy directory
    or strategy repository https://github.com/freqtrade/freqtrade-strategies
    for samples and inspiration.
    """

    buy_params = {
        'buy_rsi': 35,
        # Intentionally not specified, so "default" is tested
        # 'buy_plusdi': 0.4
    }

    sell_params = {
        'sell_rsi': 74,
        'sell_minusdi': 0.4
    }

    buy_rsi = IntParameter([0, 50], default=30, space='buy')
    buy_plusdi = RealParameter(low=0, high=1, default=0.5, space='buy')
    sell_rsi = IntParameter(low=50, high=100, default=70, space='sell')
    sell_minusdi = DecimalParameter(low=0, high=1, default=0.5001, decimals=3, space='sell',
                                    load=False)
    protection_enabled = BooleanParameter(default=True)
    protection_cooldown_lookback = IntParameter([0, 50], default=30)

    @property
    def protections(self):
        prot = []
        if self.protection_enabled.value:
            prot.append({
                "method": "CooldownPeriod",
                "stop_duration_candles": self.protection_cooldown_lookback.value
            })
        return prot

    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_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        """
        Based on TA indicators, populates the buy signal for the given dataframe
        :param dataframe: DataFrame
        :param metadata: Additional information, like the currently traded pair
        :return: DataFrame with buy column
        """
        dataframe.loc[
            (
                (dataframe['rsi'] < self.buy_rsi.value) &
                (dataframe['fastd'] < 35) &
                (dataframe['adx'] > 30) &
                (dataframe['plus_di'] > self.buy_plusdi.value)
            ) |
            (
                (dataframe['adx'] > 65) &
                (dataframe['plus_di'] > self.buy_plusdi.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
        :param metadata: Additional information, like the currently traded pair
        :return: DataFrame with buy column
        """
        dataframe.loc[
            (
                (
                    (qtpylib.crossed_above(dataframe['rsi'], self.sell_rsi.value)) |
                    (qtpylib.crossed_above(dataframe['fastd'], 70))
                ) &
                (dataframe['adx'] > 10) &
                (dataframe['minus_di'] > 0)
            ) |
            (
                (dataframe['adx'] > 70) &
                (dataframe['minus_di'] > self.sell_minusdi.value)
            ),
            'sell'] = 1
        return dataframe
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
Example #9
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 BigZ04_TSL3(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 = 200

    # 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.7, 1.1, default=0.989, space='buy', optimize=False, load=True)
    buy_bb20_close_bblowerband_safe_2 = DecimalParameter(0.7, 1.1, default=0.982, 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_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=False, 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_1 = DecimalParameter(10.0, 40.0, default=28.0, space='buy', decimals=1, optimize=False, 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)

    # 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

        if (current_profit > PF_1):
            return stoploss_from_open(sl_profit, current_profit)
        else:
            return stoploss_from_open(HSL, current_profit)
        return stoploss_from_open(HSL, 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.EMA(informative_1h, timeperiod=50)
        informative_1h['ema_200'] = ta.EMA(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.EMA(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'] < 30) &
                (dataframe['close'] * 1.024 < dataframe['open'].shift(3)) &
                (dataframe['rsi_1h'] < 71) &

                (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'] < 69) &
                (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
        """
        
        conditions = []
        conditions.append(
            (
                (dataframe['close'] > (dataframe[f'ma_sell_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) &
                (dataframe['volume'] > 0)
            )
        )

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

        return dataframe
Example #11
0
class FrostAuraM2Strategy(IStrategy):
    """
    This is FrostAura's mark 2 strategy which aims to make purchase decisions
    based on the Stochastic and RSI.
    
    Last Optimization:
        Profit %        : 2.32%
        Optimized for   : Last 45 days, 1h
        Avg             : 5h 23m
    """
    # 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.52, "120": 0.167, "723": 0.044, "1967": 0}

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

    # 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 = 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']

        return dataframe

    buy_rsi = IntParameter([15, 80], default=80, space='buy')
    buy_slowd = IntParameter([15, 45], default=42, space='buy')
    buy_slowk = IntParameter([15, 45], default=30, 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) &
                       (dataframe["slowd"] > self.buy_slowd.value) &
                       (dataframe["slowk"] > self.buy_slowk.value) &
                       (dataframe["close"] > minimum_coin_price)), 'buy'] = 1

        return dataframe

    sell_rsi = IntParameter([20, 80], default=76, space='sell')
    sell_slowd = IntParameter([45, 80], default=48, space='sell')
    sell_slowk = IntParameter([45, 80], default=58, space='sell')

    def populate_sell_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        dataframe.loc[((dataframe['rsi'] < self.sell_rsi.value) &
                       (dataframe["slowd"] < self.sell_slowd.value) &
                       (dataframe["slowk"] < self.sell_slowk.value)),
                      '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 []
Example #13
0
class NostalgiaForInfinityV2(IStrategy):
    INTERFACE_VERSION = 2

    minimal_roi = {"0": 10}

    stoploss = -1.0

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

    custom_info = {}

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

    # Trailing stoploss
    trailing_stop = False
    trailing_only_offset_is_reached = True
    trailing_stop_positive = 0.02
    trailing_stop_positive_offset = 0.04
    # Custom stoploss
    use_custom_stoploss = False

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

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

    # Buy  params
    buy_params = {
        "buy_bb40_bbdelta_close": 0.017,
        "buy_bb40_closedelta_close": 0.013,
        "buy_bb40_tail_bbdelta": 0.445,
        "buy_bb20_close_bblowerband": 0.992,
        "buy_bb20_volume": 27,
        "buy_rsi_diff": 52.438,
    }

    # Sell params
    sell_params = {
        "sell_rsi_bb": 79.706,
        "sell_rsi_main": 85.023,
        "sell_rsi_2": 87.545,
        "sell_rsi_diff": 0.873,
        "sell_ema_relative": 0.03,
    }

    buy_bb40_bbdelta_close = DecimalParameter(0.005,
                                              0.05,
                                              default=0.049,
                                              space='buy',
                                              optimize=True,
                                              load=True)
    buy_bb40_closedelta_close = DecimalParameter(0.01,
                                                 0.03,
                                                 default=0.023,
                                                 space='buy',
                                                 optimize=True,
                                                 load=True)
    buy_bb40_tail_bbdelta = DecimalParameter(0.15,
                                             0.45,
                                             default=0.287,
                                             space='buy',
                                             optimize=True,
                                             load=True)
    buy_bb20_close_bblowerband = DecimalParameter(0.8,
                                                  1.1,
                                                  default=0.991,
                                                  space='buy',
                                                  optimize=True,
                                                  load=True)
    buy_bb20_volume = IntParameter(18,
                                   34,
                                   default=20,
                                   space='buy',
                                   optimize=True,
                                   load=True)
    buy_rsi_diff = DecimalParameter(36.0,
                                    54.0,
                                    default=37.0,
                                    space='buy',
                                    optimize=True,
                                    load=True)

    sell_rsi_bb = DecimalParameter(60.0,
                                   80.0,
                                   default=70,
                                   space='sell',
                                   optimize=True,
                                   load=True)
    sell_rsi_main = DecimalParameter(72.0,
                                     90.0,
                                     default=78.0,
                                     space='sell',
                                     optimize=True,
                                     load=True)
    sell_rsi_2 = DecimalParameter(72.0,
                                  90.0,
                                  default=60.0,
                                  space='sell',
                                  optimize=True,
                                  load=True)
    sell_ema_relative = DecimalParameter(0.005,
                                         0.1,
                                         default=0.03,
                                         space='sell',
                                         optimize=False,
                                         load=True)
    sell_rsi_diff = DecimalParameter(0.0,
                                     5.0,
                                     default=5.0,
                                     space='sell',
                                     optimize=True,
                                     load=True)

    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_20'] = ta.EMA(informative_1h, timeperiod=20)
        informative_1h['ema_50'] = ta.EMA(informative_1h, timeperiod=50)
        informative_1h['ema_200'] = ta.EMA(informative_1h, timeperiod=200)
        # RSI
        informative_1h['rsi'] = ta.RSI(informative_1h, timeperiod=14)
        # SSL Channels
        ssl_down_1h, ssl_up_1h = SSLChannels(informative_1h, 20)
        informative_1h['ssl_down'] = ssl_down_1h
        informative_1h['ssl_up'] = ssl_up_1h

        return informative_1h

    def normal_tf_indicators(self, dataframe: DataFrame,
                             metadata: dict) -> DataFrame:
        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()

        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['ema_slow'] = ta.EMA(dataframe, timeperiod=50)
        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.SMA(dataframe, timeperiod=5)
        dataframe['sma_9'] = ta.SMA(dataframe, timeperiod=9)

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

        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:
        dataframe.loc[
            ((dataframe['close'] < dataframe['sma_9']) &
             (dataframe['close'] > dataframe['ema_200_1h']) &
             (dataframe['ema_50'] > dataframe['ema_200']) &
             (dataframe['ema_50_1h'] > dataframe['ema_200_1h'])
             & dataframe['lower'].shift().gt(0)
             & dataframe['bbdelta'].gt(dataframe['close'] *
                                       self.buy_bb40_bbdelta_close.value)
             & dataframe['closedelta'].gt(dataframe['close'] *
                                          self.buy_bb40_closedelta_close.value)
             & dataframe['tail'].lt(dataframe['bbdelta'] *
                                    self.buy_bb40_tail_bbdelta.value)
             & dataframe['close'].lt(dataframe['lower'].shift())
             & dataframe['close'].le(dataframe['close'].shift()) &
             (dataframe['volume'] > 0))
            | ((dataframe['close'] < dataframe['sma_9']) &
               (dataframe['close'] > dataframe['ema_200']) &
               (dataframe['close'] > dataframe['ema_200_1h']) &
               (dataframe['close'] < dataframe['ema_slow']) &
               (dataframe['close'] < self.buy_bb20_close_bblowerband.value *
                dataframe['bb_lowerband']) &
               (dataframe['volume'] < (dataframe['volume_mean_slow'].shift(1) *
                                       self.buy_bb20_volume.value)))
            |
            ((dataframe['close'] < dataframe['sma_5']) &
             (dataframe['close'] > dataframe['ema_200']) &
             (dataframe['close'] > dataframe['ema_200_1h']) &
             (dataframe['ssl_up_1h'] > dataframe['ssl_down_1h']) &
             (dataframe['ema_50'] > dataframe['ema_200']) &
             (dataframe['ema_50_1h'] > dataframe['ema_200_1h']) &
             (dataframe['rsi'] < dataframe['rsi_1h'] - self.buy_rsi_diff.value)
             & (dataframe['volume'] > 0)), 'buy'] = 1
        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        dataframe.loc[(
            (dataframe['rsi'] > self.sell_rsi_bb.value) &
            (dataframe['close'] > dataframe['bb_upperband']) &
            (dataframe['close'].shift(1) > dataframe['bb_upperband'].shift(1))
            &
            (dataframe['close'].shift(2) > dataframe['bb_upperband'].shift(2))
            & (dataframe['volume'] > 0))
                      | ((dataframe['rsi'] > self.sell_rsi_main.value) &
                         (dataframe['volume'] > 0))
                      | ((dataframe['close'] < dataframe['ema_200']) &
                         (dataframe['close'] > dataframe['ema_50']) &
                         (dataframe['rsi'] > self.sell_rsi_2.value) &
                         (dataframe['volume'] > 0))
                      |
                      ((dataframe['close'] < dataframe['ema_200']) &
                       (((dataframe['ema_200'] - dataframe['close']) /
                         dataframe['close']) < self.sell_ema_relative.value) &
                       (dataframe['rsi'] > dataframe['rsi_1h'] +
                        self.sell_rsi_diff.value) & (dataframe['volume'] > 0)),
                      'sell'] = 1
        return dataframe
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
Example #15
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
class SwingHighToSky(IStrategy):
    INTERFACE_VERSION = 2

    timeframe = '15m'

    stoploss = -0.34338

    minimal_roi = {"0": 0.27058, "33": 0.0853, "64": 0.04093, "244": 0}

    buy_cci = IntParameter(low=-200, high=200, default=100, space='buy', optimize=True)
    buy_cciTime = IntParameter(low=10, high=80, default=20, space='buy', optimize=True)
    buy_rsi = IntParameter(low=10, high=90, default=30, space='buy', optimize=True)
    buy_rsiTime = IntParameter(low=10, high=80, default=26, space='buy', optimize=True)

    sell_cci = IntParameter(low=-200, high=200, default=100, space='sell', optimize=True)
    sell_cciTime = IntParameter(low=10, high=80, default=20, space='sell', optimize=True)
    sell_rsi = IntParameter(low=10, high=90, default=30, space='sell', optimize=True)
    sell_rsiTime = IntParameter(low=10, high=80, default=26, space='sell', optimize=True)

    # Buy hyperspace params:
    buy_params = {
        "buy_cci": -175,
        "buy_cciTime": 72,
        "buy_rsi": 90,
        "buy_rsiTime": 36,
    }

    # Sell hyperspace params:
    sell_params = {
        "sell_cci": -106,
        "sell_cciTime": 66,
        "sell_rsi": 88,
        "sell_rsiTime": 45,
    }

    def informative_pairs(self):
        return []

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

        for val in self.buy_cciTime.range:
            dataframe[f'cci-{val}'] = ta.CCI(dataframe, timeperiod=val)

        for val in self.sell_cciTime.range:
            dataframe[f'cci-sell-{val}'] = ta.CCI(dataframe, timeperiod=val)

        for val in self.buy_rsiTime.range:
            dataframe[f'rsi-{val}'] = ta.RSI(dataframe, timeperiod=val)

        for val in self.sell_rsiTime.range:
            dataframe[f'rsi-sell-{val}'] = ta.RSI(dataframe, timeperiod=val)

        return dataframe

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

        dataframe.loc[
            (
                (dataframe[f'cci-{self.buy_cciTime.value}'] < self.buy_cci.value) &
                (dataframe[f'rsi-{self.buy_rsiTime.value}'] < self.buy_rsi.value)
            ),
            'buy'] = 1

        return dataframe

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

        dataframe.loc[
            (
                (dataframe[f'cci-sell-{self.sell_cciTime.value}'] > self.sell_cci.value) &
                (dataframe[f'rsi-sell-{self.sell_rsiTime.value}'] > self.sell_rsi.value)
            ),
            'sell'] = 1

        return dataframe
Example #17
0
class MACDStrategy(IStrategy):
    """
    author@: Gert Wohlgemuth

    idea:

        uptrend definition:
            MACD above MACD signal
            and CCI < -50

        downtrend definition:
            MACD below MACD signal
            and CCI > 100

    freqtrade hyperopt --strategy MACDStrategy --hyperopt-loss <someLossFunction> --spaces buy sell

    The idea is to optimize only the CCI value.
    - Buy side: CCI between -700 and 0
    - Sell side: CCI between 0 and 700

    """
    INTERFACE_VERSION = 2

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

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

    buy_cci = IntParameter(low=-700,
                           high=0,
                           default=-50,
                           space='buy',
                           optimize=True)
    sell_cci = IntParameter(low=0,
                            high=700,
                            default=100,
                            space='sell',
                            optimize=True)

    # Buy hyperspace params:
    buy_params = {
        "buy_cci": -48,
    }

    # Sell hyperspace params:
    sell_params = {
        "sell_cci": 687,
    }

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

        macd = ta.MACD(dataframe)
        dataframe['macd'] = macd['macd']
        dataframe['macdsignal'] = macd['macdsignal']
        dataframe['macdhist'] = macd['macdhist']
        dataframe['cci'] = ta.CCI(dataframe)

        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[((dataframe['macd'] > dataframe['macdsignal']) &
                       (dataframe['cci'] <= self.buy_cci.value) &
                       (dataframe['volume'] > 0)  # Make sure Volume is not 0
                       ), '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[((dataframe['macd'] < dataframe['macdsignal']) &
                       (dataframe['cci'] >= self.sell_cci.value) &
                       (dataframe['volume'] > 0)  # Make sure Volume is not 0
                       ), 'sell'] = 1

        return dataframe
Example #18
0
class FrostAuraMP1Strategy(IStrategy):
    """
    This is FrostAura's implementation of the Facebook Prophet library for time series forecasting.
    
    Last Optimization:
        Profit %        : -.--%
        Optimized for   : Last 45 days, 1h
        Avg             : -m
    """
    # 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.797,
        "1905": 0.212,
        "3884": 0.089,
        "4825": 0
    }

    # Stoploss:
    stoploss = -0.27

    # 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': 'market',
        'sell': 'market',
        '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:
        # Train the Prophet model on each time tick.
        df = dataframe[['time', 'close']] \
                .rename(columns={'time': 'ds', 'close': 'y'})

        self.model = Prophet(interval_width=0.95, daily_seasonality=True)
        self.model.fit(df)

        return dataframe

    def make_predictions(self, dataframe: DataFrame, n_predictions, frequency = 'H') -> DataFrame:
        # Add the predictions to the dataframe with column name 'yhat'.
        predictions = self.model.make_future_dataframe(periods=n_predictions, freq=frequency)
        forecasts = self.model.predict(predictions)['ds', 'yhat']

        return forecasts

    buy_n_predictions = IntParameter([1, 48], default=4, space='buy')
    buy_required_delta_percentage = IntParameter([1, 100], default=1, space='buy')
    1 + 1
    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        hours_to_predict_ahead = self.buy_n_predictions.value
        predictions = self.make_predictions(dataframe, n_predictions=hours_to_predict_ahead)
        mean_prediction = predictions['yhat'].mean()
        trend_is_upwards = (mean_prediction * (1 + self.buy_required_delta_percentage.value / 100 )) > dataframe['close']

        dataframe.loc[
            (
                (trend_is_upwards)
            ),
            'buy'] = 1

        return dataframe

    sell_n_predictions = IntParameter([1, 48], default=4, space='sell')
    sell_required_delta_percentage = IntParameter([1, 100], default=1, space='sell')

    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        hours_to_predict_ahead = self.sell_n_predictions.value
        predictions = self.make_predictions(dataframe, n_predictions=hours_to_predict_ahead)
        mean_prediction = predictions['yhat'].mean()
        trend_is_downwards = (mean_prediction * (1 - self.sell_required_delta_percentage.value / 100 )) < dataframe['close']
        
        dataframe.loc[
            (
                (trend_is_downwards)
            ),
            'sell'] = 1
        
        return dataframe
Example #19
0
class SampleStrategy(IStrategy):
    """
    This is a sample strategy to inspire you.
    More information in https://www.freqtrade.io/en/latest/strategy-customization/

    You can:
        :return: a Dataframe with all mandatory indicators for the strategies
    - Rename the class name (Do not forget to update class_name)
    - Add any methods you want to build your strategy
    - Add any lib you need to build your strategy

    You must keep:
    - the lib in the section "Do not remove these libs"
    - the methods: populate_indicators, populate_buy_trend, populate_sell_trend
    You should keep:
    - timeframe, minimal_roi, stoploss, trailing_*
    """
    # 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 = {"60": 0.01, "30": 0.02, "0": 0.04}

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

    # Trailing stoploss
    trailing_stop = False
    # trailing_only_offset_is_reached = False
    # trailing_stop_positive = 0.01
    # trailing_stop_positive_offset = 0.0  # Disabled / not configured

    # Hyperoptable parameters
    buy_rsi = IntParameter(low=1,
                           high=50,
                           default=30,
                           space='buy',
                           optimize=True,
                           load=True)
    sell_rsi = IntParameter(low=50,
                            high=100,
                            default=70,
                            space='sell',
                            optimize=True,
                            load=True)

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

    # 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):
        """
        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.
        :param dataframe: Dataframe with data from the exchange
        :param metadata: Additional information, like the currently traded pair
        :return: a Dataframe with all mandatory indicators for the strategies
        """

        # Momentum Indicators
        # ------------------------------------

        # ADX
        dataframe['adx'] = ta.ADX(dataframe)

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

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

        # # Aroon, Aroon Oscillator
        # aroon = ta.AROON(dataframe)
        # dataframe['aroonup'] = aroon['aroonup']
        # dataframe['aroondown'] = aroon['aroondown']
        # dataframe['aroonosc'] = ta.AROONOSC(dataframe)

        # # Awesome Oscillator
        # dataframe['ao'] = qtpylib.awesome_oscillator(dataframe)

        # # Keltner Channel
        # keltner = qtpylib.keltner_channel(dataframe)
        # dataframe["kc_upperband"] = keltner["upper"]
        # dataframe["kc_lowerband"] = keltner["lower"]
        # dataframe["kc_middleband"] = keltner["mid"]
        # dataframe["kc_percent"] = (
        #     (dataframe["close"] - dataframe["kc_lowerband"]) /
        #     (dataframe["kc_upperband"] - dataframe["kc_lowerband"])
        # )
        # dataframe["kc_width"] = (
        #     (dataframe["kc_upperband"] - dataframe["kc_lowerband"]) / dataframe["kc_middleband"]
        # )

        # # Ultimate Oscillator
        # dataframe['uo'] = ta.ULTOSC(dataframe)

        # # Commodity Channel Index: values [Oversold:-100, Overbought:100]
        # dataframe['cci'] = ta.CCI(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'] = (np.exp(2 * rsi) - 1) / (np.exp(2 * rsi) + 1)

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

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

        # Stochastic Fast
        stoch_fast = ta.STOCHF(dataframe)
        dataframe['fastd'] = stoch_fast['fastd']
        dataframe['fastk'] = stoch_fast['fastk']

        # # Stochastic RSI
        # Please read https://github.com/freqtrade/freqtrade/issues/2961 before using this.
        # STOCHRSI is NOT aligned with tradingview, which may result in non-expected results.
        # stoch_rsi = ta.STOCHRSI(dataframe)
        # dataframe['fastd_rsi'] = stoch_rsi['fastd']
        # dataframe['fastk_rsi'] = stoch_rsi['fastk']

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

        # MFI
        dataframe['mfi'] = ta.MFI(dataframe)

        # # ROC
        # dataframe['roc'] = ta.ROC(dataframe)

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

        # Bollinger Bands
        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["bb_percent"] = (
            (dataframe["close"] - dataframe["bb_lowerband"]) /
            (dataframe["bb_upperband"] - dataframe["bb_lowerband"]))
        dataframe["bb_width"] = (
            (dataframe["bb_upperband"] - dataframe["bb_lowerband"]) /
            dataframe["bb_middleband"])

        # Bollinger Bands - Weighted (EMA based instead of SMA)
        # weighted_bollinger = qtpylib.weighted_bollinger_bands(
        #     qtpylib.typical_price(dataframe), window=20, stds=2
        # )
        # dataframe["wbb_upperband"] = weighted_bollinger["upper"]
        # dataframe["wbb_lowerband"] = weighted_bollinger["lower"]
        # dataframe["wbb_middleband"] = weighted_bollinger["mid"]
        # dataframe["wbb_percent"] = (
        #     (dataframe["close"] - dataframe["wbb_lowerband"]) /
        #     (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"])
        # )
        # dataframe["wbb_width"] = (
        #     (dataframe["wbb_upperband"] - dataframe["wbb_lowerband"]) /
        #     dataframe["wbb_middleband"]
        # )

        # # EMA - Exponential Moving Average
        # dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3)
        # dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
        # dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
        # dataframe['ema21'] = ta.EMA(dataframe, timeperiod=21)
        # dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
        # dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)

        # # SMA - Simple Moving Average
        # dataframe['sma3'] = ta.SMA(dataframe, timeperiod=3)
        # dataframe['sma5'] = ta.SMA(dataframe, timeperiod=5)
        # dataframe['sma10'] = ta.SMA(dataframe, timeperiod=10)
        # dataframe['sma21'] = ta.SMA(dataframe, timeperiod=21)
        # dataframe['sma50'] = ta.SMA(dataframe, timeperiod=50)
        # dataframe['sma100'] = ta.SMA(dataframe, timeperiod=100)

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

        # TEMA - Triple Exponential Moving Average
        dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)

        # Cycle Indicator
        # ------------------------------------
        # Hilbert Transform Indicator - SineWave
        hilbert = ta.HT_SINE(dataframe)
        dataframe['htsine'] = hilbert['sine']
        dataframe['htleadsine'] = hilbert['leadsine']

        # Pattern Recognition - Bullish candlestick patterns
        # ------------------------------------
        # # Hammer: values [0, 100]
        # dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe)
        # # Inverted Hammer: values [0, 100]
        # dataframe['CDLINVERTEDHAMMER'] = ta.CDLINVERTEDHAMMER(dataframe)
        # # Dragonfly Doji: values [0, 100]
        # dataframe['CDLDRAGONFLYDOJI'] = ta.CDLDRAGONFLYDOJI(dataframe)
        # # Piercing Line: values [0, 100]
        # dataframe['CDLPIERCING'] = ta.CDLPIERCING(dataframe) # values [0, 100]
        # # Morningstar: values [0, 100]
        # dataframe['CDLMORNINGSTAR'] = ta.CDLMORNINGSTAR(dataframe) # values [0, 100]
        # # Three White Soldiers: values [0, 100]
        # dataframe['CDL3WHITESOLDIERS'] = ta.CDL3WHITESOLDIERS(dataframe) # values [0, 100]

        # Pattern Recognition - Bearish candlestick patterns
        # ------------------------------------
        # # Hanging Man: values [0, 100]
        # dataframe['CDLHANGINGMAN'] = ta.CDLHANGINGMAN(dataframe)
        # # Shooting Star: values [0, 100]
        # dataframe['CDLSHOOTINGSTAR'] = ta.CDLSHOOTINGSTAR(dataframe)
        # # Gravestone Doji: values [0, 100]
        # dataframe['CDLGRAVESTONEDOJI'] = ta.CDLGRAVESTONEDOJI(dataframe)
        # # Dark Cloud Cover: values [0, 100]
        # dataframe['CDLDARKCLOUDCOVER'] = ta.CDLDARKCLOUDCOVER(dataframe)
        # # Evening Doji Star: values [0, 100]
        # dataframe['CDLEVENINGDOJISTAR'] = ta.CDLEVENINGDOJISTAR(dataframe)
        # # Evening Star: values [0, 100]
        # dataframe['CDLEVENINGSTAR'] = ta.CDLEVENINGSTAR(dataframe)

        # Pattern Recognition - Bullish/Bearish candlestick patterns
        # ------------------------------------
        # # Three Line Strike: values [0, -100, 100]
        # dataframe['CDL3LINESTRIKE'] = ta.CDL3LINESTRIKE(dataframe)
        # # Spinning Top: values [0, -100, 100]
        # dataframe['CDLSPINNINGTOP'] = ta.CDLSPINNINGTOP(dataframe) # values [0, -100, 100]
        # # Engulfing: values [0, -100, 100]
        # dataframe['CDLENGULFING'] = ta.CDLENGULFING(dataframe) # values [0, -100, 100]
        # # Harami: values [0, -100, 100]
        # dataframe['CDLHARAMI'] = ta.CDLHARAMI(dataframe) # values [0, -100, 100]
        # # Three Outside Up/Down: values [0, -100, 100]
        # dataframe['CDL3OUTSIDE'] = ta.CDL3OUTSIDE(dataframe) # values [0, -100, 100]
        # # Three Inside Up/Down: values [0, -100, 100]
        # dataframe['CDL3INSIDE'] = ta.CDL3INSIDE(dataframe) # values [0, -100, 100]

        # # Chart type
        # # ------------------------------------
        # # Heikin Ashi Strategy
        # heikinashi = qtpylib.heikinashi(dataframe)
        # dataframe['ha_open'] = heikinashi['open']
        # dataframe['ha_close'] = heikinashi['close']
        # dataframe['ha_high'] = heikinashi['high']
        # dataframe['ha_low'] = heikinashi['low']

        # Retrieve best bid and best ask from the orderbook
        # ------------------------------------
        """
        # first check if dataprovider is available
        if self.dp:
            if self.dp.runmode.value in ('live', 'dry_run'):
                ob = self.dp.orderbook(metadata['pair'], 1)
                dataframe['best_bid'] = ob['bids'][0][0]
                dataframe['best_ask'] = ob['asks'][0][0]
        """

        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 populated with indicators
        :param metadata: Additional information, like the currently traded pair
        :return: DataFrame with buy column
        """
        dataframe.loc[(
            # Signal: RSI crosses above 30
            (qtpylib.crossed_above(dataframe['rsi'], self.buy_rsi.value)) &
            (dataframe['tema'] <= dataframe['bb_middleband'])
            &  # Guard: tema below BB middle
            (dataframe['tema'] > dataframe['tema'].shift(1))
            &  # Guard: tema is raising
            (dataframe['volume'] > 0)  # Make sure Volume is not 0
        ), '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 populated with indicators
        :param metadata: Additional information, like the currently traded pair
        :return: DataFrame with sell column
        """
        dataframe.loc[(
            # Signal: RSI crosses above 70
            (qtpylib.crossed_above(dataframe['rsi'], self.sell_rsi.value)) &
            (dataframe['tema'] > dataframe['bb_middleband'])
            &  # Guard: tema above BB middle
            (dataframe['tema'] < dataframe['tema'].shift(1))
            &  # Guard: tema is falling
            (dataframe['volume'] > 0)  # Make sure Volume is not 0
        ), 'sell'] = 1
        return dataframe
Example #20
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
Example #21
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
class HourBasedStrategy(IStrategy):
    # SHIB/USDT, 1000$x1:100days
    # 158/1000:     51 trades. 29/19/3 Wins/Draws/Losses. Avg profit   4.02%. Median profit   2.48%. Total profit  4867.53438466 USDT ( 486.75%). Avg duration 1 day, 19:38:00 min. Objective: -4.17276
    # buy_params = {"buy_hour_max": 18,"buy_hour_min": 7,}
    # sell_params = {"sell_hour_max": 9,"sell_hour_min": 21,}
    # minimal_roi = {"0": 0.18,"171": 0.155,"315": 0.075,"1035": 0}
    # stoploss = -0.292

    # SHIB/USDT, 1000$x1:100days
    # 36/1000:    113 trades. 55/14/44 Wins/Draws/Losses. Avg profit   2.06%. Median profit   0.00%. Total profit  5126.14785426 USDT ( 512.61%). Avg duration 16:48:00 min. Objective: -4.57837
    # buy_params = {"buy_hour_max": 21,"buy_hour_min": 6,}
    # sell_params = {"sell_hour_max": 6,"sell_hour_min": 4,}
    # minimal_roi = {"0": 0.247,"386": 0.186,"866": 0.052,"1119": 0}
    # stoploss = -0.302

    # SAND/USDT, 1000$x1:100days
    # 72/1000:    158 trades. 67/13/78 Wins/Draws/Losses. Avg profit   1.37%. Median profit   0.00%. Total profit  4274.73622346 USDT ( 427.47%). Avg duration 13:50:00 min. Objective: -4.87331
    # buy_params = {"buy_hour_max": 23,"buy_hour_min": 4,}
    # sell_params = {"sell_hour_max": 23,"sell_hour_min": 3,}
    # minimal_roi = {"0": 0.482,"266": 0.191,"474": 0.09,"1759": 0}
    # stoploss = -0.05

    # KDA/USDT, 1000$x1:100days
    # 7/1000:     65 trades. 40/23/2 Wins/Draws/Losses. Avg profit   6.42%. Median profit   7.59%. Total profit  41120.00939125 USDT ( 4112.00%). Avg duration 1 day, 9:40:00 min. Objective: -8.46089
    # buy_params = {"buy_hour_max": 22,"buy_hour_min": 9,}
    # sell_params = {"sell_hour_max": 1,"sell_hour_min": 7,}
    # minimal_roi = {"0": 0.517,"398": 0.206,"1003": 0.076,"1580": 0}
    # stoploss = -0.338

    # {KDA/USDT, BTC/USDT, DOGE/USDT, SAND/USDT, ETH/USDT, SOL/USDT}, 1000$x1:100days, ShuffleFilter42
    # 56/1000:     63 trades. 41/19/3 Wins/Draws/Losses. Avg profit   4.60%. Median profit   8.89%. Total profit  11596.50333022 USDT ( 1159.65%). Avg duration 1 day, 14:46:00 min. Objective: -5.76694

    # Buy hyperspace params:
    buy_params = {
        "buy_hour_max": 24,
        "buy_hour_min": 4,
    }

    # Sell hyperspace params:
    sell_params = {
        "sell_hour_max": 21,
        "sell_hour_min": 22,
    }

    # ROI table:
    minimal_roi = {
        "0": 0.528,
        "169": 0.113,
        "528": 0.089,
        "1837": 0
    }

    # Stoploss:
    stoploss = -0.10

    # Optimal timeframe
    timeframe = '1h'

    buy_hour_min = IntParameter(0, 24, default=1, space='buy')
    buy_hour_max = IntParameter(0, 24, default=0, space='buy')

    sell_hour_min = IntParameter(0, 24, default=1, space='sell')
    sell_hour_max = IntParameter(0, 24, default=0, space='sell')

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe['hour'] = dataframe['date'].dt.hour
        return dataframe

    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        min, max = self.buy_hour_min.value, self.buy_hour_max.value
        dataframe.loc[
            (
                (dataframe['hour'].between(min, max))
            ),
            'buy'] = 1

        return dataframe

    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        min, max = self.sell_hour_min.value, self.sell_hour_max.value
        dataframe.loc[
            (
                (dataframe['hour'].between(max, min))
            ),
            'sell'] = 1
        return dataframe
Example #23
0
class HyperoptableStrategy(IStrategy):
    """
    Default Strategy provided by freqtrade bot.
    Please do not modify this strategy, it's  intended for internal use only.
    Please look at the SampleStrategy in the user_data/strategy directory
    or strategy repository https://github.com/freqtrade/freqtrade-strategies
    for samples and inspiration.
    """
    INTERFACE_VERSION = 2

    # Minimal ROI designed for the strategy
    minimal_roi = {"40": 0.0, "30": 0.01, "20": 0.02, "0": 0.04}

    # Optimal stoploss designed for the strategy
    stoploss = -0.10

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

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

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

    # Optional time in force for orders
    order_time_in_force = {
        'buy': 'gtc',
        'sell': 'gtc',
    }

    buy_params = {
        'buy_rsi': 35,
        # Intentionally not specified, so "default" is tested
        # 'buy_plusdi': 0.4
    }

    sell_params = {'sell_rsi': 74, 'sell_minusdi': 0.4}

    buy_rsi = IntParameter([0, 50], default=30, space='buy')
    buy_plusdi = RealParameter(low=0, high=1, default=0.5, space='buy')
    sell_rsi = IntParameter(low=50, high=100, default=70, space='sell')
    sell_minusdi = DecimalParameter(low=0,
                                    high=1,
                                    default=0.5001,
                                    decimals=3,
                                    space='sell',
                                    load=False)

    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.
        :param dataframe: Dataframe with data from the exchange
        :param metadata: Additional information, like the currently traded pair
        :return: a Dataframe with all mandatory indicators for the strategies
        """

        # Momentum Indicator
        # ------------------------------------

        # ADX
        dataframe['adx'] = ta.ADX(dataframe)

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

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

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

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

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

        # Bollinger bands
        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']

        # EMA - Exponential Moving Average
        dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)

        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
        :param metadata: Additional information, like the currently traded pair
        :return: DataFrame with buy column
        """
        dataframe.loc[((dataframe['rsi'] < self.buy_rsi.value) &
                       (dataframe['fastd'] < 35) & (dataframe['adx'] > 30) &
                       (dataframe['plus_di'] > self.buy_plusdi.value)) |
                      ((dataframe['adx'] > 65) &
                       (dataframe['plus_di'] > self.buy_plusdi.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
        :param metadata: Additional information, like the currently traded pair
        :return: DataFrame with buy column
        """
        dataframe.loc[(
            ((qtpylib.crossed_above(dataframe['rsi'], self.sell_rsi.value)) |
             (qtpylib.crossed_above(dataframe['fastd'], 70))) &
            (dataframe['adx'] > 10) & (dataframe['minus_di'] > 0)) |
                      ((dataframe['adx'] > 70) &
                       (dataframe['minus_di'] > self.sell_minusdi.value)),
                      'sell'] = 1
        return dataframe
class CofiBitStrategy(IStrategy):
    """
        taken from slack by user CofiBit
    """

    # Buy hyperspace params:
    buy_params = {
        "buy_fastx": 25,
        "buy_adx": 25,
    }

    # Sell hyperspace params:
    sell_params = {
        "sell_fastx": 75,
    }

    # Minimal ROI designed for the strategy.
    # This attribute will be overridden if the config file contains "minimal_roi"
    minimal_roi = {"40": 0.05, "30": 0.06, "20": 0.07, "0": 0.10}

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

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

    buy_fastx = IntParameter(20, 30, default=25)
    buy_adx = IntParameter(20, 30, default=25)
    sell_fastx = IntParameter(70, 80, default=75)

    def populate_indicators(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        stoch_fast = ta.STOCHF(dataframe, 5, 3, 0, 3, 0)
        dataframe['fastd'] = stoch_fast['fastd']
        dataframe['fastk'] = stoch_fast['fastk']
        dataframe['ema_high'] = ta.EMA(dataframe, timeperiod=5, price='high')
        dataframe['ema_close'] = ta.EMA(dataframe, timeperiod=5, price='close')
        dataframe['ema_low'] = ta.EMA(dataframe, timeperiod=5, price='low')
        dataframe['adx'] = ta.ADX(dataframe)

        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[(
            (dataframe['open'] < dataframe['ema_low']) &
            (qtpylib.crossed_above(dataframe['fastk'], dataframe['fastd'])) &
            (dataframe['fastk'] < self.buy_fastx.value) &
            (dataframe['fastd'] < self.buy_fastx.value) &
            (dataframe['adx'] > self.buy_adx.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
        """
        dataframe.loc[((dataframe['open'] >= dataframe['ema_high'])) | (
            (qtpylib.crossed_above(dataframe['fastk'], self.sell_fastx.value))
            |
            (qtpylib.crossed_above(dataframe['fastd'], self.sell_fastx.value))
        ), 'sell'] = 1

        return dataframe
Example #25
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 AverageStrategy(IStrategy):
    """

    author@: Gert Wohlgemuth

    idea:
        buys and sells on crossovers - doesn't really perfom that well and its just a proof of concept
    """

    # Minimal ROI designed for the strategy.
    # This attribute will be overridden if the config file contains "minimal_roi"
    minimal_roi = {"0": 0.5}

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

    # Optimal timeframe for the strategy
    timeframe = '4h'

    buy_range_short = IntParameter(5, 20, default=8)
    buy_range_long = IntParameter(20, 120, default=21)

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

        # Combine all ranges ... to avoid duplicate calculation
        for val in list(
                set(
                    list(self.buy_range_short.range) +
                    list(self.buy_range_long.range))):
            dataframe[f'ema{val}'] = ta.EMA(dataframe, timeperiod=val)

        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[f'ema{self.buy_range_short.value}'],
                          dataframe[f'ema{self.buy_range_long.value}']) &
            (dataframe['volume'] > 0)), '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[f'ema{self.buy_range_long.value}'],
                          dataframe[f'ema{self.buy_range_short.value}']) &
            (dataframe['volume'] > 0)), 'sell'] = 1
        return dataframe
Example #27
0
class StrategyTestV3(IStrategy):
    """
    Strategy used by tests freqtrade bot.
    Please do not modify this strategy, it's  intended for internal use only.
    Please look at the SampleStrategy in the user_data/strategy directory
    or strategy repository https://github.com/freqtrade/freqtrade-strategies
    for samples and inspiration.
    """
    INTERFACE_VERSION = 3

    # Minimal ROI designed for the strategy
    minimal_roi = {"40": 0.0, "30": 0.01, "20": 0.02, "0": 0.04}

    # Optimal stoploss designed for the strategy
    stoploss = -0.10

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

    # Optional order type mapping
    order_types = {
        'entry': 'limit',
        'exit': 'limit',
        'stoploss': 'limit',
        'stoploss_on_exchange': False
    }

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

    # Optional time in force for orders
    order_time_in_force = {
        'entry': 'gtc',
        'exit': 'gtc',
    }

    buy_params = {
        'buy_rsi': 35,
        # Intentionally not specified, so "default" is tested
        # 'buy_plusdi': 0.4
    }

    sell_params = {'sell_rsi': 74, 'sell_minusdi': 0.4}

    buy_rsi = IntParameter([0, 50], default=30, space='buy')
    buy_plusdi = RealParameter(low=0, high=1, default=0.5, space='buy')
    sell_rsi = IntParameter(low=50, high=100, default=70, space='sell')
    sell_minusdi = DecimalParameter(low=0,
                                    high=1,
                                    default=0.5001,
                                    decimals=3,
                                    space='sell',
                                    load=False)
    protection_enabled = BooleanParameter(default=True)
    protection_cooldown_lookback = IntParameter([0, 50], default=30)

    # TODO: Can this work with protection tests? (replace HyperoptableStrategy implicitly ... )
    # @property
    # def protections(self):
    #     prot = []
    #     if self.protection_enabled.value:
    #         prot.append({
    #             "method": "CooldownPeriod",
    #             "stop_duration_candles": self.protection_cooldown_lookback.value
    #         })
    #     return prot

    def informative_pairs(self):

        return []

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

        # Momentum Indicator
        # ------------------------------------

        # ADX
        dataframe['adx'] = ta.ADX(dataframe)

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

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

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

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

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

        # Bollinger bands
        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']

        # EMA - Exponential Moving Average
        dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)

        return dataframe

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

        dataframe.loc[((dataframe['rsi'] < self.buy_rsi.value) &
                       (dataframe['fastd'] < 35) & (dataframe['adx'] > 30) &
                       (dataframe['plus_di'] > self.buy_plusdi.value)) |
                      ((dataframe['adx'] > 65) &
                       (dataframe['plus_di'] > self.buy_plusdi.value)),
                      'enter_long'] = 1
        dataframe.loc[(
            qtpylib.crossed_below(dataframe['rsi'], self.sell_rsi.value)),
                      'enter_short'] = 1

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        dataframe.loc[(
            ((qtpylib.crossed_above(dataframe['rsi'], self.sell_rsi.value)) |
             (qtpylib.crossed_above(dataframe['fastd'], 70))) &
            (dataframe['adx'] > 10) & (dataframe['minus_di'] > 0)) |
                      ((dataframe['adx'] > 70) &
                       (dataframe['minus_di'] > self.sell_minusdi.value)),
                      'exit_long'] = 1

        dataframe.loc[(
            qtpylib.crossed_above(dataframe['rsi'], self.buy_rsi.value)),
                      'exit_short'] = 1

        return dataframe

    def leverage(self, pair: str, current_time: datetime, current_rate: float,
                 proposed_leverage: float, max_leverage: float, side: str,
                 **kwargs) -> float:
        # Return 3.0 in all cases.
        # Bot-logic must make sure it's an allowed leverage and eventually adjust accordingly.

        return 3.0

    def adjust_trade_position(self, trade: Trade, current_time: datetime,
                              current_rate: float, current_profit: float,
                              min_stake: float, max_stake: float, **kwargs):

        if current_profit < -0.0075:
            orders = trade.select_filled_orders(trade.entry_side)
            return round(orders[0].cost, 0)

        return None
Example #28
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
Example #29
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
Example #30
0
class MADisplaceV3(IStrategy):

    ma_lower_length = IntParameter(15,
                                   25,
                                   default=buy_params['ma_lower_length'],
                                   space='buy')
    ma_lower_offset = DecimalParameter(0.95,
                                       0.97,
                                       default=buy_params['ma_lower_offset'],
                                       space='buy')

    informative_fast_length = IntParameter(
        15, 35, default=buy_params['informative_fast_length'], space='disable')
    informative_slow_length = IntParameter(
        20, 40, default=buy_params['informative_slow_length'], space='disable')

    rsi_fast_length = IntParameter(2,
                                   8,
                                   default=buy_params['rsi_fast_length'],
                                   space='disable')
    rsi_fast_threshold = IntParameter(5,
                                      35,
                                      default=buy_params['rsi_fast_threshold'],
                                      space='disable')
    rsi_slow_length = IntParameter(10,
                                   45,
                                   default=buy_params['rsi_slow_length'],
                                   space='disable')
    rsi_slow_confirmation = IntParameter(
        1, 5, default=buy_params['rsi_slow_confirmation'], space='disable')

    ma_middle_1_length = IntParameter(
        15, 35, default=sell_params['ma_middle_1_length'], space='sell')
    ma_middle_1_offset = DecimalParameter(
        0.93, 1.005, default=sell_params['ma_middle_1_offset'], space='sell')
    ma_upper_length = IntParameter(15,
                                   25,
                                   default=sell_params['ma_upper_length'],
                                   space='sell')
    ma_upper_offset = DecimalParameter(1.005,
                                       1.025,
                                       default=sell_params['ma_upper_offset'],
                                       space='sell')

    minimal_roi = {"0": 1}

    stoploss = -0.2

    trailing_stop = True
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.025
    trailing_only_offset_is_reached = True

    timeframe = '5m'

    use_sell_signal = True
    sell_profit_only = False

    process_only_new_candles = True

    plot_config = {
        'main_plot': {
            'ma_lower': {
                'color': 'red'
            },
            'ma_middle_1': {
                'color': 'green'
            },
            'ma_upper': {
                'color': 'pink'
            },
        },
    }

    use_custom_stoploss = True
    startup_candle_count = 200

    informative_timeframe = '1h'

    def informative_pairs(self):

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

        return informative_pairs

    def get_informative_indicators(self, metadata: dict):

        if self.config['runmode'].value == 'hyperopt':
            dataframe = self.informative_dataframe.copy()
        else:
            dataframe = self.dp.get_pair_dataframe(
                pair=metadata['pair'], timeframe=self.informative_timeframe)

        dataframe['ema_fast'] = ta.EMA(dataframe,
                                       timeperiod=int(
                                           self.informative_fast_length.value))
        dataframe['ema_slow'] = ta.EMA(dataframe,
                                       timeperiod=int(
                                           self.informative_slow_length.value))

        dataframe['uptrend'] = ((dataframe['ema_fast'] >
                                 dataframe['ema_slow'])).astype('int')

        return dataframe

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

        if current_profit < -0.04 and current_time - timedelta(
                minutes=35) > trade.open_date_utc:
            return -0.01

        return -0.99

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

        dataframe['rsi_fast'] = ta.RSI(dataframe,
                                       timeperiod=int(
                                           self.rsi_fast_length.value))
        dataframe['rsi_slow'] = ta.RSI(dataframe,
                                       timeperiod=int(
                                           self.rsi_slow_length.value))
        dataframe['rsi_slow_descending'] = (
            dataframe['rsi_slow'] <
            dataframe['rsi_slow'].shift()).astype('int')

        dataframe['ma_lower'] = ta.SMA(
            dataframe, timeperiod=int(
                self.ma_lower_length.value)) * self.ma_lower_offset.value
        dataframe['ma_middle_1'] = ta.SMA(
            dataframe, timeperiod=int(
                self.ma_middle_1_length.value)) * self.ma_middle_1_offset.value
        dataframe['ma_upper'] = ta.SMA(
            dataframe, timeperiod=int(
                self.ma_upper_length.value)) * self.ma_upper_offset.value

        # 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

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

        if self.config['runmode'].value == 'hyperopt':

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

        if self.config['runmode'].value != 'hyperopt':

            informative = self.get_informative_indicators(metadata)
            dataframe = self.merge_informative(informative, dataframe)
            dataframe = self.get_main_indicators(dataframe, metadata)

        return dataframe

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

        # calculate indicators with adjustable params for hyperopt
        # it's calling multiple times and dataframe overrides same columns
        # so check if any calculated column already exist

        if self.config[
                'runmode'].value == 'hyperopt' and 'uptrend' not in dataframe:
            informative = self.get_informative_indicators(metadata)
            dataframe = self.merge_informative(informative, dataframe)
            dataframe = self.get_main_indicators(dataframe, metadata)
            pd.options.mode.chained_assignment = None

        dataframe.loc[((dataframe['rsi_slow_descending'].
                        rolling(self.rsi_slow_confirmation.value).sum() ==
                        self.rsi_slow_confirmation.value)
                       &
                       (dataframe['rsi_fast'] < self.rsi_fast_threshold.value)
                       & (dataframe['uptrend'] > 0)
                       & (dataframe['close'] < dataframe['ma_lower'])
                       & (dataframe['volume'] > 0)), 'buy'] = 1

        return dataframe

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

        if self.config[
                'runmode'].value == 'hyperopt' and 'uptrend' not in dataframe:
            informative = self.get_informative_indicators(metadata)
            dataframe = self.merge_informative(informative, dataframe)
            dataframe = self.get_main_indicators(dataframe, metadata)
            pd.options.mode.chained_assignment = None

        dataframe.loc[((
            (dataframe['uptrend'] == 0)
            | (dataframe['close'] > dataframe['ma_upper'])
            | (qtpylib.
               crossed_below(dataframe['close'], dataframe['ma_middle_1'])))
                       & (dataframe['volume'] > 0)), 'sell'] = 1

        return dataframe

    def merge_informative(self, informative: DataFrame,
                          dataframe: DataFrame) -> DataFrame:

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

        # don't overwrite the base dataframe's HLCV information
        skip_columns = [
            (s + "_" + self.informative_timeframe)
            for s in ['date', 'open', 'high', 'low', 'close', 'volume']
        ]
        dataframe.rename(columns=lambda s: s.replace(
            "_{}".format(self.informative_timeframe), "")
                         if (not s in skip_columns) else s,
                         inplace=True)

        return dataframe