From 4e6ea1d2ba52d637eb38199ff47aa493f52af3ed Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 30 Nov 2025 14:21:14 +0100 Subject: [PATCH 1/3] feat: don't cancel stoploss early if it's not necessary --- freqtrade/freqtradebot.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 6485b5d11..89abb0915 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1063,7 +1063,16 @@ class FreqtradeBot(LoggingMixin): return True - def cancel_stoploss_on_exchange(self, trade: Trade) -> Trade: + def cancel_stoploss_on_exchange(self, trade: Trade, allow_nonblocking: bool = False) -> Trade: + """ + Cancels on exchange stoploss orders for the given trade. + :param trade: Trade for which to cancel stoploss order + :param allow_nonblocking: If True, will skip cancelling stoploss on exchange + if the exchange supports blocking stoploss orders. + """ + if allow_nonblocking and not self.exchange.get_option("stoploss_blocks_assets", True): + logger.info(f"Skipping cancelling stoploss on exchange for {trade}.") + return trade # First cancelling stoploss on exchange ... for oslo in trade.open_sl_orders: try: @@ -2088,7 +2097,7 @@ class FreqtradeBot(LoggingMixin): limit = self.get_valid_price(custom_exit_price, proposed_limit_rate) # First cancelling stoploss on exchange ... - trade = self.cancel_stoploss_on_exchange(trade) + trade = self.cancel_stoploss_on_exchange(trade, allow_nonblocking=True) order_type = ordertype or self.strategy.order_types[exit_type] if exit_check.exit_type == ExitType.EMERGENCY_EXIT: From 4b2d09925879be11c17137ebfdd230967bdddc22 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Dec 2025 06:14:48 +0100 Subject: [PATCH 2/3] fix: ensure stoploss on exchange is canceled once the trade closes. --- freqtrade/freqtradebot.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 89abb0915..09afd8528 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -2387,6 +2387,8 @@ class FreqtradeBot(LoggingMixin): self.strategy.ft_stoploss_adjust( current_rate, trade, datetime.now(UTC), profit, 0, after_fill=True ) + if not trade.is_open: + self.cancel_stoploss_on_exchange(trade) # Updating wallets when order is closed self.wallets.update() return trade From faf552837aeee78d3ae4fba187141c2cf19069fa Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Dec 2025 06:24:57 +0100 Subject: [PATCH 3/3] test: add test for stoploss cancel skips --- tests/freqtradebot/test_integration.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/freqtradebot/test_integration.py b/tests/freqtradebot/test_integration.py index a1090104a..5d3df4c3b 100644 --- a/tests/freqtradebot/test_integration.py +++ b/tests/freqtradebot/test_integration.py @@ -800,9 +800,13 @@ def test_dca_handle_similar_open_order( # Should Create a new exit order freqtrade.exchange.amount_to_contract_precision = MagicMock(return_value=2) freqtrade.strategy.adjust_trade_position = MagicMock(return_value=-2) + msg = r"Skipping cancelling stoploss on exchange for.*" mocker.patch(f"{EXMS}._dry_is_price_crossed", return_value=False) + assert not log_has_re(msg, caplog) freqtrade.process() + assert log_has_re(msg, caplog) + trade = Trade.get_trades().first() assert trade.orders[-2].status == "closed"