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]) |             self.fee = self.exchange.get_fee(symbol=self.pairlists.whitelist[0]) | ||||||
|  |  | ||||||
|         Trade.use_db = False |         Trade.use_db = False | ||||||
|  |         Trade.reset_trades() | ||||||
|         PairLocks.timeframe = self.config['timeframe'] |         PairLocks.timeframe = self.config['timeframe'] | ||||||
|         PairLocks.use_db = False |         PairLocks.use_db = False | ||||||
|  |         PairLocks.reset_locks() | ||||||
|         if self.config.get('enable_protections', False): |         if self.config.get('enable_protections', False): | ||||||
|             self.protections = ProtectionManager(self.config) |             self.protections = ProtectionManager(self.config) | ||||||
|  |  | ||||||
| @@ -130,6 +132,11 @@ class Backtesting: | |||||||
|         # Load one (first) strategy |         # Load one (first) strategy | ||||||
|         self._set_strategy(self.strategylist[0]) |         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): |     def _set_strategy(self, strategy): | ||||||
|         """ |         """ | ||||||
|         Load strategy into backtesting |         Load strategy into backtesting | ||||||
| @@ -321,6 +328,13 @@ class Backtesting: | |||||||
|                      f"max_open_trades: {max_open_trades}, position_stacking: {position_stacking}" |                      f"max_open_trades: {max_open_trades}, position_stacking: {position_stacking}" | ||||||
|                      ) |                      ) | ||||||
|         trades = [] |         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 |         # Use dict of lists with data for performance | ||||||
|         # (looping lists is a lot faster than pandas DataFrames) |         # (looping lists is a lot faster than pandas DataFrames) | ||||||
|   | |||||||
| @@ -327,6 +327,14 @@ class Trade(_DECL_BASE): | |||||||
|             'open_order_id': self.open_order_id, |             '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: |     def adjust_min_max_rates(self, current_price: float) -> None: | ||||||
|         """ |         """ | ||||||
|         Adjust the max_rate and min_rate. |         Adjust the max_rate and min_rate. | ||||||
|   | |||||||
| @@ -21,6 +21,14 @@ class PairLocks(): | |||||||
|  |  | ||||||
|     timeframe: str = '' |     timeframe: str = '' | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|  |     def reset_locks() -> None: | ||||||
|  |         """ | ||||||
|  |         Resets all locks. Only active for backtesting mode. | ||||||
|  |         """ | ||||||
|  |         if not PairLocks.use_db: | ||||||
|  |             PairLocks.locks = [] | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def lock_pair(pair: str, until: datetime, reason: str = None, *, now: datetime = None) -> None: |     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() |         # trades = Trade.get_trades(filters).all() | ||||||
|  |  | ||||||
|         trades1 = Trade.get_trades_proxy(pair=pair, is_open=False, close_date=look_back_until) |         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 |         trades = [trade for trade in trades1 if str(trade.sell_reason) == SellType.STOP_LOSS.value | ||||||
|                   or (trade.sell_reason == SellType.TRAILING_STOP_LOSS |                   or (str(trade.sell_reason) == SellType.TRAILING_STOP_LOSS.value | ||||||
|                       and trade.close_profit < 0)] |                       and trade.close_profit < 0)] | ||||||
|  |  | ||||||
|         if len(trades) > self._trade_limit: |         if len(trades) > self._trade_limit: | ||||||
|   | |||||||
| @@ -95,6 +95,7 @@ def simple_backtest(config, contour, num_results, mocker, testdatadir) -> None: | |||||||
|         end_date=max_date, |         end_date=max_date, | ||||||
|         max_open_trades=1, |         max_open_trades=1, | ||||||
|         position_stacking=False, |         position_stacking=False, | ||||||
|  |         enable_protections=config.get('enable_protections', False), | ||||||
|     ) |     ) | ||||||
|     # results :: <class 'pandas.core.frame.DataFrame'> |     # results :: <class 'pandas.core.frame.DataFrame'> | ||||||
|     assert len(results) == num_results |     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: | 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) |     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: |     for [contour, numres] in tests: | ||||||
|         simple_backtest(default_conf, contour, numres, mocker, testdatadir) |         simple_backtest(default_conf, contour, numres, mocker, testdatadir) | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user