def _set_strategy(self, strategy: IStrategy):
     """
     Load strategy into backtesting
     """
     self.strategy: IStrategy = strategy
     strategy.dp = self.dataprovider
     # Attach Wallets to Strategy baseclass
     strategy.wallets = self.wallets
     # Set stoploss_on_exchange to false for backtesting,
     # since a "perfect" stoploss-sell is assumed anyway
     # And the regular "stoploss" function would not apply to that case
     self.strategy.order_types['stoploss_on_exchange'] = False
Example #2
0
    def backtest_one_strategy(self, strat: IStrategy, data: Dict[str, DataFrame],
                              timerange: TimeRange):
        self.progress.init_step(BacktestState.ANALYZE, 0)

        logger.info("Running backtesting for Strategy %s", strat.get_strategy_name())
        backtest_start_time = datetime.now(timezone.utc)
        self._set_strategy(strat)

        strategy_safe_wrapper(self.strategy.bot_loop_start, supress_error=True)()

        # Use max_open_trades in backtesting, except --disable-max-market-positions is set
        if self.config.get('use_max_market_positions', True):
            # Must come from strategy config, as the strategy may modify this setting.
            max_open_trades = self.strategy.config['max_open_trades']
        else:
            logger.info(
                'Ignoring max_open_trades (--disable-max-market-positions was used) ...')
            max_open_trades = 0

        # need to reprocess data every time to populate signals
        preprocessed = self.strategy.advise_all_indicators(data)

        # Trim startup period from analyzed dataframe
        preprocessed_tmp = trim_dataframes(preprocessed, timerange, self.required_startup)

        if not preprocessed_tmp:
            raise OperationalException(
                "No data left after adjusting for startup candles.")

        # Use preprocessed_tmp for date generation (the trimmed dataframe).
        # Backtesting will re-trim the dataframes after buy/sell signal generation.
        min_date, max_date = history.get_timerange(preprocessed_tmp)
        logger.info(f'Backtesting with data from {min_date.strftime(DATETIME_PRINT_FORMAT)} '
                    f'up to {max_date.strftime(DATETIME_PRINT_FORMAT)} '
                    f'({(max_date - min_date).days} days).')
        # Execute backtest and store results
        results = self.backtest(
            processed=preprocessed,
            start_date=min_date,
            end_date=max_date,
            max_open_trades=max_open_trades,
            position_stacking=self.config.get('position_stacking', False),
            enable_protections=self.config.get('enable_protections', False),
        )
        backtest_end_time = datetime.now(timezone.utc)
        results.update({
            'run_id': self.run_ids.get(strat.get_strategy_name(), ''),
            'backtest_start_time': int(backtest_start_time.timestamp()),
            'backtest_end_time': int(backtest_end_time.timestamp()),
        })
        self.all_results[self.strategy.get_strategy_name()] = results

        return min_date, max_date
 def _normalize_attributes(strategy: IStrategy) -> IStrategy:
     """
     Normalize attributes to have the correct type.
     """
     # Sort and apply type conversions
     if hasattr(strategy, 'minimal_roi'):
         strategy.minimal_roi = dict(
             sorted({
                 int(key): value
                 for (key, value) in strategy.minimal_roi.items()
             }.items(),
                    key=lambda t: t[0]))
     if hasattr(strategy, 'stoploss'):
         strategy.stoploss = float(strategy.stoploss)
     return strategy
Example #4
0
    def _normalize_attributes(strategy: IStrategy) -> IStrategy:
        """
        Normalize attributes to have the correct type.
        """
        # Assign deprecated variable - to not break users code relying on this.
        if hasattr(strategy, 'timeframe'):
            strategy.ticker_interval = strategy.timeframe

        # Sort and apply type conversions
        if hasattr(strategy, 'minimal_roi'):
            strategy.minimal_roi = dict(sorted(
                {int(key): value for (key, value) in strategy.minimal_roi.items()}.items(),
                key=lambda t: t[0]))
        if hasattr(strategy, 'stoploss'):
            strategy.stoploss = float(strategy.stoploss)
        return strategy
