From 28c62724df27568fe451156f9d6147cbb3e508af Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 1 Sep 2023 06:58:54 +0200 Subject: [PATCH] Add explicit test and message for "Order could not be replaced" scenario part of #9128 --- freqtrade/freqtradebot.py | 5 +-- tests/test_integration.py | 70 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 681bc71c7..4444ddd94 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1410,7 +1410,7 @@ class FreqtradeBot(LoggingMixin): replacing=replacing) if adjusted_entry_price: # place new order only if new price is supplied - self.execute_entry( + if not self.execute_entry( pair=trade.pair, stake_amount=( order_obj.safe_remaining * order_obj.safe_price / trade.leverage), @@ -1418,7 +1418,8 @@ class FreqtradeBot(LoggingMixin): trade=trade, is_short=trade.is_short, order_adjust=True, - ) + ): + logger.warning(f"Could not replace order for {trade}.") def cancel_all_open_orders(self) -> None: """ diff --git a/tests/test_integration.py b/tests/test_integration.py index 41265ae74..6f6ceaaaf 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -497,6 +497,76 @@ def test_dca_order_adjust(default_conf_usdt, ticker_usdt, leverage, fee, mocker) assert freqtrade.strategy.adjust_entry_price.call_count == 0 +@pytest.mark.parametrize('leverage', [1, 2]) +@pytest.mark.parametrize("is_short", [False, True]) +def test_dca_order_adjust_entry_replace_fails( + default_conf_usdt, ticker_usdt, fee, mocker, caplog, is_short, leverage +) -> None: + spot = leverage == 1 + if not spot: + default_conf_usdt['trading_mode'] = 'futures' + default_conf_usdt['margin_mode'] = 'isolated' + default_conf_usdt['position_adjustment_enable'] = True + default_conf_usdt['max_open_trades'] = 2 + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) + mocker.patch.multiple( + EXMS, + fetch_ticker=ticker_usdt, + get_fee=fee, + get_funding_fees=MagicMock(return_value=0), + ) + + # no order fills. + mocker.patch(f'{EXMS}._dry_is_price_crossed', side_effect=[False, True]) + patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short) + freqtrade.enter_positions() + + trades = Trade.session.scalars( + select(Trade).filter(Trade.open_order_id.is_not(None))).all() + assert len(trades) == 1 + + mocker.patch(f'{EXMS}._dry_is_price_crossed', return_value=False) + + # Timeout to not interfere + freqtrade.strategy.ft_check_timed_out = MagicMock(return_value=False) + + # Create DCA order for 2nd trade (so we have 2 open orders on 2 trades) + # this 2nd order won't fill. + + freqtrade.strategy.adjust_trade_position = MagicMock(return_value=20) + + freqtrade.process() + + assert freqtrade.strategy.adjust_trade_position.call_count == 1 + trades = Trade.session.scalars( + select(Trade).filter(Trade.open_order_id.is_not(None))).all() + assert len(trades) == 2 + + # We now have 2 orders open + freqtrade.strategy.adjust_entry_price = MagicMock(return_value=2.05) + freqtrade.manage_open_orders() + trades = Trade.session.scalars( + select(Trade).filter(Trade.open_order_id.is_not(None))).all() + assert len(trades) == 2 + assert len(Order.get_open_orders()) == 2 + # Entry adjustment is called + assert freqtrade.strategy.adjust_entry_price.call_count == 2 + + # Attempt order replacement - fails. + freqtrade.strategy.adjust_entry_price = MagicMock(return_value=1234) + + entry_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.execute_entry', + return_value=False) + msg = r"Could not replace order for.*" + assert not log_has_re(msg, caplog) + freqtrade.manage_open_orders() + + assert log_has_re(msg, caplog) + assert entry_mock.call_count == 2 + assert len(Trade.get_trades().all()) == 2 + assert len(Order.get_open_orders()) == 0 + + @pytest.mark.parametrize('leverage', [1, 2]) def test_dca_exiting(default_conf_usdt, ticker_usdt, fee, mocker, caplog, leverage) -> None: default_conf_usdt['position_adjustment_enable'] = True