Merge pull request #7596 from iprogger/fix/backtest-counting-available-slots

Fix counting available trade slots in backtesting.
This commit is contained in:
Matthias 2022-10-16 16:45:09 +02:00 committed by GitHub
commit 2b70106019
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 72 additions and 3 deletions

View File

@ -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()

View File

@ -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)

View File

@ -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',

View 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

View File

@ -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"