Fixed max_open_trades update from hyperopt

Fixed max_open_trades update from hyperopt + removed max_open_trades as a param to backtesting + refactoring
This commit is contained in:
Antonio Della Fortuna
2023-01-08 12:39:39 +01:00
parent 8c3ac56bc5
commit 464cb4761c
7 changed files with 132 additions and 52 deletions

View File

@@ -919,6 +919,7 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data: BTContainer)
default_conf["trailing_stop_positive"] = data.trailing_stop_positive
default_conf["trailing_stop_positive_offset"] = data.trailing_stop_positive_offset
default_conf["use_exit_signal"] = data.use_exit_signal
default_conf["max_open_trades"] = 10
mocker.patch("freqtrade.exchange.Exchange.get_fee", return_value=0.0)
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
@@ -951,7 +952,6 @@ def test_backtest_results(default_conf, fee, mocker, caplog, data: BTContainer)
processed=data_processed,
start_date=min_date,
end_date=max_date,
max_open_trades=10,
)
results = result['results']

View File

@@ -96,7 +96,6 @@ def _make_backtest_conf(mocker, datadir, conf=None, pair='UNITTEST/BTC'):
'processed': processed,
'start_date': min_date,
'end_date': max_date,
'max_open_trades': 10,
}
@@ -684,6 +683,8 @@ def test_backtest__get_sell_trade_entry(default_conf, fee, mocker) -> None:
def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
default_conf['use_exit_signal'] = False
default_conf['max_open_trades'] = 10
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'))
@@ -701,7 +702,6 @@ def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
processed=deepcopy(processed),
start_date=min_date,
end_date=max_date,
max_open_trades=10,
)
results = result['results']
assert not results.empty
@@ -785,6 +785,8 @@ def test_backtest_one_detail(default_conf_usdt, fee, mocker, testdatadir, use_de
def custom_entry_price(proposed_rate, **kwargs):
return proposed_rate * 0.997
default_conf_usdt['max_open_trades'] = 10
backtesting = Backtesting(default_conf_usdt)
backtesting._set_strategy(backtesting.strategylist[0])
backtesting.strategy.populate_entry_trend = advise_entry
@@ -805,7 +807,6 @@ def test_backtest_one_detail(default_conf_usdt, fee, mocker, testdatadir, use_de
processed=deepcopy(processed),
start_date=min_date,
end_date=max_date,
max_open_trades=10,
)
results = result['results']
assert not results.empty
@@ -859,6 +860,7 @@ def test_backtest_timedout_entry_orders(default_conf, fee, mocker, testdatadir)
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)
default_conf['max_open_trades'] = 1
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
# Testing dataframe contains 11 candles. Expecting 10 timed out orders.
@@ -871,7 +873,6 @@ def test_backtest_timedout_entry_orders(default_conf, fee, mocker, testdatadir)
processed=deepcopy(data),
start_date=min_date,
end_date=max_date,
max_open_trades=1,
)
assert result['timedout_entry_orders'] == 10
@@ -879,6 +880,7 @@ def test_backtest_timedout_entry_orders(default_conf, fee, mocker, testdatadir)
def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None:
default_conf['use_exit_signal'] = False
default_conf['max_open_trades'] = 1
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'))
@@ -896,7 +898,6 @@ def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None
processed=processed,
start_date=min_date,
end_date=max_date,
max_open_trades=1,
)
assert not results['results'].empty
assert len(results['results']) == 1
@@ -904,6 +905,8 @@ def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None
def test_backtest_trim_no_data_left(default_conf, fee, mocker, testdatadir) -> None:
default_conf['use_exit_signal'] = False
default_conf['max_open_trades'] = 10
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'))
@@ -927,7 +930,6 @@ def test_backtest_trim_no_data_left(default_conf, fee, mocker, testdatadir) -> N
processed=deepcopy(processed),
start_date=min_date,
end_date=max_date,
max_open_trades=10,
)
@@ -948,6 +950,7 @@ def test_processed(default_conf, mocker, testdatadir) -> None:
def test_backtest_dataprovider_analyzed_df(default_conf, fee, mocker, testdatadir) -> None:
default_conf['use_exit_signal'] = False
default_conf['max_open_trades'] = 10
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=100000)
@@ -981,7 +984,6 @@ def test_backtest_dataprovider_analyzed_df(default_conf, fee, mocker, testdatadi
processed=deepcopy(processed),
start_date=min_date,
end_date=max_date,
max_open_trades=10,
)
assert count == 5
@@ -998,6 +1000,7 @@ def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatad
default_conf['enable_protections'] = True
default_conf['timeframe'] = '1m'
default_conf['max_open_trades'] = 1
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'))
@@ -1024,7 +1027,6 @@ def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatad
processed=processed,
start_date=min_date,
end_date=max_date,
max_open_trades=1,
)
assert len(results['results']) == numres
@@ -1062,11 +1064,12 @@ def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir,
processed = backtesting.strategy.advise_all_indicators(data)
min_date, max_date = get_timerange(processed)
assert isinstance(processed, dict)
backtesting.strategy.max_open_trades = 1
backtesting.config.update({'max_open_trades': 1})
results = backtesting.backtest(
processed=processed,
start_date=min_date,
end_date=max_date,
max_open_trades=1,
)
assert len(results['results']) == expected
@@ -1077,7 +1080,7 @@ def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir):
buy_value = 1
sell_value = 1
return _trend(dataframe, buy_value, sell_value)
default_conf['max_open_trades'] = 10
backtest_conf = _make_backtest_conf(mocker, conf=default_conf, datadir=testdatadir)
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
@@ -1094,6 +1097,7 @@ def test_backtest_only_sell(mocker, default_conf, testdatadir):
sell_value = 1
return _trend(dataframe, buy_value, sell_value)
default_conf['max_open_trades'] = 10
backtest_conf = _make_backtest_conf(mocker, conf=default_conf, datadir=testdatadir)
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
@@ -1107,6 +1111,7 @@ def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir):
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'))
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
default_conf['max_open_trades'] = 10
backtest_conf = _make_backtest_conf(mocker, conf=default_conf,
pair='UNITTEST/BTC', datadir=testdatadir)
default_conf['timeframe'] = '1m'
@@ -1165,6 +1170,7 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
if tres > 0:
data[pair] = data[pair][tres:].reset_index()
default_conf['timeframe'] = '5m'
default_conf['max_open_trades'] = 3
backtesting = Backtesting(default_conf)
backtesting._set_strategy(backtesting.strategylist[0])
@@ -1173,11 +1179,11 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
processed = backtesting.strategy.advise_all_indicators(data)
min_date, max_date = get_timerange(processed)
backtest_conf = {
'processed': deepcopy(processed),
'start_date': min_date,
'end_date': max_date,
'max_open_trades': 3,
}
results = backtesting.backtest(**backtest_conf)
@@ -1195,11 +1201,12 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
backtesting.dataprovider.get_analyzed_dataframe('NXT/BTC', '5m')[0]
) == len(data['NXT/BTC']) - 1 - backtesting.strategy.startup_candle_count
backtesting.strategy.max_open_trades = 1
backtesting.config.update({'max_open_trades': 1})
backtest_conf = {
'processed': deepcopy(processed),
'start_date': min_date,
'end_date': max_date,
'max_open_trades': 1,
}
results = backtesting.backtest(**backtest_conf)
assert len(evaluate_result_multi(results['results'], '5m', 1)) == 0

