コード例 #1
0
    def custom_stoploss(self, pair: str, trade: 'Trade',
                        current_time: datetime, current_rate: float,
                        current_profit: float, **kwargs) -> float:
        cs = self.get_pair_params(pair, 'custom_stop')

        trade_dur = int(
            (current_time.timestamp() - trade.open_date_utc.timestamp()) // 60)
        min_profit = trade.calc_profit_ratio(trade.min_rate)
        max_profit = trade.calc_profit_ratio(trade.max_rate)
        profit_diff = current_profit - min_profit

        decay_stoploss = cta.linear_growth(cs['decay-start'], cs['decay-end'],
                                           cs['decay-delay'], cs['decay-time'],
                                           trade_dur)

        # enable stoploss in positive profits after threshold to trail as specifed distance
        if cs['pos-trail'] == True:
            if current_profit > cs['pos-threshold']:
                return current_profit - cs['pos-trail-dist']

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

        if current_profit < cs['cur-threshold']:
            # Dynamic bailout based on rate of change
            if (roc /
                    100) <= cs['roc-bail'] or cs['con-bail'] <= consensus_sell:
                if cs['bail-how'] == 'atr':
                    return ((current_rate - atr) / current_rate) - 1
                elif cs['bail-how'] == 'immediate':
                    return current_rate
                else:
                    return decay_stoploss

        # if we might be on a rebound, move the stoploss to the low point or keep it where it was
        if (current_profit > min_profit) or roc > 0 or rmi_slow >= cs[
                'rmi-trend'] or consensus_buy >= cs['con-trend']:
            if profit_diff > cs['cur-min-diff']:
                return min_profit
            return -1

        return decay_stoploss
コード例 #2
0
    def custom_stoploss(self, pair: str, trade: 'Trade', current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float:
        start = self.custom_stop['decay-start']
        end = self.custom_stop['decay-end']
        time = self.custom_stop['decay-time']
        delay = self.custom_stop['decay-delay']
        pos_trail = self.custom_stop['pos-trail']
        pos_threshold = self.custom_stop['pos-threshold']
        pos_trail_dist = self.custom_stop['pos-trail-dist']

        open_minutes: int = (current_time - trade.open_date).total_seconds() // 60

        decay_stoploss = cta.linear_growth(start, end, delay, time, open_minutes)

        min_profit = trade.calc_profit_ratio(trade.min_rate)
        max_profit = trade.calc_profit_ratio(trade.max_rate)
        profit_diff = current_profit - min_profit

        if pos_trail == True:
            # if trade is positive above threshold, immediately move the stoploss up to x% behind it
            if current_profit > pos_threshold:
                return current_profit - pos_trail_dist

        # if we might be on a rebound, move the stoploss to the low point or keep it where it was
        if current_profit > min_profit:
            if profit_diff > 0.015:
                return min_profit
            return -1

        # otherwise return the time decaying stoploss
        return decay_stoploss
コード例 #3
0
    def populate_sell_trend(self, dataframe: DataFrame,
                            metadata: dict) -> DataFrame:
        params = self.get_pair_params(metadata['pair'], 'sell')
        trade_data = self.custom_trade_info[metadata['pair']]
        conditions = []

        # If we are in an active trade (which is the only way a sell can occur...)
        # This is needed to block backtest/hyperopt from hitting the profit stuff and erroring out.
        if trade_data['active_trade']:
            # Decay a loss cutoff point where we allow a sell to occur idea is this allows
            # a trade to go into the negative a little bit before we react to sell.
            loss_cutoff = cta.linear_growth(-0.03, 0, 0, 240,
                                            trade_data['open_minutes'])

            conditions.append((trade_data['current_profit'] < loss_cutoff))

            # Examine the state of our other trades and free_slots to inform the decision
            if trade_data['other_trades']:
                if trade_data['free_slots'] > 0:
                    # If the average of all our other trades is below a certain threshold based
                    # on free slots available, hold and wait for market recovery.
                    hold_pct = (1 / trade_data['free_slots']) * -0.04
                    conditions.append(
                        trade_data['avg_other_profit'] >= hold_pct)
                else:
                    # If we are out of free slots disregard the above and allow the biggest loser to sell.
                    conditions.append(trade_data['biggest_loser'] == True)

        conditions.append(
            dataframe['consensus_sell'] > params['consensus-sell'])

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

        return dataframe
コード例 #4
0
    def min_roi_reached_dynamic(
            self, trade: Trade, current_profit: float, current_time: datetime,
            trade_dur: int) -> Tuple[Optional[int], Optional[float]]:

        dynamic_roi = self.dynamic_roi
        minimal_roi = self.minimal_roi

        print(f"In the ROI place for pair: {trade.pair}")

        if not dynamic_roi or not minimal_roi:
            return None, None

        if self.custom_trade_info and trade and trade.pair in self.custom_trade_info:
            # If in backtest or hyperopt, get the indicator values out of the trades dict (Thanks @JoeSchr!)
            if self.dp and self.dp.runmode.value in ('backtest', 'hyperopt'):
                roc = self.custom_trade_info[
                    trade.pair]['roc'].loc[current_time]['roc']
                atr = self.custom_trade_info[
                    trade.pair]['atr'].loc[current_time]['atr']
                rmi_slow = self.custom_trade_info[
                    trade.pair]['rmi-slow'].loc[current_time]['rmi-slow']
                rmi_trend = self.custom_trade_info[trade.pair][
                    'rmi-up-trend'].loc[current_time]['rmi-up-trend']
            # Otherwise get it out of the dataframe
            else:
                dataframe, last_updated = self.dp.get_analyzed_dataframe(
                    pair=trade.pair, timeframe=self.timeframe)
                roc = dataframe['roc'].iat[-1]
                atr = dataframe['atr'].iat[-1]
                rmi_slow = dataframe['rmi-slow'].iat[-1]
                rmi_trend = dataframe['rmi-up-trend'].iat[-1]

            d = dynamic_roi
            profit_factor = (1 - (rmi_slow / d['profit-factor']))
            rmi_grow = cta.linear_growth(d['rmi-start'], d['rmi-end'],
                                         d['grow-delay'], d['grow-time'],
                                         trade_dur)
            max_profit = trade.calc_profit_ratio(trade.max_rate)
            open_rate = trade.open_rate

            atr_roi = max(0, ((open_rate + atr) / open_rate) - 1)
            roc_roi = max(0, (roc / 100))

            # If we observe a strong upward trend and our current profit is above the max (multiplied by a factor), hold
            if (current_profit > (max_profit * profit_factor)) and (
                    rmi_trend == 1) and (rmi_slow > rmi_grow):
                min_roi = 100
            # Otherwise fallback something else as specified
            else:
                if d['fallback'] == 'atr':
                    min_roi = atr_roi
                if d['fallback'] == 'roc':
                    min_roi = roc_roi
                else:
                    _, min_roi = self.min_roi_reached_entry(trade_dur)
        else:
            return self.min_roi_reached_entry(trade_dur)

        return trade_dur, min_roi
コード例 #5
0
    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        params = self.get_pair_params(metadata['pair'], 'sell')
        at = self.custom_active_trade
        trade_data = self.custom_trade_info[metadata['pair']]
        conditions = []
        
        """
        Previous Schism sell implementation has been *partially* replaced by custom_stoploss.
        Doing it there makes it able to be backtested more realistically but some of the functionality
        such as the ability to use other trades or the # of slots in our decision need to remain here.

        The current sell is meant to be a pre-stoploss bailout but most sells at a loss
        should happen due to the stoploss in live/dry.

        ** Only part of this signal functions in backtest/hyperopt. Can only be optimized in live/dry.
        ** Results in backtest/hyperopts will sell far more readily than in live due to the profit guards.
        """

        # If we are in an active trade (which is the only way a sell can occur...)
        # This is needed to block backtest/hyperopt from hitting the profit stuff and erroring out.
        if trade_data['active_trade']:  
            # Decay a loss cutoff point where we allow a sell to occur idea is this allows
            # a trade to go into the negative a little bit before we react to sell.
            loss_cutoff = cta.linear_growth(at['loss-start'], at['loss-end'], 
                                            at['loss-delay'], at['loss-time'], trade_data['open_minutes'])

            conditions.append((trade_data['current_profit'] < loss_cutoff))
            
            # Examine the state of our other trades and free_slots to inform the decision
            if trade_data['other_trades']:
                if trade_data['free_slots'] > 0:
                    # If the average of all our other trades is below a certain threshold based
                    # on free slots available, hold and wait for market recovery.
                    hold_pct = (1/trade_data['free_slots']) * at['sell-mkt-down']
                    conditions.append(trade_data['avg_other_profit'] >= hold_pct)
                else:
                    # If we are out of free slots disregard the above and allow the biggest loser to sell.
                    conditions.append(trade_data['biggest_loser'] == True)

        # Primary sell trigger
        rmi_drop = dataframe['rmi-max'] - (dataframe['rmi-max'] * params['sell-rmi-drop'])
        conditions.append(
            (dataframe['rmi-dn-trend'] == 1) &
            (qtpylib.crossed_below(dataframe['rmi-slow'], rmi_drop)) &
            (dataframe['volume'].gt(0))
        )

        # TODO: Evaluate PMAX in dry/live
        if params['sell-pmax-enable'] == True:
            conditions.append((dataframe['pmax-trend'] == 'down'))
             
        if conditions:
            dataframe.loc[
                reduce(lambda x, y: x & y, conditions),
                'sell'] = 1

        return dataframe
コード例 #6
0
    def custom_stoploss(self, pair: str, trade: 'Trade',
                        current_time: datetime, current_rate: float,
                        current_profit: float, **kwargs) -> float:
        cs = self.custom_stop

        trade_dur: int = (current_time - trade.open_date).total_seconds() // 60
        min_profit = trade.calc_profit_ratio(trade.min_rate)
        max_profit = trade.calc_profit_ratio(trade.max_rate)
        profit_diff = current_profit - min_profit

        decay_stoploss = cta.linear_growth(cs['decay-start'], cs['decay-end'],
                                           cs['decay-delay'], cs['decay-time'],
                                           trade_dur)

        # enable stoploss in positive profits after threshold to trail as specifed distance
        if cs['pos-trail'] == True:
            if current_profit > cs['pos-threshold']:
                return current_profit - cs['pos-trail-dist']

        # If in backtest or hyperopt, get the indicator values out of the trades dict (Thanks @JoeSchr!)
        if self.dp and self.dp.runmode.value in ('backtest', 'hyperopt'):
            roc = self.custom_trade_info[
                trade.pair]['roc'].loc[current_time]['roc']
            atr = self.custom_trade_info[
                trade.pair]['atr'].loc[current_time]['atr']
            rmi_slow = self.custom_trade_info[
                trade.pair]['rmi-slow'].loc[current_time]['rmi-slow']
            rmi_trend = self.custom_trade_info[
                trade.pair]['rmi-up-trend'].loc[current_time]['rmi-up-trend']
        # Otherwise get it out of the dataframe
        else:
            dataframe, last_updated = self.dp.get_analyzed_dataframe(
                pair=pair, timeframe=self.timeframe)
            roc = dataframe['roc'].iat[-1]
            atr = dataframe['atr'].iat[-1]
            rmi_slow = dataframe['rmi-slow'].iat[-1]
            rmi_trend = dataframe['rmi-up-trend'].iat[-1]

        if current_profit < cs['cur-threshold']:
            # Dynamic bailout based on rate of change
            if (roc / 100) < cs['cur-roc']:
                stoploss = (current_rate - atr) / current_rate
                return stoploss - 1

            # if we might be on a rebound, move the stoploss to the low point or keep it where it was
            if (current_profit > min_profit) or roc > 0:
                if profit_diff > cs['cur-min-diff']:
                    return min_profit
                return -1

        return decay_stoploss
コード例 #7
0
    def custom_stoploss(self, pair: str, trade: 'Trade',
                        current_time: datetime, current_rate: float,
                        current_profit: float, **kwargs) -> float:
        cs = self.custom_stop

        open_minutes: int = (current_time -
                             trade.open_date).total_seconds() // 60
        min_profit = trade.calc_profit_ratio(trade.min_rate)
        max_profit = trade.calc_profit_ratio(trade.max_rate)
        profit_diff = current_profit - min_profit

        # enable stoploss in positive profits after threshold to trail as specifed distance
        if cs['pos-trail'] == True:
            if current_profit > cs['pos-threshold']:
                return current_profit - cs['pos-trail-dist']

        # decay-only is literally just the decay, decay uses the decay unless profit is increasing
        if cs['mode'] == 'decay' or cs['mode'] == 'decay-only':
            decay_stoploss = cta.linear_growth(cs['decay-start'],
                                               cs['decay-end'],
                                               cs['decay-delay'],
                                               cs['decay-time'], open_minutes)
            if cs['mode'] == 'decay-only':
                return decay_stoploss

            # if we might be on a rebound, move the stoploss to the low point or keep it where it was
            if (current_profit >
                    min_profit) and current_profit < cs['cur-threshold']:
                if profit_diff > cs['cur-min-diff']:
                    return min_profit
                return -1

            return decay_stoploss

        if cs['mode'] == 'dynamic':
            if not pair in cs:
                cs[pair] = {}

            cs[pair]['current_profit']
            cs[pair]['min_profit']
            cs[pair]['max_profit']

        # if all else fails, keep the stoploss where it is
        return -1
コード例 #8
0
    def min_roi_reached_dynamic(
            self, trade: Trade, current_profit: float, current_time: datetime,
            trade_dur: int) -> Tuple[Optional[int], Optional[float]]:

        dynamic_roi = self.get_pair_params(trade.pair, 'dynamic_roi')
        minimal_roi = self.get_pair_params(trade.pair, 'minimal_roi')

        if not dynamic_roi or not minimal_roi:
            return None, None

        _, table_roi = self.min_roi_reached_entry(trade_dur, trade.pair)

        # 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)
                roc = dataframe['roc'].iat[-1]
                atr = dataframe['atr'].iat[-1]
                rmi_slow = dataframe['rmi-slow'].iat[-1]
                rmi_trend = dataframe['rmi-up-trend'].iat[-1]
            # If in backtest or hyperopt, get the indicator values out of the trades dict (Thanks @JoeSchr!)
            else:
                roc = self.custom_trade_info[
                    trade.pair]['roc'].loc[current_time]['roc']
                atr = self.custom_trade_info[
                    trade.pair]['atr'].loc[current_time]['atr']
                rmi_slow = self.custom_trade_info[
                    trade.pair]['rmi-slow'].loc[current_time]['rmi-slow']
                rmi_trend = self.custom_trade_info[trade.pair][
                    'rmi-up-trend'].loc[current_time]['rmi-up-trend']

            d = dynamic_roi
            open_rate = trade.open_rate
            max_profit = trade.calc_profit_ratio(trade.max_rate)

            atr_roi = max(d['min-roc-atr'],
                          ((open_rate + atr) / open_rate) - 1)
            roc_roi = max(d['min-roc-atr'], (roc / 100))

            # atr as the fallback (if > min-roc-atr)
            if d['fallback'] == 'atr':
                min_roi = atr_roi
            # roc as the fallback (if > min-roc-atr)
            elif d['fallback'] == 'roc':
                min_roi = roc_roi
            # atr or table as the fallback (whichever is larger)
            elif d['fallback'] == 'atr-table':
                min_roi = max(table_roi, atr_roi)
            # roc or table as the fallback (whichever is larger)
            elif d['fallback'] == 'roc-table':
                min_roi = max(table_roi, roc_roi)
            # default to table
            else:
                min_roi = table_roi

            rmi_grow = cta.linear_growth(d['rmi-start'], d['rmi-end'],
                                         d['grow-delay'], d['grow-time'],
                                         trade_dur)
            profit_factor = (1 - (rmi_slow / d['profit-factor']))
            pullback_buffer = (max_profit * profit_factor)

            # If we observe a strong upward trend and our current profit has not retreated from the peak by much, hold
            if (rmi_trend == 1) and (rmi_slow > rmi_grow):
                if (current_profit < pullback_buffer) and max_profit > min_roi:
                    # allow immediate bailout if we were above the ROI point and retreated below it
                    min_roi = current_profit / 2
                else:
                    min_roi = 100

        else:
            min_roi = table_roi

        return trade_dur, min_roi
コード例 #9
0
    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        params = self.get_pair_params(metadata['pair'], 'buy')
        at = self.custom_active_trade
        trade_data = self.custom_trade_info[metadata['pair']]
        conditions = []

        """
        The primary "secret sauce" of Solipsis is to take advantage of the ignore_roi_if_buy_signal setting.
        Ideally, the ROI table is very tight and aggressive allowing for quick exits on ROI without reliance 
        on a sell signal. However, if certain criteria are met for an open trade, we stimulate a sticking buy
        signal on purpose to prevent the bot from selling to the ROI in the midst of an upward trend.

        This is not currently able to be backtested or hyperopted so all settings here must be tested and validated
        in dry-run or live. It is safe to assume that when backtesting, all sells for reason roi will have likely been 
        for higher profits in live trading than in backtest.
        """

        # If active trade, look at trend to persist a buy signal for ignore_roi_if_buy_signal
        if trade_data['active_trade']:
            profit_factor = (1 - (dataframe['rmi-slow'].iloc[-1] / at['roi-pft-factor']))
            rmi_grow = cta.linear_growth(at['roi-rmi-start'], at['roi-rmi-end'], 
                                         at['roi-delay'], at['roi-time'], trade_data['open_minutes'])

            conditions.append(trade_data['current_profit'] > (trade_data['peak_profit'] * profit_factor))
            # TODO: Experiment with replacing or augmenting this with PMAX 
            conditions.append(
                (
                    (params['base-pmax-enable'] == True) &
                    (dataframe['pmax-trend'] == 'up')
                ) | (
                    (dataframe['rmi-up-trend'] == 1) &
                    (dataframe['rmi-slow'] >= rmi_grow)
                )
            )
        
        # Standard signals for entering new trades
        else:

            # Guards on informative timeframe
            if params['inf-guard'] == 'upper' or params['inf-guard'] == 'both':
                conditions.append(
                    (dataframe['close'] <= dataframe[f"3d_low_{self.inf_timeframe}"] + 
                    (params['inf-pct-adr-top'] * dataframe[f"adr_{self.inf_timeframe}"]))
                )

            if params['inf-guard'] == 'lower' or params['inf-guard'] == 'both':
                conditions.append(
                    (dataframe['close'] >= dataframe[f"3d_low_{self.inf_timeframe}"] + 
                    (params['inf-pct-adr-bot'] * dataframe[f"adr_{self.inf_timeframe}"]))
                )
   
            # Informative Timeframe
            conditions.append(
                (dataframe[f"rsi_{self.inf_timeframe}"] >= params['inf-rsi']) &
                (dataframe[f"fib-ret_{self.inf_timeframe}"] >= params['inf-fib-level'])
            )

            if params['base-pmax-enable'] == True:
                conditions.append((dataframe['pmax-trend'] == params['base-pmax']))

            # # Base Timeframe
            # Look for bottom of downward trend and expect bounce
            if params['base-trend'] == 'down':
                conditions.append(
                    (dataframe['fib-ret'] <= params['base-fib-level']) &
                    (dataframe['rmi-dn-trend'] == 1) &
                    (dataframe['rmi-slow'] >= params['base-rmi-slow']) &
                    (dataframe['rmi-fast'] <= params['base-rmi-fast']) &
                    (dataframe['mp'] <= params['base-mp'])
                )

            # Look for upward trend
            elif params['base-trend'] == 'up':
                conditions.append(
                    (dataframe['fib-ret'] >= params['base-fib-level']) &
                    (dataframe['rmi-up-trend'] == 1) &
                    (dataframe['rmi-slow'] <= params['base-rmi-slow']) &
                    (dataframe['rmi-fast'] >= params['base-rmi-fast']) &
                    (dataframe['mp'] >= params['base-mp'])
                )

            # Extra conditions for BTC/ETH stakes on additional informative pairs
            if self.config['stake_currency'] in ('BTC', 'ETH'):
                conditions.append(
                    (dataframe[f"{self.config['stake_currency']}_rsi"] < params['xtra-base-stake-rsi']) | 
                    (dataframe[f"{self.custom_fiat}_rsi"] > params['xtra-base-fiat-rsi'])
                )
                conditions.append(dataframe[f"{self.config['stake_currency']}_rmi_{self.inf_timeframe}"] < params['xtra-inf-stake-rmi'])

        # Applies to active or new trades
        conditions.append(dataframe['volume'].gt(0))

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

        return dataframe
コード例 #10
0
    def custom_stoploss(self, pair: str, trade: 'Trade',
                        current_time: datetime, current_rate: float,
                        current_profit: float, **kwargs) -> float:
        cs = self.custom_stop

        # Attempting to populate some of the trade data used elsewhere in the strategy with the trade object from here in backtest/hyperopt
        if self.dp and self.dp.runmode.value in ('backtest', 'hyperopt'):
            self.custom_trade_info[pair] = self.populate_trades(
                pair,
                trade=trade,
                time=current_time,
                rate=current_rate,
                profit=current_profit)

        trade_data = self.custom_trade_info[pair]

        print(f"Trade Data from Populate Trades: {trade_data}")

        open_minutes: int = (current_time -
                             trade.open_date).total_seconds() // 60
        min_profit = trade.calc_profit_ratio(
            trade.min_rate)  # min array no object?
        max_profit = trade.calc_profit_ratio(
            trade.max_rate)  # max array no object?
        profit_diff = current_profit - min_profit

        print(f"Trade object in stoploss {trade}")
        print(f"Open Minutes: {open_minutes}")
        print(f"Current Rate: {current_rate}")
        print(f"Current Profit: {current_profit}")
        print(f"Min Profit: {min_profit}")
        print(f"Max Profit: {max_profit}")

        # enable stoploss in positive profits after threshold to trail as specifed distance
        if cs['pos-trail'] == True:
            if current_profit > cs['pos-threshold']:
                return current_profit - cs['pos-trail-dist']

        # decay-only is literally just the decay, decay uses the decay unless profit is increasing
        if cs['mode'] == 'decay' or cs['mode'] == 'decay-only':
            decay_stoploss = cta.linear_growth(cs['decay-start'],
                                               cs['decay-end'],
                                               cs['decay-delay'],
                                               cs['decay-time'], open_minutes)
            if cs['mode'] == 'decay-only':
                return decay_stoploss

            # if we might be on a rebound, move the stoploss to the low point or keep it where it was
            if (current_profit >
                    min_profit) and current_profit < cs['cur-threshold']:
                if profit_diff > cs['cur-min-diff']:
                    return min_profit
                return -1

            return decay_stoploss

        if cs['mode'] == 'dynamic':
            if not pair in cs:
                cs[pair] = {}

            cs[pair]['current_profit']
            cs[pair]['min_profit']
            cs[pair]['max_profit']

        # if all else fails, keep the stoploss where it is
        return -1
コード例 #11
0
    def populate_buy_trend(self, dataframe: DataFrame,
                           metadata: dict) -> DataFrame:
        params = self.get_pair_params(metadata['pair'], 'buy')
        trade_data = self.custom_trade_info[metadata['pair']]
        conditions = []

        # If active trade, look at trend to persist a buy signal for ignore_roi_if_buy_signal
        if trade_data['active_trade']:
            profit_factor = (1 - (dataframe['rmi-slow'].iloc[-1] / 400))
            rmi_grow = cta.linear_growth(30, 70, 180, 720,
                                         trade_data['open_minutes'])

            conditions.append(
                trade_data['current_profit'] > (trade_data['peak_profit'] *
                                                profit_factor))
            conditions.append(dataframe['rmi-up-trend'] == 1)
            conditions.append(dataframe['rmi-slow'] >= rmi_grow)

        # Standard signals for entering new trades
        else:

            # Primary guards on informative timeframe to make sure we don't trade when market is peaked or bottomed out
            if params['inf-guard'] == 'upper' or params['inf-guard'] == 'both':
                conditions.append((dataframe['close'] <=
                                   dataframe[f"3d_low_{self.inf_timeframe}"] +
                                   (params['inf-pct-adr-top'] *
                                    dataframe[f"adr_{self.inf_timeframe}"])))

            if params['inf-guard'] == 'lower' or params['inf-guard'] == 'both':
                conditions.append((dataframe['close'] >=
                                   dataframe[f"3d_low_{self.inf_timeframe}"] +
                                   (params['inf-pct-adr-bot'] *
                                    dataframe[f"adr_{self.inf_timeframe}"])))

            # Base Timeframe
            conditions.append(
                (dataframe['rmi-dn-trend'] == 1)
                & (dataframe['rmi-slow'] >= params['base-rmi-slow'])
                & (dataframe['rmi-fast'] <= params['base-rmi-fast'])
                & (dataframe['mp'] <= params['base-mp']))

            # Extra conditions for */BTC and */ETH stakes on additional informative pairs
            if self.config['stake_currency'] in ('BTC', 'ETH'):
                conditions.append((
                    dataframe[f"{self.config['stake_currency']}_rmi"] <
                    params['xtra-base-stake-rmi'])
                                  | (dataframe[f"{self.custom_fiat}_rmi"] >
                                     params['xtra-base-fiat-rmi']))
                conditions.append(dataframe[
                    f"{self.config['stake_currency']}_rmi_{self.inf_timeframe}"]
                                  > params['xtra-inf-stake-rmi'])
            # Extra conditions 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:
                    conditions.append(
                        (dataframe['BTC_rmi'] < params['xbtc-base-rmi'])
                        & (dataframe[f"BTC_rmi_{self.inf_timeframe}"] >
                           params['xbtc-inf-rmi']))

        # Applies to active or new trades
        conditions.append(dataframe['volume'].gt(0))

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

        return dataframe
コード例 #12
0
    def min_roi_reached_dynamic(
            self, trade: Trade, current_profit: float, current_time: datetime,
            trade_dur: int) -> Tuple[Optional[int], Optional[float]]:

        dynamic_roi = self.get_pair_params(trade.pair, 'dynamic_roi')
        minimal_roi = self.get_pair_params(trade.pair, 'minimal_roi')

        if not dynamic_roi or not minimal_roi:
            return None, None

        _, table_roi = self.min_roi_reached_entry(trade_dur, trade.pair)

        # 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)
                roc = dataframe['roc'].iat[-1]
                atr = dataframe['atr'].iat[-1]
                rmi_slow = dataframe['rmi-slow'].iat[-1]
                rmi_trend = dataframe['rmi-up-trend'].iat[-1]
            # If in backtest or hyperopt, get the indicator values out of the trades dict (Thanks @JoeSchr!)
            else:
                roc = self.custom_trade_info[
                    trade.pair]['roc'].loc[current_time]['roc']
                atr = self.custom_trade_info[
                    trade.pair]['atr'].loc[current_time]['atr']
                rmi_slow = self.custom_trade_info[
                    trade.pair]['rmi-slow'].loc[current_time]['rmi-slow']
                rmi_trend = self.custom_trade_info[trade.pair][
                    'rmi-up-trend'].loc[current_time]['rmi-up-trend']

            d = dynamic_roi
            profit_factor = (1 - (rmi_slow / d['profit-factor']))
            rmi_grow = cta.linear_growth(d['rmi-start'], d['rmi-end'],
                                         d['grow-delay'], d['grow-time'],
                                         trade_dur)
            max_profit = trade.calc_profit_ratio(trade.max_rate)
            open_rate = trade.open_rate

            atr_roi = max(d['min-roc-atr'],
                          ((open_rate + atr) / open_rate) - 1)
            roc_roi = max(d['min-roc-atr'], (roc / 100))

            # atr as the fallback (if > min-roc-atr)
            if d['fallback'] == 'atr':
                min_roi = atr_roi
            # roc as the fallback (if > min-roc-atr)
            elif d['fallback'] == 'roc':
                min_roi = roc_roi
            # atr or table as the fallback (whichever is larger)
            elif d['fallback'] == 'atr-table':
                min_roi = max(table_roi, atr_roi)
            # roc or table as the fallback (whichever is larger)
            elif d['fallback'] == 'roc-table':
                min_roi = max(table_roi, roc_roi)
            # default to table
            else:
                min_roi = table_roi

            # If we observe a strong upward trend and our current profit has not retreated from the peak by much, hold
            if (rmi_trend == 1) and (rmi_slow > rmi_grow):
                if current_profit > min_roi and (current_profit <
                                                 (max_profit * profit_factor)):
                    min_roi = min_roi
                else:
                    min_roi = 100

        else:
            min_roi = table_roi
        """
        # Attempting to wedge the dynamic roi value into a thing so we can trick backtesting...
        if self.config['runmode'].value not in ('live', 'dry_run'):
            # Theoretically, if backtesting uses this value, ROI was triggered so we need to trick it with a sell
            # rate other than what is on the standard ROI table...
            self.custom_trade_info['backtest']['roi'] = max(min_roi, current_profit)
        """

        return trade_dur, min_roi