From f2fa476dc69a9412f705fe1d27bb9a27868bea8d Mon Sep 17 00:00:00 2001 From: Antonio Della Fortuna Date: Wed, 4 Jan 2023 16:09:27 +0100 Subject: [PATCH] max_open_trades should be an integer Max open trades will be always an integer in the strategy (-1 for infinity), but in the config -1 will be parsed as infinity --- freqtrade/data/btanalysis.py | 2 +- freqtrade/freqtradebot.py | 2 +- freqtrade/optimize/backtesting.py | 9 +++++---- freqtrade/optimize/hyperopt.py | 5 ++++- freqtrade/optimize/optimize_reports.py | 2 +- freqtrade/resolvers/strategy_resolver.py | 14 ++++++++++---- freqtrade/strategy/interface.py | 2 +- tests/strategy/test_strategy_loading.py | 7 ++++--- 8 files changed, 27 insertions(+), 16 deletions(-) diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index bc28d9de0..3102683b2 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -332,7 +332,7 @@ def analyze_trade_parallelism(results: pd.DataFrame, timeframe: str) -> pd.DataF def evaluate_result_multi(results: pd.DataFrame, timeframe: str, - max_open_trades: int | float) -> pd.DataFrame: + max_open_trades: int) -> pd.DataFrame: """ Find overlapping trades by expanding each trade once per period it was open and then counting overlaps diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4bf208176..779c134bd 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -521,7 +521,7 @@ class FreqtradeBot(LoggingMixin): self.log_once(f"Pair {pair} is currently locked.", logger.info) return False stake_amount = self.wallets.get_trade_stake_amount( - pair, self.edge, self.config['max_open_trades']) + pair, self.edge) bid_check_dom = self.config.get('entry_pricing', {}).get('check_depth_of_market', {}) if ((bid_check_dom.get('enabled', False)) and diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 178b2c18d..181a5b580 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -920,7 +920,7 @@ class Backtesting: trade.close(exit_row[OPEN_IDX], show_msg=False) LocalTrade.close_bt_trade(trade) - def trade_slot_available(self, max_open_trades: int | float, open_trade_count: int) -> bool: + def trade_slot_available(self, max_open_trades: int, open_trade_count: int) -> bool: # Always allow trades when max_open_trades is enabled. if max_open_trades <= 0 or open_trade_count < max_open_trades: return True @@ -1051,7 +1051,7 @@ class Backtesting: def backtest_loop( self, row: Tuple, pair: str, current_time: datetime, end_date: datetime, - max_open_trades: int | float, + max_open_trades: 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. @@ -1123,7 +1123,7 @@ class Backtesting: def backtest(self, processed: Dict, start_date: datetime, end_date: datetime, - max_open_trades: int | float = 0) -> Dict[str, Any]: + max_open_trades: int = 0) -> Dict[str, Any]: """ Implement backtesting functionality @@ -1228,7 +1228,8 @@ class Backtesting: # Use max_open_trades in backtesting, except --disable-max-market-positions is set if 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'] + max_open_trades = self.strategy.config['max_open_trades'] \ + if self.strategy.config['max_open_trades'] != float('inf') else -1 else: logger.info( 'Ignoring max_open_trades (--disable-max-market-positions was used) ...') diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index aae7802a5..4ab9b1a5d 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -119,11 +119,14 @@ class Hyperopt: # Use max_open_trades for hyperopt as well, except --disable-max-market-positions is set if self.config.get('use_max_market_positions', True): - self.max_open_trades = self.config['max_open_trades'] + 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) ...') self.max_open_trades = 0 + print("Strategy max open trades", self.max_open_trades) + if HyperoptTools.has_space(self.config, 'sell'): # Make sure use_exit_signal is enabled self.config['use_exit_signal'] = True diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index e09cbf428..7de8f1a47 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -191,7 +191,7 @@ def generate_tag_metrics(tag_type: str, return [] -def generate_exit_reason_stats(max_open_trades: int | float, results: DataFrame) -> List[Dict]: +def generate_exit_reason_stats(max_open_trades: int, results: DataFrame) -> List[Dict]: """ Generate small table outlining Backtest results :param max_open_trades: Max_open_trades parameter diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 859388a4e..febda7822 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -104,14 +104,22 @@ class StrategyResolver(IResolver): if (attribute in config and not isinstance(getattr(type(strategy), attribute, None), property)): # Ensure Properties are not overwritten - setattr(strategy, attribute, config[attribute]) + val = config[attribute] + # max_open_trades set to float('inf') in the config will be copied as -1 in the strategy + if attribute == 'max_open_trades' and val == float('inf'): + val = -1 + setattr(strategy, attribute, val) logger.info("Override strategy '%s' with value in config file: %s.", attribute, config[attribute]) elif hasattr(strategy, attribute): val = getattr(strategy, attribute) # None's cannot exist in the config, so do not copy them if val is not None: - config[attribute] = val + # max_open_trades set to -1 in the strategy will be copied as infinity in the config + if attribute == 'max_open_trades' and val == -1: + config[attribute] = float('inf') + else: + config[attribute] = val # Explicitly check for None here as other "falsy" values are possible elif default is not None: setattr(strategy, attribute, default) @@ -129,8 +137,6 @@ class StrategyResolver(IResolver): key=lambda t: t[0])) if hasattr(strategy, 'stoploss'): strategy.stoploss = float(strategy.stoploss) - if hasattr(strategy, 'max_open_trades') and strategy.max_open_trades == -1: - strategy.max_open_trades = float('inf') return strategy @staticmethod diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index e39c403b5..d1b9a8498 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -55,7 +55,7 @@ class IStrategy(ABC, HyperStrategyMixin): stoploss: float # max open trades for the strategy - max_open_trades: int | float + max_open_trades: int # trailing stoploss trailing_stop: bool = False diff --git a/tests/strategy/test_strategy_loading.py b/tests/strategy/test_strategy_loading.py index e08193531..2296d4bc6 100644 --- a/tests/strategy/test_strategy_loading.py +++ b/tests/strategy/test_strategy_loading.py @@ -371,19 +371,20 @@ def test_strategy_max_open_trades_infinity_from_strategy(caplog, default_conf): strategy = StrategyResolver.load_strategy(default_conf) # this test assumes -1 set to 'max_open_trades' in CURRENT_TEST_STRATEGY - assert strategy.max_open_trades == float('inf') + assert strategy.max_open_trades == -1 + assert default_conf['max_open_trades'] == float('inf') def test_strategy_max_open_trades_infinity_from_config(caplog, default_conf): caplog.set_level(logging.INFO) default_conf.update({ 'strategy': CURRENT_TEST_STRATEGY, - 'max_open_trades': -1 + 'max_open_trades': float('inf') }) strategy = StrategyResolver.load_strategy(default_conf) - assert strategy.max_open_trades == float('inf') + assert strategy.max_open_trades == -1 @ pytest.mark.filterwarnings("ignore:deprecated")