From 2811a470aa2ca1467713bc7caebef44f1c98b7e6 Mon Sep 17 00:00:00 2001 From: Axel-CH Date: Wed, 11 Sep 2024 21:11:20 -0400 Subject: [PATCH] handle pre existing open order cancelation on trade exit --- freqtrade/freqtradebot.py | 55 +++++++++++++++++++------- tests/freqtradebot/test_integration.py | 4 +- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index e23d07af9..e1d56d581 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1683,6 +1683,34 @@ class FreqtradeBot(LoggingMixin): logger.warning(f"Unable to replace order for {trade.pair}: {exception}") self.replace_order_failed(trade, f"Could not replace order for {trade}.") + def cancel_open_orders_of_trade(self, trade: Trade, reason: str, sides: List[str]) -> None: + """ + Cancel trade orders of specified sides that are currently open + :param trade: Trade object of the trade we're analyzing + :param reason: The reason for that cancelation + :param sides: The sides where cancellation should take place + :return: None + """ + + for open_order in trade.open_orders: + try: + order = self.exchange.fetch_order(open_order.order_id, trade.pair) + except ExchangeError: + logger.info("Can't query order for %s due to %s", trade, traceback.format_exc()) + continue + + for side in sides: + if (order["side"] == side): + if order["side"] == trade.entry_side: + self.handle_cancel_enter( + trade, order, open_order, reason + ) + + elif order["side"] == trade.exit_side: + self.handle_cancel_exit( + trade, order, open_order, reason + ) + def cancel_all_open_orders(self) -> None: """ Cancel all orders that are currently open @@ -1690,22 +1718,11 @@ class FreqtradeBot(LoggingMixin): """ for trade in Trade.get_open_trades(): - for open_order in trade.open_orders: - try: - order = self.exchange.fetch_order(open_order.order_id, trade.pair) - except ExchangeError: - logger.info("Can't query order for %s due to %s", trade, traceback.format_exc()) - continue + self.cancel_open_orders_of_trade( + trade, constants.CANCEL_REASON["ALL_CANCELLED"], + [trade.entry_side, trade.exit_side] + ) - if order["side"] == trade.entry_side: - self.handle_cancel_enter( - trade, order, open_order, constants.CANCEL_REASON["ALL_CANCELLED"] - ) - - elif order["side"] == trade.exit_side: - self.handle_cancel_exit( - trade, order, open_order, constants.CANCEL_REASON["ALL_CANCELLED"] - ) Trade.commit() def handle_cancel_enter( @@ -1951,6 +1968,14 @@ class FreqtradeBot(LoggingMixin): limit = self.get_valid_price(custom_exit_price, proposed_limit_rate) + if trade.has_open_orders: + # cancel any open order of this trade + self.cancel_open_orders_of_trade( + trade, constants.CANCEL_REASON["REPLACE"], + [trade.exit_side] + ) + Trade.commit() + # First cancelling stoploss on exchange ... trade = self.cancel_stoploss_on_exchange(trade) diff --git a/tests/freqtradebot/test_integration.py b/tests/freqtradebot/test_integration.py index 649bacddf..418d38d07 100644 --- a/tests/freqtradebot/test_integration.py +++ b/tests/freqtradebot/test_integration.py @@ -497,7 +497,9 @@ def test_dca_order_adjust(default_conf_usdt, ticker_usdt, leverage, fee, mocker, print("AFTER Process trade.orders") print(trade.orders) - assert len(trade.orders) == 5 + assert trade.orders[-2].status == "canceled" + assert len(trade.orders) == 6 + assert trade.orders[-1].side == trade.exit_side assert trade.orders[-1].status == "open" assert trade.orders[-1].price == 2.02 # Adjust entry price cannot be called - this is an exit order