View File

@@ -17,6 +17,7 @@ from tests.conftest import patch_exchange
def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) -> None:
default_conf['use_exit_signal'] = False
default_conf['max_open_trades'] = 10
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
mocker.patch('freqtrade.optimize.backtesting.amount_to_contract_precision',
lambda x, *args, **kwargs: round(x, 8))
@@ -41,7 +42,6 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) ->
processed=deepcopy(processed),
start_date=min_date,
end_date=max_date,
max_open_trades=10,
)
results = result['results']
assert not results.empty

View File

@@ -1,5 +1,6 @@
# pragma pylint: disable=missing-docstring,W0212,C0103
from datetime import datetime, timedelta
from functools import wraps
from pathlib import Path
from unittest.mock import ANY, MagicMock, PropertyMock
@@ -336,8 +337,7 @@ def test_start_calls_optimizer(mocker, hyperopt_conf, capsys) -> None:
assert dumper2.call_count == 1
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
assert hasattr(hyperopt, "max_open_trades")
assert hyperopt.max_open_trades == hyperopt_conf['max_open_trades']
assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf['max_open_trades']
assert hasattr(hyperopt.backtesting, "_position_stacking")
@@ -708,8 +708,7 @@ def test_simplified_interface_roi_stoploss(mocker, hyperopt_conf, capsys) -> Non
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
assert hasattr(hyperopt, "max_open_trades")
assert hyperopt.max_open_trades == hyperopt_conf['max_open_trades']
assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf['max_open_trades']
assert hasattr(hyperopt.backtesting, "_position_stacking")
@@ -782,8 +781,7 @@ def test_simplified_interface_buy(mocker, hyperopt_conf, capsys) -> None:
assert dumper2.call_count == 1
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
assert hasattr(hyperopt, "max_open_trades")
assert hyperopt.max_open_trades == hyperopt_conf['max_open_trades']
assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf['max_open_trades']
assert hasattr(hyperopt.backtesting, "_position_stacking")
@@ -825,8 +823,7 @@ def test_simplified_interface_sell(mocker, hyperopt_conf, capsys) -> None:
assert dumper2.call_count == 1
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
assert hasattr(hyperopt, "max_open_trades")
assert hyperopt.max_open_trades == hyperopt_conf['max_open_trades']
assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf['max_open_trades']
assert hasattr(hyperopt.backtesting, "_position_stacking")
@@ -880,7 +877,7 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None:
assert hyperopt.backtesting.strategy.buy_rsi.value == 35
assert hyperopt.backtesting.strategy.sell_rsi.value == 74
assert hyperopt.backtesting.strategy.protection_cooldown_lookback.value == 30
assert hyperopt.max_open_trades == 1
assert hyperopt.backtesting.strategy.max_open_trades == 1
buy_rsi_range = hyperopt.backtesting.strategy.buy_rsi.range
assert isinstance(buy_rsi_range, range)
# Range from 0 - 50 (inclusive)
@@ -891,7 +888,7 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None:
assert hyperopt.backtesting.strategy.protection_cooldown_lookback.value != 30
assert hyperopt.backtesting.strategy.buy_rsi.value != 35
assert hyperopt.backtesting.strategy.sell_rsi.value != 74
assert hyperopt.max_open_trades != 1
assert hyperopt.backtesting.strategy.max_open_trades != 1
hyperopt.custom_hyperopt.generate_estimator = lambda *args, **kwargs: 'ET1'
with pytest.raises(OperationalException, match="Estimator ET1 not supported."):
@@ -992,3 +989,75 @@ def test_SKDecimal():
assert space.transform([2.0]) == [200]
assert space.transform([1.0]) == [100]
assert space.transform([1.5, 1.6]) == [150, 160]
def test_stake_amount_unlimited_max_open_trades(mocker, hyperopt_conf, tmpdir, fee) -> None:
# This test is to ensure that unlimited max_open_trades are ignored for the backtesting
# if we have an unlimited stake amount
patch_exchange(mocker)
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
(Path(tmpdir) / 'hyperopt_results').mkdir(parents=True)
hyperopt_conf.update({
'strategy': 'HyperoptableStrategy',
'user_data_dir': Path(tmpdir),
'hyperopt_random_state': 42,
'spaces': ['trades'],
'stake_amount': 'unlimited'
})
hyperopt = Hyperopt(hyperopt_conf)
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._get_params_dict',
return_value={
'max_open_trades': -1
})
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
assert hyperopt.backtesting.strategy.max_open_trades == 1
hyperopt.start()
assert hyperopt.backtesting.strategy.max_open_trades == 1
def test_max_open_trades_consistency(mocker, hyperopt_conf, tmpdir, fee) -> None:
# This test is to ensure that max_open_trades is the same across all functions needing it
# after it has been changed from the hyperopt
patch_exchange(mocker)
mocker.patch('freqtrade.exchange.Exchange.get_fee', return_value=0)
(Path(tmpdir) / 'hyperopt_results').mkdir(parents=True)
hyperopt_conf.update({
'strategy': 'HyperoptableStrategy',
'user_data_dir': Path(tmpdir),
'hyperopt_random_state': 42,
'spaces': ['trades'],
'stake_amount': 'unlimited',
'dry_run_wallet': 8,
'available_capital': 8,
'dry_run': True,
'epochs': 1
})
hyperopt = Hyperopt(hyperopt_conf)
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
first_time_evaluated = False
def stake_amount_interceptor(func):
@wraps(func)
def wrapper(*args, **kwargs):
nonlocal first_time_evaluated
stake_amount = func(*args, **kwargs)
if first_time_evaluated is False:
assert stake_amount == 1
first_time_evaluated = True
return stake_amount
return wrapper
hyperopt.backtesting.wallets._calculate_unlimited_stake_amount = stake_amount_interceptor(
hyperopt.backtesting.wallets._calculate_unlimited_stake_amount)
hyperopt.start()
assert hyperopt.backtesting.strategy.max_open_trades == 8
assert hyperopt.config['max_open_trades'] == 8