Example #5
0
    def backtest_one_strategy(self, strat: IStrategy, data: Dict[str, Any],
                              timerange: TimeRange):
        logger.info("Running backtesting for Strategy %s",
                    strat.get_strategy_name())
        backtest_start_time = datetime.now(timezone.utc)
        self._set_strategy(strat)

        strategy_safe_wrapper(self.strategy.bot_loop_start,
                              supress_error=True)()

        # Use max_open_trades in backtesting, except --disable-max-market-positions is set
        if self.config.get('use_max_market_positions', True):
            # Must come from strategy config, as the strategy may modify this setting.
            max_open_trades = self.strategy.config['max_open_trades']
        else:
            logger.info(
                'Ignoring max_open_trades (--disable-max-market-positions was used) ...'
            )
            max_open_trades = 0

        # need to reprocess data every time to populate signals
        preprocessed = self.strategy.ohlcvdata_to_dataframe(data)

        # Trim startup period from analyzed dataframe
        for pair, df in preprocessed.items():
            preprocessed[pair] = trim_dataframe(
                df, timerange, startup_candles=self.required_startup)
        min_date, max_date = history.get_timerange(preprocessed)

        logger.info(
            f'Backtesting with data from {min_date.strftime(DATETIME_PRINT_FORMAT)} '
            f'up to {max_date.strftime(DATETIME_PRINT_FORMAT)} '
            f'({(max_date - min_date).days} days)..')
        # Execute backtest and store results
        results = self.backtest(
            processed=preprocessed,
            start_date=min_date.datetime,
            end_date=max_date.datetime,
            max_open_trades=max_open_trades,
            position_stacking=self.config.get('position_stacking', False),
            enable_protections=self.config.get('enable_protections', False),
        )
        backtest_end_time = datetime.now(timezone.utc)
        self.all_results[self.strategy.get_strategy_name()] = {
            'results':
            results,
            'config':
            self.strategy.config,
            'locks':
            PairLocks.get_all_locks(),
            'final_balance':
            self.wallets.get_total(self.strategy.config['stake_currency']),
            'backtest_start_time':
            int(backtest_start_time.timestamp()),
            'backtest_end_time':
            int(backtest_end_time.timestamp()),
        }
        return min_date, max_date
Example #6
0
    def backtest_one_strategy(self, strat: IStrategy, data: Dict[str, Any], timerange: TimeRange):
        logger.info("Running backtesting for Strategy %s", strat.get_strategy_name())
        backtest_start_time = datetime.now(timezone.utc)
        self._set_strategy(strat)

        strategy_safe_wrapper(self.strategy.bot_loop_start, supress_error=True)()

        # Use max_open_trades in backtesting, except --disable-max-market-positions is set
        if self.config.get('use_max_market_positions', True):
            # Must come from strategy config, as the strategy may modify this setting.
            max_open_trades = self.strategy.config['max_open_trades']
        else:
            logger.info(
                'Ignoring max_open_trades (--disable-max-market-positions was used) ...')
            max_open_trades = 0

        # need to reprocess data every time to populate signals
        preprocessed = self.strategy.ohlcvdata_to_dataframe(data)

        # Trim startup period from analyzed dataframe
        for pair in list(preprocessed):
            df = preprocessed[pair]
            df = trim_dataframe(df, timerange, startup_candles=self.required_startup)
            if len(df) > 0:
                preprocessed[pair] = df
            else:
                logger.warning(f'{pair} has no data left after adjusting for startup candles, '
                               f'skipping.')
                del preprocessed[pair]

        if not preprocessed:
            raise OperationalException(
                "No data left after adjusting for startup candles.")

        min_date, max_date = history.get_timerange(preprocessed)
        logger.info(f'Backtesting with data from {min_date.strftime(DATETIME_PRINT_FORMAT)} '
                    f'up to {max_date.strftime(DATETIME_PRINT_FORMAT)} '
                    f'({(max_date - min_date).days} days).')
        # Execute backtest and store results
        results = self.backtest(
            processed=preprocessed,
            start_date=min_date,
            end_date=max_date,
            max_open_trades=max_open_trades,
            position_stacking=self.config.get('position_stacking', False),
            enable_protections=self.config.get('enable_protections', False),
        )
        backtest_end_time = datetime.now(timezone.utc)
        results.update({
            'backtest_start_time': int(backtest_start_time.timestamp()),
            'backtest_end_time': int(backtest_end_time.timestamp()),
        })
        self.all_results[self.strategy.get_strategy_name()] = results

        return min_date, max_date
