diff --git a/docs/backtesting.md b/docs/backtesting.md index 45c2704a0..b4d9aef80 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -320,7 +320,9 @@ A backtesting result will look like that: | Avg. Duration Loser | 6:55:00 | | Rejected Entry signals | 3089 | | Entry/Exit Timeouts | 0 / 0 | -| Canceled Trade Entries | 123 | +| Canceled Trade Entries | 34 | +| Canceled Entry Orders | 123 | +| Replaced Entry Orders | 89 | | | | | Min balance | 0.00945123 BTC | | Max balance | 0.01846651 BTC | @@ -417,7 +419,9 @@ It contains some useful key metrics about performance of your strategy on backte | Avg. Duration Loser | 6:55:00 | | Rejected Entry signals | 3089 | | Entry/Exit Timeouts | 0 / 0 | -| Canceled Trade Entries | 123 | +| Canceled Trade Entries | 34 | +| Canceled Entry Orders | 123 | +| Replaced Entry Orders | 89 | | | | | Min balance | 0.00945123 BTC | | Max balance | 0.01846651 BTC | @@ -450,6 +454,8 @@ It contains some useful key metrics about performance of your strategy on backte - `Rejected Entry signals`: Trade entry signals that could not be acted upon due to `max_open_trades` being reached. - `Entry/Exit Timeouts`: Entry/exit orders which did not fill (only applicable if custom pricing is used). - `Canceled Trade Entries`: Number of trades that have been canceled by user request via `adjust_entry_price`. +- `Canceled Entry Orders`: Number of entry orders that have been canceled by user request via `adjust_entry_price`. +- `Replaced Entry Orders`: Number of entry orders that have been replaced by user request via `adjust_entry_price`. - `Min balance` / `Max balance`: Lowest and Highest Wallet balance during the backtest period. - `Max % of account underwater`: Maximum percentage your account has decreased from the top since the simulation started. Calculated as the maximum of `(Max Balance - Current Balance) / (Max Balance)`. diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index db46cc9ff..4e604898f 100755 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -298,6 +298,8 @@ class Backtesting: self.timedout_entry_orders = 0 self.timedout_exit_orders = 0 self.canceled_trade_entries = 0 + self.canceled_entry_orders = 0 + self.replaced_entry_orders = 0 self.dataprovider.clear_cache() if enable_protections: self._load_protections(self.strategy) @@ -935,6 +937,7 @@ class Backtesting: return False else: del trade.orders[trade.orders.index(order)] + self.canceled_entry_orders += 1 # place new order if result was not None if requested_rate: @@ -942,6 +945,7 @@ class Backtesting: requested_rate=requested_rate, requested_stake=(order.remaining * order.price), direction='short' if trade.is_short else 'long') + self.replaced_entry_orders += 1 else: # assumption: there can't be multiple open entry orders at any given time return (trade.nr_of_successful_entries == 0) @@ -1090,6 +1094,8 @@ class Backtesting: 'timedout_entry_orders': self.timedout_entry_orders, 'timedout_exit_orders': self.timedout_exit_orders, 'canceled_trade_entries': self.canceled_trade_entries, + 'canceled_entry_orders': self.canceled_entry_orders, + 'replaced_entry_orders': self.replaced_entry_orders, '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 7ee25ea73..93336fa3f 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -469,6 +469,8 @@ def generate_strategy_stats(pairlist: List[str], 'timedout_entry_orders': content['timedout_entry_orders'], 'timedout_exit_orders': content['timedout_exit_orders'], 'canceled_trade_entries': content['canceled_trade_entries'], + 'canceled_entry_orders': content['canceled_entry_orders'], + 'replaced_entry_orders': content['replaced_entry_orders'], 'max_open_trades': max_open_trades, 'max_open_trades_setting': (config['max_open_trades'] if config['max_open_trades'] != float('inf') else -1), @@ -754,6 +756,12 @@ def text_table_add_metrics(strat_results: Dict) -> str: ('Drawdown End', strat_results['drawdown_end']), ]) + entry_adjustment_metrics = [ + ('Canceled Trade Entries', strat_results.get('canceled_trade_entries', 'N/A')), + ('Canceled Entry Orders', strat_results.get('canceled_entry_orders', 'N/A')), + ('Replaced Entry Orders', strat_results.get('replaced_entry_orders', 'N/A')), + ] if strat_results.get('canceled_entry_orders', 0) > 0 else [] + # Newly added fields should be ignored if they are missing in strat_results. hyperopt-show # command stores these results and newer version of freqtrade must be able to handle old # results with missing new fields. @@ -802,7 +810,7 @@ def text_table_add_metrics(strat_results: Dict) -> str: ('Entry/Exit Timeouts', f"{strat_results.get('timedout_entry_orders', 'N/A')} / " f"{strat_results.get('timedout_exit_orders', 'N/A')}"), - ('Canceled Trade Entries', strat_results.get('canceled_trade_entries', 'N/A')), + *entry_adjustment_metrics, ('', ''), # 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 5b080fb11..f169e0a35 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -1169,6 +1169,8 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, }) mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', @@ -1282,6 +1284,8 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, }, { @@ -1292,6 +1296,8 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, } ]) @@ -1435,6 +1441,8 @@ def test_backtest_start_nomock_futures(default_conf_usdt, mocker, 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, }, { @@ -1445,6 +1453,8 @@ def test_backtest_start_nomock_futures(default_conf_usdt, mocker, 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, } ]) @@ -1540,6 +1550,8 @@ def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker, 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, }, { @@ -1550,6 +1562,8 @@ def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker, 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, } ]) @@ -1614,6 +1628,8 @@ def test_backtest_start_multi_strat_caching(default_conf, mocker, caplog, testda 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, }) mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 1d729190e..dcc1ddeea 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -369,6 +369,8 @@ def test_hyperopt_format_results(hyperopt): 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'backtest_start_time': 1619718665, 'backtest_end_time': 1619718665, } @@ -440,6 +442,8 @@ def test_generate_optimizer(mocker, hyperopt_conf) -> None: 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'final_balance': 1000, } diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index 6ba03f08e..997c0436e 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -88,6 +88,8 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmpdir): 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'backtest_start_time': Arrow.utcnow().int_timestamp, 'backtest_end_time': Arrow.utcnow().int_timestamp, 'run_id': '123', @@ -141,6 +143,8 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmpdir): 'timedout_entry_orders': 0, 'timedout_exit_orders': 0, 'canceled_trade_entries': 0, + 'canceled_entry_orders': 0, + 'replaced_entry_orders': 0, 'backtest_start_time': Arrow.utcnow().int_timestamp, 'backtest_end_time': Arrow.utcnow().int_timestamp, 'run_id': '124',