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:
parent
8c3ac56bc5
commit
464cb4761c
@ -252,7 +252,7 @@ AVAILABLE_CLI_OPTIONS = {
|
|||||||
'--spaces',
|
'--spaces',
|
||||||
help='Specify which parameters to hyperopt. Space-separated list.',
|
help='Specify which parameters to hyperopt. Space-separated list.',
|
||||||
choices=['all', 'buy', 'sell', 'roi', 'stoploss',
|
choices=['all', 'buy', 'sell', 'roi', 'stoploss',
|
||||||
'trailing', 'protection', 'default', 'trades'],
|
'trailing', 'protection', 'trades', 'default'],
|
||||||
nargs='+',
|
nargs='+',
|
||||||
default='default',
|
default='default',
|
||||||
),
|
),
|
||||||
|
@ -920,8 +920,9 @@ class Backtesting:
|
|||||||
trade.close(exit_row[OPEN_IDX], show_msg=False)
|
trade.close(exit_row[OPEN_IDX], show_msg=False)
|
||||||
LocalTrade.close_bt_trade(trade)
|
LocalTrade.close_bt_trade(trade)
|
||||||
|
|
||||||
def trade_slot_available(self, max_open_trades: int, open_trade_count: int) -> bool:
|
def trade_slot_available(self, open_trade_count: int) -> bool:
|
||||||
# Always allow trades when max_open_trades is enabled.
|
# Always allow trades when max_open_trades is enabled.
|
||||||
|
max_open_trades = self.config['max_open_trades']
|
||||||
if max_open_trades <= 0 or open_trade_count < max_open_trades:
|
if max_open_trades <= 0 or open_trade_count < max_open_trades:
|
||||||
return True
|
return True
|
||||||
# Rejected trade
|
# Rejected trade
|
||||||
@ -1051,7 +1052,6 @@ class Backtesting:
|
|||||||
|
|
||||||
def backtest_loop(
|
def backtest_loop(
|
||||||
self, row: Tuple, pair: str, current_time: datetime, end_date: datetime,
|
self, row: Tuple, pair: str, current_time: datetime, end_date: datetime,
|
||||||
max_open_trades: int,
|
|
||||||
open_trade_count_start: int, is_first: bool = True) -> int:
|
open_trade_count_start: int, is_first: bool = True) -> int:
|
||||||
"""
|
"""
|
||||||
NOTE: This method is used by Hyperopt at each iteration. Please keep it optimized.
|
NOTE: This method is used by Hyperopt at each iteration. Please keep it optimized.
|
||||||
@ -1075,7 +1075,7 @@ class Backtesting:
|
|||||||
if (
|
if (
|
||||||
(self._position_stacking or len(LocalTrade.bt_trades_open_pp[pair]) == 0)
|
(self._position_stacking or len(LocalTrade.bt_trades_open_pp[pair]) == 0)
|
||||||
and is_first
|
and is_first
|
||||||
and self.trade_slot_available(max_open_trades, open_trade_count_start)
|
and self.trade_slot_available(open_trade_count_start)
|
||||||
and current_time != end_date
|
and current_time != end_date
|
||||||
and trade_dir is not None
|
and trade_dir is not None
|
||||||
and not PairLocks.is_pair_locked(pair, row[DATE_IDX], trade_dir)
|
and not PairLocks.is_pair_locked(pair, row[DATE_IDX], trade_dir)
|
||||||
@ -1122,8 +1122,7 @@ class Backtesting:
|
|||||||
return open_trade_count_start
|
return open_trade_count_start
|
||||||
|
|
||||||
def backtest(self, processed: Dict,
|
def backtest(self, processed: Dict,
|
||||||
start_date: datetime, end_date: datetime,
|
start_date: datetime, end_date: datetime) -> Dict[str, Any]:
|
||||||
max_open_trades: int = 0) -> Dict[str, Any]:
|
|
||||||
"""
|
"""
|
||||||
Implement backtesting functionality
|
Implement backtesting functionality
|
||||||
|
|
||||||
@ -1135,7 +1134,6 @@ class Backtesting:
|
|||||||
optimize memory usage!
|
optimize memory usage!
|
||||||
:param start_date: backtesting timerange start datetime
|
:param start_date: backtesting timerange start datetime
|
||||||
:param end_date: backtesting timerange end datetime
|
:param end_date: backtesting timerange end datetime
|
||||||
:param max_open_trades: maximum number of concurrent trades, <= 0 means unlimited
|
|
||||||
:return: DataFrame with trades (results of backtesting)
|
:return: DataFrame with trades (results of backtesting)
|
||||||
"""
|
"""
|
||||||
self.prepare_backtest(self.enable_protections)
|
self.prepare_backtest(self.enable_protections)
|
||||||
@ -1176,7 +1174,7 @@ class Backtesting:
|
|||||||
if len(detail_data) == 0:
|
if len(detail_data) == 0:
|
||||||
# Fall back to "regular" data if no detail data was found for this candle
|
# Fall back to "regular" data if no detail data was found for this candle
|
||||||
open_trade_count_start = self.backtest_loop(
|
open_trade_count_start = self.backtest_loop(
|
||||||
row, pair, current_time, end_date, max_open_trades,
|
row, pair, current_time, end_date,
|
||||||
open_trade_count_start)
|
open_trade_count_start)
|
||||||
continue
|
continue
|
||||||
detail_data.loc[:, 'enter_long'] = row[LONG_IDX]
|
detail_data.loc[:, 'enter_long'] = row[LONG_IDX]
|
||||||
@ -1189,13 +1187,13 @@ class Backtesting:
|
|||||||
current_time_det = current_time
|
current_time_det = current_time
|
||||||
for det_row in detail_data[HEADERS].values.tolist():
|
for det_row in detail_data[HEADERS].values.tolist():
|
||||||
open_trade_count_start = self.backtest_loop(
|
open_trade_count_start = self.backtest_loop(
|
||||||
det_row, pair, current_time_det, end_date, max_open_trades,
|
det_row, pair, current_time_det, end_date,
|
||||||
open_trade_count_start, is_first)
|
open_trade_count_start, is_first)
|
||||||
current_time_det += timedelta(minutes=self.timeframe_detail_min)
|
current_time_det += timedelta(minutes=self.timeframe_detail_min)
|
||||||
is_first = False
|
is_first = False
|
||||||
else:
|
else:
|
||||||
open_trade_count_start = self.backtest_loop(
|
open_trade_count_start = self.backtest_loop(
|
||||||
row, pair, current_time, end_date, max_open_trades, open_trade_count_start)
|
row, pair, current_time, end_date, open_trade_count_start)
|
||||||
|
|
||||||
# Move time one configured time_interval ahead.
|
# Move time one configured time_interval ahead.
|
||||||
self.progress.increment()
|
self.progress.increment()
|
||||||
@ -1227,14 +1225,11 @@ class Backtesting:
|
|||||||
self._set_strategy(strat)
|
self._set_strategy(strat)
|
||||||
|
|
||||||
# Use max_open_trades in backtesting, except --disable-max-market-positions is set
|
# Use max_open_trades in backtesting, except --disable-max-market-positions is set
|
||||||
if self.config.get('use_max_market_positions', True):
|
if not self.config.get('use_max_market_positions', True):
|
||||||
# Must come from strategy config, as the strategy may modify this setting.
|
|
||||||
max_open_trades = self.strategy.config['max_open_trades'] \
|
|
||||||
if self.strategy.config['max_open_trades'] != float('inf') else -1
|
|
||||||
else:
|
|
||||||
logger.info(
|
logger.info(
|
||||||
'Ignoring max_open_trades (--disable-max-market-positions was used) ...')
|
'Ignoring max_open_trades (--disable-max-market-positions was used) ...')
|
||||||
max_open_trades = 0
|
self.strategy.max_open_trades = -1
|
||||||
|
self.config.update({'max_open_trades': float('inf')})
|
||||||
|
|
||||||
# need to reprocess data every time to populate signals
|
# need to reprocess data every time to populate signals
|
||||||
preprocessed = self.strategy.advise_all_indicators(data)
|
preprocessed = self.strategy.advise_all_indicators(data)
|
||||||
@ -1257,7 +1252,6 @@ class Backtesting:
|
|||||||
processed=preprocessed,
|
processed=preprocessed,
|
||||||
start_date=min_date,
|
start_date=min_date,
|
||||||
end_date=max_date,
|
end_date=max_date,
|
||||||
max_open_trades=max_open_trades,
|
|
||||||
)
|
)
|
||||||
backtest_end_time = datetime.now(timezone.utc)
|
backtest_end_time = datetime.now(timezone.utc)
|
||||||
results.update({
|
results.update({
|
||||||
|
@ -118,14 +118,10 @@ class Hyperopt:
|
|||||||
self.current_best_epoch: Optional[Dict[str, Any]] = None
|
self.current_best_epoch: Optional[Dict[str, Any]] = None
|
||||||
|
|
||||||
# Use max_open_trades for hyperopt as well, except --disable-max-market-positions is set
|
# Use max_open_trades for hyperopt as well, except --disable-max-market-positions is set
|
||||||
if self.config.get('use_max_market_positions', True):
|
if not self.config.get('use_max_market_positions', True):
|
||||||
self.max_open_trades = self.config['max_open_trades'] \
|
|
||||||
if self.config['max_open_trades'] != float('inf') else -1
|
|
||||||
else:
|
|
||||||
logger.debug('Ignoring max_open_trades (--disable-max-market-positions was used) ...')
|
logger.debug('Ignoring max_open_trades (--disable-max-market-positions was used) ...')
|
||||||
self.max_open_trades = 0
|
self.backtesting.strategy.max_open_trades = -1
|
||||||
|
config.update({'max_open_trades': float('inf')})
|
||||||
print("Strategy max open trades", self.max_open_trades)
|
|
||||||
|
|
||||||
if HyperoptTools.has_space(self.config, 'sell'):
|
if HyperoptTools.has_space(self.config, 'sell'):
|
||||||
# Make sure use_exit_signal is enabled
|
# Make sure use_exit_signal is enabled
|
||||||
@ -214,7 +210,8 @@ class Hyperopt:
|
|||||||
if HyperoptTools.has_space(self.config, 'trailing'):
|
if HyperoptTools.has_space(self.config, 'trailing'):
|
||||||
result['trailing'] = self.custom_hyperopt.generate_trailing_params(params)
|
result['trailing'] = self.custom_hyperopt.generate_trailing_params(params)
|
||||||
if HyperoptTools.has_space(self.config, 'trades'):
|
if HyperoptTools.has_space(self.config, 'trades'):
|
||||||
result['max_open_trades'] = {p.name: params.get(p.name) for p in self.trades_space}
|
result['max_open_trades'] = {
|
||||||
|
'max_open_trades': self.backtesting.strategy.max_open_trades}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -342,7 +339,21 @@ class Hyperopt:
|
|||||||
d['trailing_only_offset_is_reached']
|
d['trailing_only_offset_is_reached']
|
||||||
|
|
||||||
if HyperoptTools.has_space(self.config, 'trades'):
|
if HyperoptTools.has_space(self.config, 'trades'):
|
||||||
self.max_open_trades = params_dict['max_open_trades']
|
if self.config["stake_amount"] == "unlimited" and \
|
||||||
|
(params_dict['max_open_trades'] == -1 or params_dict['max_open_trades'] == 0):
|
||||||
|
# Ignore unlimited max open trades if stake amount is unlimited
|
||||||
|
params_dict.update({'max_open_trades': self.config['max_open_trades']})
|
||||||
|
|
||||||
|
updated_config_max_open_trades = int(params_dict['max_open_trades']) \
|
||||||
|
if (params_dict['max_open_trades'] != -1
|
||||||
|
and params_dict['max_open_trades'] != 0) else float('inf')
|
||||||
|
|
||||||
|
updated_strategy_max_open_trades = int(updated_config_max_open_trades) \
|
||||||
|
if updated_config_max_open_trades != float('inf') else -1
|
||||||
|
|
||||||
|
self.config.update({'max_open_trades': updated_config_max_open_trades})
|
||||||
|
|
||||||
|
self.backtesting.strategy.max_open_trades = updated_strategy_max_open_trades
|
||||||
|
|
||||||
with self.data_pickle_file.open('rb') as f:
|
with self.data_pickle_file.open('rb') as f:
|
||||||
processed = load(f, mmap_mode='r')
|
processed = load(f, mmap_mode='r')
|
||||||
@ -353,8 +364,7 @@ class Hyperopt:
|
|||||||
bt_results = self.backtesting.backtest(
|
bt_results = self.backtesting.backtest(
|
||||||
processed=processed,
|
processed=processed,
|
||||||
start_date=self.min_date,
|
start_date=self.min_date,
|
||||||
end_date=self.max_date,
|
end_date=self.max_date
|
||||||
max_open_trades=self.max_open_trades,
|
|
||||||
)
|
)
|
||||||
backtest_end_time = datetime.now(timezone.utc)
|
backtest_end_time = datetime.now(timezone.utc)
|
||||||
bt_results.update({
|
bt_results.update({
|
||||||
|
@ -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"] = data.trailing_stop_positive
|
||||||
default_conf["trailing_stop_positive_offset"] = data.trailing_stop_positive_offset
|
default_conf["trailing_stop_positive_offset"] = data.trailing_stop_positive_offset
|
||||||
default_conf["use_exit_signal"] = data.use_exit_signal
|
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_fee", return_value=0.0)
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
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,
|
processed=data_processed,
|
||||||
start_date=min_date,
|
start_date=min_date,
|
||||||
end_date=max_date,
|
end_date=max_date,
|
||||||
max_open_trades=10,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
results = result['results']
|
results = result['results']
|
||||||
|
@ -96,7 +96,6 @@ def _make_backtest_conf(mocker, datadir, conf=None, pair='UNITTEST/BTC'):
|
|||||||
'processed': processed,
|
'processed': processed,
|
||||||
'start_date': min_date,
|
'start_date': min_date,
|
||||||
'end_date': max_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:
|
def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None:
|
||||||
default_conf['use_exit_signal'] = False
|
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_fee', fee)
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
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_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),
|
processed=deepcopy(processed),
|
||||||
start_date=min_date,
|
start_date=min_date,
|
||||||
end_date=max_date,
|
end_date=max_date,
|
||||||
max_open_trades=10,
|
|
||||||
)
|
)
|
||||||
results = result['results']
|
results = result['results']
|
||||||
assert not results.empty
|
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):
|
def custom_entry_price(proposed_rate, **kwargs):
|
||||||
return proposed_rate * 0.997
|
return proposed_rate * 0.997
|
||||||
|
|
||||||
|
default_conf_usdt['max_open_trades'] = 10
|
||||||
|
|
||||||
backtesting = Backtesting(default_conf_usdt)
|
backtesting = Backtesting(default_conf_usdt)
|
||||||
backtesting._set_strategy(backtesting.strategylist[0])
|
backtesting._set_strategy(backtesting.strategylist[0])
|
||||||
backtesting.strategy.populate_entry_trend = advise_entry
|
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),
|
processed=deepcopy(processed),
|
||||||
start_date=min_date,
|
start_date=min_date,
|
||||||
end_date=max_date,
|
end_date=max_date,
|
||||||
max_open_trades=10,
|
|
||||||
)
|
)
|
||||||
results = result['results']
|
results = result['results']
|
||||||
assert not results.empty
|
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_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_max_pair_stake_amount", return_value=float('inf'))
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
|
default_conf['max_open_trades'] = 1
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
backtesting._set_strategy(backtesting.strategylist[0])
|
backtesting._set_strategy(backtesting.strategylist[0])
|
||||||
# Testing dataframe contains 11 candles. Expecting 10 timed out orders.
|
# 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),
|
processed=deepcopy(data),
|
||||||
start_date=min_date,
|
start_date=min_date,
|
||||||
end_date=max_date,
|
end_date=max_date,
|
||||||
max_open_trades=1,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result['timedout_entry_orders'] == 10
|
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:
|
def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None:
|
||||||
default_conf['use_exit_signal'] = False
|
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_fee', fee)
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
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_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,
|
processed=processed,
|
||||||
start_date=min_date,
|
start_date=min_date,
|
||||||
end_date=max_date,
|
end_date=max_date,
|
||||||
max_open_trades=1,
|
|
||||||
)
|
)
|
||||||
assert not results['results'].empty
|
assert not results['results'].empty
|
||||||
assert len(results['results']) == 1
|
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:
|
def test_backtest_trim_no_data_left(default_conf, fee, mocker, testdatadir) -> None:
|
||||||
default_conf['use_exit_signal'] = False
|
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_fee', fee)
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
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_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),
|
processed=deepcopy(processed),
|
||||||
start_date=min_date,
|
start_date=min_date,
|
||||||
end_date=max_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:
|
def test_backtest_dataprovider_analyzed_df(default_conf, fee, mocker, testdatadir) -> None:
|
||||||
default_conf['use_exit_signal'] = False
|
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_fee', fee)
|
||||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
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)
|
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),
|
processed=deepcopy(processed),
|
||||||
start_date=min_date,
|
start_date=min_date,
|
||||||
end_date=max_date,
|
end_date=max_date,
|
||||||
max_open_trades=10,
|
|
||||||
)
|
)
|
||||||
assert count == 5
|
assert count == 5
|
||||||
|
|
||||||
@ -998,6 +1000,7 @@ def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatad
|
|||||||
|
|
||||||
default_conf['enable_protections'] = True
|
default_conf['enable_protections'] = True
|
||||||
default_conf['timeframe'] = '1m'
|
default_conf['timeframe'] = '1m'
|
||||||
|
default_conf['max_open_trades'] = 1
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
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_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_max_pair_stake_amount", return_value=float('inf'))
|
||||||
@ -1024,7 +1027,6 @@ def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatad
|
|||||||
processed=processed,
|
processed=processed,
|
||||||
start_date=min_date,
|
start_date=min_date,
|
||||||
end_date=max_date,
|
end_date=max_date,
|
||||||
max_open_trades=1,
|
|
||||||
)
|
)
|
||||||
assert len(results['results']) == numres
|
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)
|
processed = backtesting.strategy.advise_all_indicators(data)
|
||||||
min_date, max_date = get_timerange(processed)
|
min_date, max_date = get_timerange(processed)
|
||||||
assert isinstance(processed, dict)
|
assert isinstance(processed, dict)
|
||||||
|
backtesting.strategy.max_open_trades = 1
|
||||||
|
backtesting.config.update({'max_open_trades': 1})
|
||||||
results = backtesting.backtest(
|
results = backtesting.backtest(
|
||||||
processed=processed,
|
processed=processed,
|
||||||
start_date=min_date,
|
start_date=min_date,
|
||||||
end_date=max_date,
|
end_date=max_date,
|
||||||
max_open_trades=1,
|
|
||||||
)
|
)
|
||||||
assert len(results['results']) == expected
|
assert len(results['results']) == expected
|
||||||
|
|
||||||
@ -1077,7 +1080,7 @@ def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir):
|
|||||||
buy_value = 1
|
buy_value = 1
|
||||||
sell_value = 1
|
sell_value = 1
|
||||||
return _trend(dataframe, buy_value, sell_value)
|
return _trend(dataframe, buy_value, sell_value)
|
||||||
|
default_conf['max_open_trades'] = 10
|
||||||
backtest_conf = _make_backtest_conf(mocker, conf=default_conf, datadir=testdatadir)
|
backtest_conf = _make_backtest_conf(mocker, conf=default_conf, datadir=testdatadir)
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
backtesting._set_strategy(backtesting.strategylist[0])
|
backtesting._set_strategy(backtesting.strategylist[0])
|
||||||
@ -1094,6 +1097,7 @@ def test_backtest_only_sell(mocker, default_conf, testdatadir):
|
|||||||
sell_value = 1
|
sell_value = 1
|
||||||
return _trend(dataframe, buy_value, sell_value)
|
return _trend(dataframe, buy_value, sell_value)
|
||||||
|
|
||||||
|
default_conf['max_open_trades'] = 10
|
||||||
backtest_conf = _make_backtest_conf(mocker, conf=default_conf, datadir=testdatadir)
|
backtest_conf = _make_backtest_conf(mocker, conf=default_conf, datadir=testdatadir)
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
backtesting._set_strategy(backtesting.strategylist[0])
|
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_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_max_pair_stake_amount", return_value=float('inf'))
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
|
default_conf['max_open_trades'] = 10
|
||||||
backtest_conf = _make_backtest_conf(mocker, conf=default_conf,
|
backtest_conf = _make_backtest_conf(mocker, conf=default_conf,
|
||||||
pair='UNITTEST/BTC', datadir=testdatadir)
|
pair='UNITTEST/BTC', datadir=testdatadir)
|
||||||
default_conf['timeframe'] = '1m'
|
default_conf['timeframe'] = '1m'
|
||||||
@ -1165,6 +1170,7 @@ def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir)
|
|||||||
if tres > 0:
|
if tres > 0:
|
||||||
data[pair] = data[pair][tres:].reset_index()
|
data[pair] = data[pair][tres:].reset_index()
|
||||||
default_conf['timeframe'] = '5m'
|
default_conf['timeframe'] = '5m'
|
||||||
|
default_conf['max_open_trades'] = 3
|
||||||
|
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
backtesting._set_strategy(backtesting.strategylist[0])
|
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)
|
processed = backtesting.strategy.advise_all_indicators(data)
|
||||||
min_date, max_date = get_timerange(processed)
|
min_date, max_date = get_timerange(processed)
|
||||||
|
|
||||||
backtest_conf = {
|
backtest_conf = {
|
||||||
'processed': deepcopy(processed),
|
'processed': deepcopy(processed),
|
||||||
'start_date': min_date,
|
'start_date': min_date,
|
||||||
'end_date': max_date,
|
'end_date': max_date,
|
||||||
'max_open_trades': 3,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = backtesting.backtest(**backtest_conf)
|
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]
|
backtesting.dataprovider.get_analyzed_dataframe('NXT/BTC', '5m')[0]
|
||||||
) == len(data['NXT/BTC']) - 1 - backtesting.strategy.startup_candle_count
|
) == 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 = {
|
backtest_conf = {
|
||||||
'processed': deepcopy(processed),
|
'processed': deepcopy(processed),
|
||||||
'start_date': min_date,
|
'start_date': min_date,
|
||||||
'end_date': max_date,
|
'end_date': max_date,
|
||||||
'max_open_trades': 1,
|
|
||||||
}
|
}
|
||||||
results = backtesting.backtest(**backtest_conf)
|
results = backtesting.backtest(**backtest_conf)
|
||||||
assert len(evaluate_result_multi(results['results'], '5m', 1)) == 0
|
assert len(evaluate_result_multi(results['results'], '5m', 1)) == 0
|
||||||
|
@ -17,6 +17,7 @@ from tests.conftest import patch_exchange
|
|||||||
|
|
||||||
def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) -> None:
|
def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) -> None:
|
||||||
default_conf['use_exit_signal'] = False
|
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_fee', fee)
|
||||||
mocker.patch('freqtrade.optimize.backtesting.amount_to_contract_precision',
|
mocker.patch('freqtrade.optimize.backtesting.amount_to_contract_precision',
|
||||||
lambda x, *args, **kwargs: round(x, 8))
|
lambda x, *args, **kwargs: round(x, 8))
|
||||||
@ -41,7 +42,6 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) ->
|
|||||||
processed=deepcopy(processed),
|
processed=deepcopy(processed),
|
||||||
start_date=min_date,
|
start_date=min_date,
|
||||||
end_date=max_date,
|
end_date=max_date,
|
||||||
max_open_trades=10,
|
|
||||||
)
|
)
|
||||||
results = result['results']
|
results = result['results']
|
||||||
assert not results.empty
|
assert not results.empty
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
# pragma pylint: disable=missing-docstring,W0212,C0103
|
# pragma pylint: disable=missing-docstring,W0212,C0103
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
from functools import wraps
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import ANY, MagicMock, PropertyMock
|
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 dumper2.call_count == 1
|
||||||
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
|
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
|
||||||
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
|
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
|
||||||
assert hasattr(hyperopt, "max_open_trades")
|
assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf['max_open_trades']
|
||||||
assert hyperopt.max_open_trades == hyperopt_conf['max_open_trades']
|
|
||||||
assert hasattr(hyperopt.backtesting, "_position_stacking")
|
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_exit")
|
||||||
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
|
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
|
||||||
assert hasattr(hyperopt, "max_open_trades")
|
assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf['max_open_trades']
|
||||||
assert hyperopt.max_open_trades == hyperopt_conf['max_open_trades']
|
|
||||||
assert hasattr(hyperopt.backtesting, "_position_stacking")
|
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 dumper2.call_count == 1
|
||||||
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
|
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
|
||||||
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
|
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
|
||||||
assert hasattr(hyperopt, "max_open_trades")
|
assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf['max_open_trades']
|
||||||
assert hyperopt.max_open_trades == hyperopt_conf['max_open_trades']
|
|
||||||
assert hasattr(hyperopt.backtesting, "_position_stacking")
|
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 dumper2.call_count == 1
|
||||||
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
|
assert hasattr(hyperopt.backtesting.strategy, "advise_exit")
|
||||||
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
|
assert hasattr(hyperopt.backtesting.strategy, "advise_entry")
|
||||||
assert hasattr(hyperopt, "max_open_trades")
|
assert hyperopt.backtesting.strategy.max_open_trades == hyperopt_conf['max_open_trades']
|
||||||
assert hyperopt.max_open_trades == hyperopt_conf['max_open_trades']
|
|
||||||
assert hasattr(hyperopt.backtesting, "_position_stacking")
|
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.buy_rsi.value == 35
|
||||||
assert hyperopt.backtesting.strategy.sell_rsi.value == 74
|
assert hyperopt.backtesting.strategy.sell_rsi.value == 74
|
||||||
assert hyperopt.backtesting.strategy.protection_cooldown_lookback.value == 30
|
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
|
buy_rsi_range = hyperopt.backtesting.strategy.buy_rsi.range
|
||||||
assert isinstance(buy_rsi_range, range)
|
assert isinstance(buy_rsi_range, range)
|
||||||
# Range from 0 - 50 (inclusive)
|
# 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.protection_cooldown_lookback.value != 30
|
||||||
assert hyperopt.backtesting.strategy.buy_rsi.value != 35
|
assert hyperopt.backtesting.strategy.buy_rsi.value != 35
|
||||||
assert hyperopt.backtesting.strategy.sell_rsi.value != 74
|
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'
|
hyperopt.custom_hyperopt.generate_estimator = lambda *args, **kwargs: 'ET1'
|
||||||
with pytest.raises(OperationalException, match="Estimator ET1 not supported."):
|
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([2.0]) == [200]
|
||||||
assert space.transform([1.0]) == [100]
|
assert space.transform([1.0]) == [100]
|
||||||
assert space.transform([1.5, 1.6]) == [150, 160]
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user