Merge pull request #7596 from iprogger/fix/backtest-counting-available-slots
Fix counting available trade slots in backtesting.
This commit is contained in:
		| @@ -1123,6 +1123,7 @@ class Backtesting: | |||||||
|                     if self.manage_open_orders(t, current_time, row): |                     if self.manage_open_orders(t, current_time, row): | ||||||
|                         # Close trade |                         # Close trade | ||||||
|                         open_trade_count -= 1 |                         open_trade_count -= 1 | ||||||
|  |                         open_trade_count_start -= 1 | ||||||
|                         open_trades[pair].remove(t) |                         open_trades[pair].remove(t) | ||||||
|                         LocalTrade.trades_open.remove(t) |                         LocalTrade.trades_open.remove(t) | ||||||
|                         self.wallets.update() |                         self.wallets.update() | ||||||
|   | |||||||
| @@ -799,6 +799,35 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: | |||||||
|                 t["close_rate"], 6) < round(ln.iloc[0]["high"], 6)) |                 t["close_rate"], 6) < round(ln.iloc[0]["high"], 6)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_backtest_timedout_entry_orders(default_conf, fee, mocker, testdatadir) -> None: | ||||||
|  |     # This strategy intentionally places unfillable orders. | ||||||
|  |     default_conf['strategy'] = 'StrategyTestV3CustomEntryPrice' | ||||||
|  |     default_conf['startup_candle_count'] = 0 | ||||||
|  |     # Cancel unfilled order after 4 minutes on 5m timeframe. | ||||||
|  |     default_conf["unfilledtimeout"] = {"entry": 4} | ||||||
|  |     mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) | ||||||
|  |     mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) | ||||||
|  |     mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) | ||||||
|  |     patch_exchange(mocker) | ||||||
|  |     backtesting = Backtesting(default_conf) | ||||||
|  |     backtesting._set_strategy(backtesting.strategylist[0]) | ||||||
|  |     # Testing dataframe contains 11 candles. Expecting 10 timed out orders. | ||||||
|  |     timerange = TimeRange('date', 'date', 1517227800, 1517231100) | ||||||
|  |     data = history.load_data(datadir=testdatadir, timeframe='5m', pairs=['UNITTEST/BTC'], | ||||||
|  |                              timerange=timerange) | ||||||
|  |     min_date, max_date = get_timerange(data) | ||||||
|  |  | ||||||
|  |     result = backtesting.backtest( | ||||||
|  |         processed=deepcopy(data), | ||||||
|  |         start_date=min_date, | ||||||
|  |         end_date=max_date, | ||||||
|  |         max_open_trades=1, | ||||||
|  |         position_stacking=False, | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     assert result['timedout_entry_orders'] == 10 | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None: | def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None: | ||||||
|     default_conf['use_exit_signal'] = False |     default_conf['use_exit_signal'] = False | ||||||
|     mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) |     mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) | ||||||
|   | |||||||
| @@ -1457,6 +1457,7 @@ def test_api_strategies(botclient, tmpdir): | |||||||
|         'InformativeDecoratorTest', |         'InformativeDecoratorTest', | ||||||
|         'StrategyTestV2', |         'StrategyTestV2', | ||||||
|         'StrategyTestV3', |         'StrategyTestV3', | ||||||
|  |         'StrategyTestV3CustomEntryPrice', | ||||||
|         'StrategyTestV3Futures', |         'StrategyTestV3Futures', | ||||||
|         'freqai_test_classifier', |         'freqai_test_classifier', | ||||||
|         'freqai_test_multimodel_strat', |         'freqai_test_multimodel_strat', | ||||||
|   | |||||||
							
								
								
									
										37
									
								
								tests/strategy/strats/strategy_test_v3_custom_entry_price.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								tests/strategy/strats/strategy_test_v3_custom_entry_price.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | # pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement | ||||||
|  |  | ||||||
|  | from datetime import datetime | ||||||
|  | from typing import Optional | ||||||
|  |  | ||||||
|  | from pandas import DataFrame | ||||||
|  | from strategy_test_v3 import StrategyTestV3 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class StrategyTestV3CustomEntryPrice(StrategyTestV3): | ||||||
|  |     """ | ||||||
|  |     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. | ||||||
|  |     """ | ||||||
|  |     new_entry_price: float = 0.001 | ||||||
|  |  | ||||||
|  |     def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: | ||||||
|  |         return dataframe | ||||||
|  |  | ||||||
|  |     def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: | ||||||
|  |  | ||||||
|  |         dataframe.loc[ | ||||||
|  |             dataframe['volume'] > 0, | ||||||
|  |             'enter_long'] = 1 | ||||||
|  |  | ||||||
|  |         return dataframe | ||||||
|  |  | ||||||
|  |     def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: | ||||||
|  |         return dataframe | ||||||
|  |  | ||||||
|  |     def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float, | ||||||
|  |                            entry_tag: Optional[str], side: str, **kwargs) -> float: | ||||||
|  |  | ||||||
|  |         return self.new_entry_price | ||||||
| @@ -34,7 +34,7 @@ def test_search_all_strategies_no_failed(): | |||||||
|     directory = Path(__file__).parent / "strats" |     directory = Path(__file__).parent / "strats" | ||||||
|     strategies = StrategyResolver._search_all_objects(directory, enum_failed=False) |     strategies = StrategyResolver._search_all_objects(directory, enum_failed=False) | ||||||
|     assert isinstance(strategies, list) |     assert isinstance(strategies, list) | ||||||
|     assert len(strategies) == 9 |     assert len(strategies) == 10 | ||||||
|     assert isinstance(strategies[0], dict) |     assert isinstance(strategies[0], dict) | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -42,10 +42,11 @@ def test_search_all_strategies_with_failed(): | |||||||
|     directory = Path(__file__).parent / "strats" |     directory = Path(__file__).parent / "strats" | ||||||
|     strategies = StrategyResolver._search_all_objects(directory, enum_failed=True) |     strategies = StrategyResolver._search_all_objects(directory, enum_failed=True) | ||||||
|     assert isinstance(strategies, list) |     assert isinstance(strategies, list) | ||||||
|     assert len(strategies) == 10 |     assert len(strategies) == 11 | ||||||
|     # with enum_failed=True search_all_objects() shall find 2 good strategies |     # with enum_failed=True search_all_objects() shall find 2 good strategies | ||||||
|     # and 1 which fails to load |     # and 1 which fails to load | ||||||
|     assert len([x for x in strategies if x['class'] is not None]) == 9 |     assert len([x for x in strategies if x['class'] is not None]) == 10 | ||||||
|  |  | ||||||
|     assert len([x for x in strategies if x['class'] is None]) == 1 |     assert len([x for x in strategies if x['class'] is None]) == 1 | ||||||
|  |  | ||||||
|     directory = Path(__file__).parent / "strats_nonexistingdir" |     directory = Path(__file__).parent / "strats_nonexistingdir" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user