Support multis-strategy backtests with protections
This commit is contained in:
		| @@ -120,8 +120,10 @@ class Backtesting: | ||||
|             self.fee = self.exchange.get_fee(symbol=self.pairlists.whitelist[0]) | ||||
|  | ||||
|         Trade.use_db = False | ||||
|         Trade.reset_trades() | ||||
|         PairLocks.timeframe = self.config['timeframe'] | ||||
|         PairLocks.use_db = False | ||||
|         PairLocks.reset_locks() | ||||
|         if self.config.get('enable_protections', False): | ||||
|             self.protections = ProtectionManager(self.config) | ||||
|  | ||||
| @@ -130,6 +132,11 @@ class Backtesting: | ||||
|         # Load one (first) strategy | ||||
|         self._set_strategy(self.strategylist[0]) | ||||
|  | ||||
|     def __del__(self): | ||||
|         LoggingMixin.show_output = True | ||||
|         PairLocks.use_db = True | ||||
|         Trade.use_db = True | ||||
|  | ||||
|     def _set_strategy(self, strategy): | ||||
|         """ | ||||
|         Load strategy into backtesting | ||||
| @@ -321,6 +328,13 @@ class Backtesting: | ||||
|                      f"max_open_trades: {max_open_trades}, position_stacking: {position_stacking}" | ||||
|                      ) | ||||
|         trades = [] | ||||
|         PairLocks.use_db = False | ||||
|         Trade.use_db = False | ||||
|         if enable_protections: | ||||
|             # Reset persisted data - used for protections only | ||||
|  | ||||
|             PairLocks.reset_locks() | ||||
|             Trade.reset_trades() | ||||
|  | ||||
|         # Use dict of lists with data for performance | ||||
|         # (looping lists is a lot faster than pandas DataFrames) | ||||
|   | ||||
| @@ -327,6 +327,14 @@ class Trade(_DECL_BASE): | ||||
|             'open_order_id': self.open_order_id, | ||||
|         } | ||||
|  | ||||
|     @staticmethod | ||||
|     def reset_trades() -> None: | ||||
|         """ | ||||
|         Resets all trades. Only active for backtesting mode. | ||||
|         """ | ||||
|         if not Trade.use_db: | ||||
|             Trade.trades = [] | ||||
|  | ||||
|     def adjust_min_max_rates(self, current_price: float) -> None: | ||||
|         """ | ||||
|         Adjust the max_rate and min_rate. | ||||
|   | ||||
| @@ -21,6 +21,14 @@ class PairLocks(): | ||||
|  | ||||
|     timeframe: str = '' | ||||
|  | ||||
|     @staticmethod | ||||
|     def reset_locks() -> None: | ||||
|         """ | ||||
|         Resets all locks. Only active for backtesting mode. | ||||
|         """ | ||||
|         if not PairLocks.use_db: | ||||
|             PairLocks.locks = [] | ||||
|  | ||||
|     @staticmethod | ||||
|     def lock_pair(pair: str, until: datetime, reason: str = None, *, now: datetime = None) -> None: | ||||
|         """ | ||||
|   | ||||
| @@ -55,8 +55,8 @@ class StoplossGuard(IProtection): | ||||
|         # trades = Trade.get_trades(filters).all() | ||||
|  | ||||
|         trades1 = Trade.get_trades_proxy(pair=pair, is_open=False, close_date=look_back_until) | ||||
|         trades = [trade for trade in trades1 if trade.sell_reason == SellType.STOP_LOSS | ||||
|                   or (trade.sell_reason == SellType.TRAILING_STOP_LOSS | ||||
|         trades = [trade for trade in trades1 if str(trade.sell_reason) == SellType.STOP_LOSS.value | ||||
|                   or (str(trade.sell_reason) == SellType.TRAILING_STOP_LOSS.value | ||||
|                       and trade.close_profit < 0)] | ||||
|  | ||||
|         if len(trades) > self._trade_limit: | ||||
|   | ||||
| @@ -95,6 +95,7 @@ def simple_backtest(config, contour, num_results, mocker, testdatadir) -> None: | ||||
|         end_date=max_date, | ||||
|         max_open_trades=1, | ||||
|         position_stacking=False, | ||||
|         enable_protections=config.get('enable_protections', False), | ||||
|     ) | ||||
|     # results :: <class 'pandas.core.frame.DataFrame'> | ||||
|     assert len(results) == num_results | ||||
| @@ -532,10 +533,39 @@ def test_processed(default_conf, mocker, testdatadir) -> None: | ||||
|  | ||||
|  | ||||
| def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir) -> None: | ||||
|     # TODO: Evaluate usefullness of this, the patterns and buy-signls are unrealistic | ||||
|     mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) | ||||
|     tests = [['raise', 19], ['lower', 0], ['sine', 35]] | ||||
|     tests = [ | ||||
|         ['sine', 35], | ||||
|         ['raise', 19], | ||||
|         ['lower', 0], | ||||
|         ['sine', 35], | ||||
|         ['raise', 19] | ||||
|         ] | ||||
|     # While buy-signals are unrealistic, running backtesting | ||||
|     # over and over again should not cause different results | ||||
|     for [contour, numres] in tests: | ||||
|         simple_backtest(default_conf, contour, numres, mocker, testdatadir) | ||||
|  | ||||
|  | ||||
| def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatadir) -> None: | ||||
|     # TODO: Evaluate usefullness of this, the patterns and buy-signls are unrealistic | ||||
|     default_conf['protections'] = [ | ||||
|         { | ||||
|             "method": "CooldownPeriod", | ||||
|             "stop_duration": 3, | ||||
|         }] | ||||
|  | ||||
|     default_conf['enable_protections'] = True | ||||
|     mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) | ||||
|     tests = [ | ||||
|         ['sine', 9], | ||||
|         ['raise', 10], | ||||
|         ['lower', 0], | ||||
|         ['sine', 9], | ||||
|         ['raise', 10], | ||||
|     ] | ||||
|     # While buy-signals are unrealistic, running backtesting | ||||
|     # over and over again should not cause different results | ||||
|     for [contour, numres] in tests: | ||||
|         simple_backtest(default_conf, contour, numres, mocker, testdatadir) | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user