diff --git a/docs/backtesting.md b/docs/backtesting.md index e720206bb..2027c2079 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -298,6 +298,7 @@ A backtesting result will look like that: | Avg. Duration Winners | 4:23:00 | | Avg. Duration Loser | 6:55:00 | | Zero Duration Trades | 4.6% (20) | +| Rejected Buy signals | 3089 | | | | | Min balance | 0.00945123 BTC | | Max balance | 0.01846651 BTC | @@ -386,6 +387,7 @@ It contains some useful key metrics about performance of your strategy on backte | Avg. Duration Winners | 4:23:00 | | Avg. Duration Loser | 6:55:00 | | Zero Duration Trades | 4.6% (20) | +| Rejected Buy signals | 3089 | | | | | Min balance | 0.00945123 BTC | | Max balance | 0.01846651 BTC | @@ -416,6 +418,7 @@ It contains some useful key metrics about performance of your strategy on backte - `Days win/draw/lose`: Winning / Losing days (draws are usually days without closed trade). - `Avg. Duration Winners` / `Avg. Duration Loser`: Average durations for winning and losing trades. - `Zero Duration Trades`: A number of trades that completed within same candle as they opened and had `trailing_stop_loss` sell reason. A significant amount of such trades may indicate that strategy is exploiting trailing stoploss behavior in backtesting and produces unrealistic results. +- `Rejected Buy signals`: Buy signals that could not be acted upon due to max_open_trades being reached. - `Min balance` / `Max balance`: Lowest and Highest Wallet balance during the backtest period. - `Drawdown`: Maximum drawdown experienced. For example, the value of 50% means that from highest to subsequent lowest point, a 50% drop was experienced). - `Drawdown high` / `Drawdown low`: Profit at the beginning and end of the largest drawdown period. A negative low value means initial capital lost. diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 642ed2b76..cbc0995aa 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -338,11 +338,8 @@ class Backtesting: return trades def trade_slot_available(self, max_open_trades: int, open_trade_count: int) -> bool: - if max_open_trades <= 0: - # Always allow trades when max_open_trades is enabled. - return True - if open_trade_count < max_open_trades: - + # Always allow trades when max_open_trades is enabled. + if max_open_trades <= 0 or open_trade_count < max_open_trades: return True # Rejected trade self.rejected_trades += 1 @@ -454,7 +451,7 @@ class Backtesting: 'results': results, 'config': self.strategy.config, 'locks': PairLocks.get_all_locks(), - 'rejected': self.rejected_trades, + 'rejected_signals': self.rejected_trades, 'final_balance': self.wallets.get_total(self.strategy.config['stake_currency']), } diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index ddccfd1dc..090af4a85 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -355,7 +355,7 @@ def generate_strategy_stats(btdata: Dict[str, DataFrame], 'starting_balance': starting_balance, 'dry_run_wallet': starting_balance, 'final_balance': content['final_balance'], - 'rejected_signals': content['rejected'], + 'rejected_signals': content['rejected_signals'], 'max_open_trades': max_open_trades, 'max_open_trades_setting': (config['max_open_trades'] if config['max_open_trades'] != float('inf') else -1), @@ -562,8 +562,6 @@ def text_table_add_metrics(strat_results: Dict) -> str: strat_results['stake_currency'])), ('Total trade volume', round_coin_value(strat_results['total_volume'], strat_results['stake_currency'])), - ('Rejected Buy signals', strat_results.get('rejected_signals', 'N/A')), - ('', ''), # Empty line to improve readability ('Best Pair', f"{strat_results['best_pair']['key']} " f"{round(strat_results['best_pair']['profit_sum_pct'], 2)}%"), @@ -582,6 +580,7 @@ def text_table_add_metrics(strat_results: Dict) -> str: ('Avg. Duration Winners', f"{strat_results['winner_holding_avg']}"), ('Avg. Duration Loser', f"{strat_results['loser_holding_avg']}"), ('Zero Duration Trades', zero_duration_trades), + ('Rejected Buy signals', strat_results.get('rejected_signals', 'N/A')), ('', ''), # Empty line to improve readability ('Min balance', round_coin_value(strat_results['csum_min'], diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 03a65b159..632d458ce 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -831,6 +831,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): 'results': pd.DataFrame(columns=BT_DATA_COLUMNS), 'config': default_conf, 'locks': [], + 'rejected_signals': 20, 'final_balance': 1000, }) mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', @@ -938,12 +939,14 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat 'results': result1, 'config': default_conf, 'locks': [], + 'rejected_signals': 20, 'final_balance': 1000, }, { 'results': result2, 'config': default_conf, 'locks': [], + 'rejected_signals': 20, 'final_balance': 1000, } ]) diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index b80ede734..d7eb8bf67 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -441,6 +441,7 @@ def test_hyperopt_format_results(hyperopt): 'config': hyperopt.config, 'locks': [], 'final_balance': 0.02, + 'rejected_signals': 2, 'backtest_start_time': 1619718665, 'backtest_end_time': 1619718665, } @@ -593,6 +594,7 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None: }), 'config': hyperopt_conf, 'locks': [], + 'rejected_signals': 20, 'final_balance': 1000, } diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index 575bb9092..f9dac3397 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -79,6 +79,7 @@ def test_generate_backtest_stats(default_conf, testdatadir): 'config': default_conf, 'locks': [], 'final_balance': 1000.02, + 'rejected_signals': 20, 'backtest_start_time': Arrow.utcnow().int_timestamp, 'backtest_end_time': Arrow.utcnow().int_timestamp, } @@ -126,6 +127,7 @@ def test_generate_backtest_stats(default_conf, testdatadir): 'config': default_conf, 'locks': [], 'final_balance': 1000.02, + 'rejected_signals': 20, 'backtest_start_time': Arrow.utcnow().int_timestamp, 'backtest_end_time': Arrow.utcnow().int_timestamp, }