Example #7
0
 def _set_strategy(self, strategy: IStrategy):
     """
     Load strategy into backtesting
     """
     self.strategy: IStrategy = strategy
     strategy.dp = self.dataprovider
     # Set stoploss_on_exchange to false for backtesting,
     # since a "perfect" stoploss-sell is assumed anyway
     # And the regular "stoploss" function would not apply to that case
     self.strategy.order_types['stoploss_on_exchange'] = False
     if self.config.get('enable_protections', False):
         conf = self.config
         if hasattr(strategy, 'protections'):
             conf = deepcopy(conf)
             conf['protections'] = strategy.protections
         self.protections = ProtectionManager(conf)
    def validate_strategy(strategy: IStrategy) -> IStrategy:
        if strategy.config.get('trading_mode',
                               TradingMode.SPOT) != TradingMode.SPOT:
            # Require new method
            warn_deprecated_setting(strategy, 'sell_profit_only',
                                    'exit_profit_only', True)
            warn_deprecated_setting(strategy, 'sell_profit_offset',
                                    'exit_profit_offset', True)
            warn_deprecated_setting(strategy, 'use_sell_signal',
                                    'use_exit_signal', True)
            warn_deprecated_setting(strategy, 'ignore_roi_if_buy_signal',
                                    'ignore_roi_if_entry_signal', True)

            if not check_override(strategy, IStrategy, 'populate_entry_trend'):
                raise OperationalException(
                    "`populate_entry_trend` must be implemented.")
            if not check_override(strategy, IStrategy, 'populate_exit_trend'):
                raise OperationalException(
                    "`populate_exit_trend` must be implemented.")
            if check_override(strategy, IStrategy, 'check_buy_timeout'):
                raise OperationalException(
                    "Please migrate your implementation "
                    "of `check_buy_timeout` to `check_entry_timeout`.")
            if check_override(strategy, IStrategy, 'check_sell_timeout'):
                raise OperationalException(
                    "Please migrate your implementation "
                    "of `check_sell_timeout` to `check_exit_timeout`.")

            if check_override(strategy, IStrategy, 'custom_sell'):
                raise OperationalException(
                    "Please migrate your implementation of `custom_sell` to `custom_exit`."
                )

        else:
            # TODO: Implementing one of the following methods should show a deprecation warning
            #  buy_trend and sell_trend, custom_sell
            warn_deprecated_setting(strategy, 'sell_profit_only',
                                    'exit_profit_only')
            warn_deprecated_setting(strategy, 'sell_profit_offset',
                                    'exit_profit_offset')
            warn_deprecated_setting(strategy, 'use_sell_signal',
                                    'use_exit_signal')
            warn_deprecated_setting(strategy, 'ignore_roi_if_buy_signal',
                                    'ignore_roi_if_entry_signal')

            if (not check_override(strategy, IStrategy, 'populate_buy_trend')
                    and not check_override(strategy, IStrategy,
                                           'populate_entry_trend')):
                raise OperationalException(
                    "`populate_entry_trend` or `populate_buy_trend` must be implemented."
                )
            if (not check_override(strategy, IStrategy, 'populate_sell_trend')
                    and not check_override(strategy, IStrategy,
                                           'populate_exit_trend')):
                raise OperationalException(
                    "`populate_exit_trend` or `populate_sell_trend` must be implemented."
                )

            strategy._populate_fun_len = len(
                getfullargspec(strategy.populate_indicators).args)
            strategy._buy_fun_len = len(
                getfullargspec(strategy.populate_buy_trend).args)
            strategy._sell_fun_len = len(
                getfullargspec(strategy.populate_sell_trend).args)
            if any(x == 2 for x in [
                    strategy._populate_fun_len, strategy._buy_fun_len,
                    strategy._sell_fun_len
            ]):
                strategy.INTERFACE_VERSION = 1
        return strategy