From 5800002d4214ae4f3d2502b3046eed16024ab6e4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 15 Dec 2025 06:48:53 +0100 Subject: [PATCH 01/10] feat: add stoploss_fetch_requires_stop_param property --- freqtrade/exchange/binance.py | 1 + freqtrade/exchange/bitget.py | 1 + freqtrade/exchange/exchange.py | 1 + freqtrade/exchange/exchange_types.py | 1 + freqtrade/exchange/gate.py | 1 + freqtrade/exchange/okx.py | 1 + 6 files changed, 6 insertions(+) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index e05b9c702..258afc631 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -51,6 +51,7 @@ class Binance(Exchange): "funding_fee_candle_limit": 1000, "stoploss_order_types": {"limit": "stop", "market": "stop_market"}, "stoploss_blocks_assets": False, # Stoploss orders do not block assets + "stoploss_fetch_requires_stop_param": True, "tickers_have_price": False, "floor_leverage": True, "fetch_orders_limit_minutes": 7 * 1440, # "fetch_orders" is limited to 7 days diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index f0da55cb0..7fef482d2 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -31,6 +31,7 @@ class Bitget(Exchange): "stop_price_prop": "stopPrice", "stoploss_blocks_assets": False, # Stoploss orders do not block assets "stoploss_order_types": {"limit": "limit", "market": "market"}, + "stoploss_fetch_requires_stop_param": True, "ohlcv_candle_limit": 200, # 200 for historical candles, 1000 for recent ones. "order_time_in_force": ["GTC", "FOK", "IOC", "PO"], } diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index cc1cd3322..120b027e6 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -132,6 +132,7 @@ class Exchange: "stop_price_prop": "stopLossPrice", # Used for stoploss_on_exchange response parsing "stoploss_order_types": {}, "stoploss_blocks_assets": True, # By default stoploss orders block assets + "stoploss_fetch_requires_stop_param": False, # Require "stop": True" to fetch stop orders "order_time_in_force": ["GTC"], "ohlcv_params": {}, "ohlcv_has_history": True, # Some exchanges (Kraken) don't provide history via ohlcv diff --git a/freqtrade/exchange/exchange_types.py b/freqtrade/exchange/exchange_types.py index d2c2b46e2..0a69f8752 100644 --- a/freqtrade/exchange/exchange_types.py +++ b/freqtrade/exchange/exchange_types.py @@ -19,6 +19,7 @@ class FtHas(TypedDict, total=False): stop_price_type_value_mapping: dict stoploss_order_types: dict[str, str] stoploss_blocks_assets: bool + stoploss_fetch_requires_stop_param: bool # ohlcv ohlcv_params: dict ohlcv_candle_limit: int diff --git a/freqtrade/exchange/gate.py b/freqtrade/exchange/gate.py index 4c6d524d7..01be9d9cd 100644 --- a/freqtrade/exchange/gate.py +++ b/freqtrade/exchange/gate.py @@ -30,6 +30,7 @@ class Gate(Exchange): "stoploss_order_types": {"limit": "limit"}, "stop_price_param": "stopPrice", "stop_price_prop": "stopPrice", + "stoploss_fetch_requires_stop_param": True, "l2_limit_upper": 1000, "marketOrderRequiresPrice": True, "trades_has_history": False, # Endpoint would support this - but ccxt doesn't. diff --git a/freqtrade/exchange/okx.py b/freqtrade/exchange/okx.py index 14f20a0bb..43924d706 100644 --- a/freqtrade/exchange/okx.py +++ b/freqtrade/exchange/okx.py @@ -31,6 +31,7 @@ class Okx(Exchange): "ohlcv_candle_limit": 100, # Warning, special case with data prior to X months "stoploss_order_types": {"limit": "limit"}, "stoploss_on_exchange": True, + "stoploss_fetch_requires_stop_param": True, "trades_has_history": False, # Endpoint doesn't have a "since" parameter "ws_enabled": True, } From 84e9251fcd3d2f5bbab7404f3337791f6bf5da72 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 15 Dec 2025 07:01:23 +0100 Subject: [PATCH 02/10] test: use object patching instead of direct assignment --- tests/exchange/test_gate.py | 4 +--- tests/exchange/test_okx.py | 11 +++++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/exchange/test_gate.py b/tests/exchange/test_gate.py index ee3d70c84..9c3246f46 100644 --- a/tests/exchange/test_gate.py +++ b/tests/exchange/test_gate.py @@ -42,9 +42,7 @@ def test_fetch_stoploss_order_gate(default_conf, mocker): def test_cancel_stoploss_order_gate(default_conf, mocker): exchange = get_patched_exchange(mocker, default_conf, exchange="gate") - - cancel_order_mock = MagicMock() - exchange.cancel_order = cancel_order_mock + cancel_order_mock = mocker.patch.object(exchange, "cancel_order") exchange.cancel_stoploss_order("1234", "ETH/BTC") assert cancel_order_mock.call_count == 1 diff --git a/tests/exchange/test_okx.py b/tests/exchange/test_okx.py index 750afbd40..43e14a51a 100644 --- a/tests/exchange/test_okx.py +++ b/tests/exchange/test_okx.py @@ -661,14 +661,13 @@ def test_stoploss_adjust_okx(mocker, default_conf, sl1, sl2, sl3, side): def test_stoploss_cancel_okx(mocker, default_conf): exchange = get_patched_exchange(mocker, default_conf, exchange="okx") - - exchange.cancel_order = MagicMock() + co_mock = mocker.patch.object(exchange, "cancel_order") exchange.cancel_stoploss_order("1234", "ETH/USDT") - assert exchange.cancel_order.call_count == 1 - assert exchange.cancel_order.call_args_list[0][1]["order_id"] == "1234" - assert exchange.cancel_order.call_args_list[0][1]["pair"] == "ETH/USDT" - assert exchange.cancel_order.call_args_list[0][1]["params"] == {"stop": True} + assert co_mock.call_count == 1 + assert co_mock.call_args_list[0][1]["order_id"] == "1234" + assert co_mock.call_args_list[0][1]["pair"] == "ETH/USDT" + assert co_mock.call_args_list[0][1]["params"] == {"stop": True} def test__get_stop_params_okx(mocker, default_conf): From cf6bf1b7b5d1c367da9754a4d3e20a9b3cd46a61 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 15 Dec 2025 06:56:26 +0100 Subject: [PATCH 03/10] refactor: reduce code duplication for cancel_stop_orders --- freqtrade/exchange/binance.py | 6 ------ freqtrade/exchange/bitget.py | 3 --- freqtrade/exchange/exchange.py | 3 +++ freqtrade/exchange/gate.py | 3 --- freqtrade/exchange/okx.py | 3 --- 5 files changed, 3 insertions(+), 15 deletions(-) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 258afc631..1486e5665 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -168,12 +168,6 @@ class Binance(Exchange): return order - def cancel_stoploss_order(self, order_id: str, pair: str, params: dict | None = None) -> dict: - if self.trading_mode == TradingMode.FUTURES: - params = params or {} - params.update({"stop": True}) - return self.cancel_order(order_id=order_id, pair=pair, params=params) - def get_historic_ohlcv( self, pair: str, diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index 7fef482d2..7c791a5a5 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -129,9 +129,6 @@ class Bitget(Exchange): return self._fetch_stop_order_fallback(order_id, pair) - def cancel_stoploss_order(self, order_id: str, pair: str, params: dict | None = None) -> dict: - return self.cancel_order(order_id=order_id, pair=pair, params={"stop": True}) - @retrier def additional_exchange_init(self) -> None: """ diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 120b027e6..18390d895 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1742,6 +1742,9 @@ class Exchange: raise OperationalException(e) from e def cancel_stoploss_order(self, order_id: str, pair: str, params: dict | None = None) -> dict: + if self.get_option("stoploss_fetch_requires_stop_param"): + params = params or {} + params["stop"] = True return self.cancel_order(order_id, pair, params) def is_cancel_order_result_suitable(self, corder) -> TypeGuard[CcxtOrder]: diff --git a/freqtrade/exchange/gate.py b/freqtrade/exchange/gate.py index 01be9d9cd..689959768 100644 --- a/freqtrade/exchange/gate.py +++ b/freqtrade/exchange/gate.py @@ -152,6 +152,3 @@ class Gate(Exchange): return order1 return order - - def cancel_stoploss_order(self, order_id: str, pair: str, params: dict | None = None) -> dict: - return self.cancel_order(order_id=order_id, pair=pair, params={"stop": True}) diff --git a/freqtrade/exchange/okx.py b/freqtrade/exchange/okx.py index 43924d706..d1777006d 100644 --- a/freqtrade/exchange/okx.py +++ b/freqtrade/exchange/okx.py @@ -264,9 +264,6 @@ class Okx(Exchange): return safe_value_fallback2(order, order, "id_stop", "id") return order["id"] - def cancel_stoploss_order(self, order_id: str, pair: str, params: dict | None = None) -> dict: - return self.cancel_order(order_id=order_id, pair=pair, params={"stop": True}) - def _fetch_orders_emulate(self, pair: str, since_ms: int) -> list[CcxtOrder]: orders = [] From f0908a10436b0ddf3842d1ace3d15e8c2edfd74a Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 15 Dec 2025 07:01:59 +0100 Subject: [PATCH 04/10] test: adapt test to new call logic --- tests/exchange/test_gate.py | 6 +++--- tests/exchange/test_okx.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/exchange/test_gate.py b/tests/exchange/test_gate.py index 9c3246f46..c78f9ce3e 100644 --- a/tests/exchange/test_gate.py +++ b/tests/exchange/test_gate.py @@ -46,9 +46,9 @@ def test_cancel_stoploss_order_gate(default_conf, mocker): exchange.cancel_stoploss_order("1234", "ETH/BTC") assert cancel_order_mock.call_count == 1 - assert cancel_order_mock.call_args_list[0][1]["order_id"] == "1234" - assert cancel_order_mock.call_args_list[0][1]["pair"] == "ETH/BTC" - assert cancel_order_mock.call_args_list[0][1]["params"] == {"stop": True} + assert cancel_order_mock.call_args_list[0][0][0] == "1234" + assert cancel_order_mock.call_args_list[0][0][1] == "ETH/BTC" + assert cancel_order_mock.call_args_list[0][0][2] == {"stop": True} @pytest.mark.parametrize( diff --git a/tests/exchange/test_okx.py b/tests/exchange/test_okx.py index 43e14a51a..5c5b8ed57 100644 --- a/tests/exchange/test_okx.py +++ b/tests/exchange/test_okx.py @@ -665,9 +665,9 @@ def test_stoploss_cancel_okx(mocker, default_conf): exchange.cancel_stoploss_order("1234", "ETH/USDT") assert co_mock.call_count == 1 - assert co_mock.call_args_list[0][1]["order_id"] == "1234" - assert co_mock.call_args_list[0][1]["pair"] == "ETH/USDT" - assert co_mock.call_args_list[0][1]["params"] == {"stop": True} + assert co_mock.call_args_list[0][0][0] == "1234" + assert co_mock.call_args_list[0][0][1] == "ETH/USDT" + assert co_mock.call_args_list[0][0][2] == {"stop": True} def test__get_stop_params_okx(mocker, default_conf): From d4aee7e433441f6fc03da1d0f6f20fc05e277a89 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 15 Dec 2025 07:04:40 +0100 Subject: [PATCH 05/10] test: add autospec=True to just changed tests --- tests/exchange/test_gate.py | 2 +- tests/exchange/test_okx.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/exchange/test_gate.py b/tests/exchange/test_gate.py index c78f9ce3e..5346e0efc 100644 --- a/tests/exchange/test_gate.py +++ b/tests/exchange/test_gate.py @@ -42,7 +42,7 @@ def test_fetch_stoploss_order_gate(default_conf, mocker): def test_cancel_stoploss_order_gate(default_conf, mocker): exchange = get_patched_exchange(mocker, default_conf, exchange="gate") - cancel_order_mock = mocker.patch.object(exchange, "cancel_order") + cancel_order_mock = mocker.patch.object(exchange, "cancel_order", autospec=True) exchange.cancel_stoploss_order("1234", "ETH/BTC") assert cancel_order_mock.call_count == 1 diff --git a/tests/exchange/test_okx.py b/tests/exchange/test_okx.py index 5c5b8ed57..5c8983858 100644 --- a/tests/exchange/test_okx.py +++ b/tests/exchange/test_okx.py @@ -661,7 +661,7 @@ def test_stoploss_adjust_okx(mocker, default_conf, sl1, sl2, sl3, side): def test_stoploss_cancel_okx(mocker, default_conf): exchange = get_patched_exchange(mocker, default_conf, exchange="okx") - co_mock = mocker.patch.object(exchange, "cancel_order") + co_mock = mocker.patch.object(exchange, "cancel_order", autospec=True) exchange.cancel_stoploss_order("1234", "ETH/USDT") assert co_mock.call_count == 1 From c8d74baaa1a4fefc1064895d67402e5c0e9786cd Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 16 Dec 2025 20:43:02 +0100 Subject: [PATCH 06/10] refactor: unify fetch_stoploss_order --- freqtrade/exchange/binance.py | 25 ++----------------------- freqtrade/exchange/exchange.py | 20 +++++++++++++++++++- freqtrade/exchange/exchange_types.py | 1 + freqtrade/exchange/gate.py | 21 ++------------------- 4 files changed, 24 insertions(+), 43 deletions(-) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 1486e5665..3b3f0fb73 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -17,7 +17,7 @@ from freqtrade.exchange.binance_public_data import ( download_archive_trades, ) from freqtrade.exchange.common import retrier -from freqtrade.exchange.exchange_types import CcxtOrder, FtHas, Tickers +from freqtrade.exchange.exchange_types import FtHas, Tickers from freqtrade.exchange.exchange_utils_timeframe import timeframe_to_msecs from freqtrade.misc import deep_merge_dicts, json_load from freqtrade.util import FtTTLCache @@ -52,6 +52,7 @@ class Binance(Exchange): "stoploss_order_types": {"limit": "stop", "market": "stop_market"}, "stoploss_blocks_assets": False, # Stoploss orders do not block assets "stoploss_fetch_requires_stop_param": True, + "stoploss_algo_order_info_id": "actualOrderId", "tickers_have_price": False, "floor_leverage": True, "fetch_orders_limit_minutes": 7 * 1440, # "fetch_orders" is limited to 7 days @@ -146,28 +147,6 @@ class Binance(Exchange): except ccxt.BaseError as e: raise OperationalException(e) from e - def fetch_stoploss_order( - self, order_id: str, pair: str, params: dict | None = None - ) -> CcxtOrder: - if self.trading_mode == TradingMode.FUTURES: - params = params or {} - params.update({"stop": True}) - order = self.fetch_order(order_id, pair, params) - if self.trading_mode == TradingMode.FUTURES and order.get("status", "open") == "closed": - # Places a real order - which we need to fetch explicitly. - - if new_orderid := order.get("info", {}).get("actualOrderId"): - order1 = self.fetch_order(order_id=new_orderid, pair=pair, params={}) - order1["id_stop"] = order1["id"] - order1["id"] = order_id - order1["type"] = "stoploss" - order1["stopPrice"] = order.get("stopPrice") - order1["status_stop"] = "triggered" - - return order1 - - return order - def get_historic_ohlcv( self, pair: str, diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 18390d895..bf87e76e7 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1688,7 +1688,25 @@ class Exchange: def fetch_stoploss_order( self, order_id: str, pair: str, params: dict | None = None ) -> CcxtOrder: - return self.fetch_order(order_id, pair, params) + if self.get_option("stoploss_fetch_requires_stop_param"): + params = params or {} + params["stop"] = True + order = self.fetch_order(order_id, pair, params) + if (val := self.get_option("stoploss_algo_order_info_id")) and order.get( + "status", "open" + ) == "closed": + if new_orderid := order.get("info", {}).get(val): + # Fetch real order, which was placed by the algo order. + order1 = self.fetch_order(order_id=new_orderid, pair=pair, params=None) + order1["id_stop"] = order1["id"] + order1["id"] = order_id + order1["type"] = "stoploss" + order1["stopPrice"] = order.get("stopPrice") + order1["status_stop"] = "triggered" + + return order1 + + return order def fetch_order_or_stoploss_order( self, order_id: str, pair: str, stoploss_order: bool = False diff --git a/freqtrade/exchange/exchange_types.py b/freqtrade/exchange/exchange_types.py index 0a69f8752..d427f6182 100644 --- a/freqtrade/exchange/exchange_types.py +++ b/freqtrade/exchange/exchange_types.py @@ -20,6 +20,7 @@ class FtHas(TypedDict, total=False): stoploss_order_types: dict[str, str] stoploss_blocks_assets: bool stoploss_fetch_requires_stop_param: bool + stoploss_algo_order_info_id: str # ohlcv ohlcv_params: dict ohlcv_candle_limit: int diff --git a/freqtrade/exchange/gate.py b/freqtrade/exchange/gate.py index 689959768..83b61c6ad 100644 --- a/freqtrade/exchange/gate.py +++ b/freqtrade/exchange/gate.py @@ -31,6 +31,7 @@ class Gate(Exchange): "stop_price_param": "stopPrice", "stop_price_prop": "stopPrice", "stoploss_fetch_requires_stop_param": True, + "stoploss_algo_order_info_id": "fired_order_id", "l2_limit_upper": 1000, "marketOrderRequiresPrice": True, "trades_has_history": False, # Endpoint would support this - but ccxt doesn't. @@ -43,6 +44,7 @@ class Gate(Exchange): "stop_price_type_field": "price_type", "l2_limit_upper": 300, "stoploss_blocks_assets": False, + "stoploss_algo_order_info_id": "trade_id", "stop_price_type_value_mapping": { PriceType.LAST: 0, PriceType.MARK: 1, @@ -133,22 +135,3 @@ class Gate(Exchange): def get_order_id_conditional(self, order: CcxtOrder) -> str: return safe_value_fallback2(order, order, "id_stop", "id") - - def fetch_stoploss_order( - self, order_id: str, pair: str, params: dict | None = None - ) -> CcxtOrder: - order = self.fetch_order(order_id=order_id, pair=pair, params={"stop": True}) - if order.get("status", "open") == "closed": - # Places a real order - which we need to fetch explicitly. - val = "trade_id" if self.trading_mode == TradingMode.FUTURES else "fired_order_id" - - if new_orderid := order.get("info", {}).get(val): - order1 = self.fetch_order(order_id=new_orderid, pair=pair, params=params) - order1["id_stop"] = order1["id"] - order1["id"] = order_id - order1["type"] = "stoploss" - order1["stopPrice"] = order.get("stopPrice") - order1["status_stop"] = "triggered" - - return order1 - return order From c63fba2e578ec30b7ad1a39b29e905e6fd31a2b7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 16 Dec 2025 20:44:06 +0100 Subject: [PATCH 07/10] chore: rename flag to better match what it does --- freqtrade/exchange/binance.py | 2 +- freqtrade/exchange/bitget.py | 2 +- freqtrade/exchange/exchange.py | 6 +++--- freqtrade/exchange/exchange_types.py | 2 +- freqtrade/exchange/gate.py | 2 +- freqtrade/exchange/okx.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 3b3f0fb73..40de29470 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -51,7 +51,7 @@ class Binance(Exchange): "funding_fee_candle_limit": 1000, "stoploss_order_types": {"limit": "stop", "market": "stop_market"}, "stoploss_blocks_assets": False, # Stoploss orders do not block assets - "stoploss_fetch_requires_stop_param": True, + "stoploss_query_requires_stop_flag": True, "stoploss_algo_order_info_id": "actualOrderId", "tickers_have_price": False, "floor_leverage": True, diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index 7c791a5a5..d298d14d6 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -31,7 +31,7 @@ class Bitget(Exchange): "stop_price_prop": "stopPrice", "stoploss_blocks_assets": False, # Stoploss orders do not block assets "stoploss_order_types": {"limit": "limit", "market": "market"}, - "stoploss_fetch_requires_stop_param": True, + "stoploss_query_requires_stop_flag": True, "ohlcv_candle_limit": 200, # 200 for historical candles, 1000 for recent ones. "order_time_in_force": ["GTC", "FOK", "IOC", "PO"], } diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index bf87e76e7..e01d341e6 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -132,7 +132,7 @@ class Exchange: "stop_price_prop": "stopLossPrice", # Used for stoploss_on_exchange response parsing "stoploss_order_types": {}, "stoploss_blocks_assets": True, # By default stoploss orders block assets - "stoploss_fetch_requires_stop_param": False, # Require "stop": True" to fetch stop orders + "stoploss_query_requires_stop_flag": False, # Require "stop": True" to fetch stop orders "order_time_in_force": ["GTC"], "ohlcv_params": {}, "ohlcv_has_history": True, # Some exchanges (Kraken) don't provide history via ohlcv @@ -1688,7 +1688,7 @@ class Exchange: def fetch_stoploss_order( self, order_id: str, pair: str, params: dict | None = None ) -> CcxtOrder: - if self.get_option("stoploss_fetch_requires_stop_param"): + if self.get_option("stoploss_query_requires_stop_flag"): params = params or {} params["stop"] = True order = self.fetch_order(order_id, pair, params) @@ -1760,7 +1760,7 @@ class Exchange: raise OperationalException(e) from e def cancel_stoploss_order(self, order_id: str, pair: str, params: dict | None = None) -> dict: - if self.get_option("stoploss_fetch_requires_stop_param"): + if self.get_option("stoploss_query_requires_stop_flag"): params = params or {} params["stop"] = True return self.cancel_order(order_id, pair, params) diff --git a/freqtrade/exchange/exchange_types.py b/freqtrade/exchange/exchange_types.py index d427f6182..fd5faecf7 100644 --- a/freqtrade/exchange/exchange_types.py +++ b/freqtrade/exchange/exchange_types.py @@ -19,7 +19,7 @@ class FtHas(TypedDict, total=False): stop_price_type_value_mapping: dict stoploss_order_types: dict[str, str] stoploss_blocks_assets: bool - stoploss_fetch_requires_stop_param: bool + stoploss_query_requires_stop_flag: bool stoploss_algo_order_info_id: str # ohlcv ohlcv_params: dict diff --git a/freqtrade/exchange/gate.py b/freqtrade/exchange/gate.py index 83b61c6ad..585dddad5 100644 --- a/freqtrade/exchange/gate.py +++ b/freqtrade/exchange/gate.py @@ -30,7 +30,7 @@ class Gate(Exchange): "stoploss_order_types": {"limit": "limit"}, "stop_price_param": "stopPrice", "stop_price_prop": "stopPrice", - "stoploss_fetch_requires_stop_param": True, + "stoploss_query_requires_stop_flag": True, "stoploss_algo_order_info_id": "fired_order_id", "l2_limit_upper": 1000, "marketOrderRequiresPrice": True, diff --git a/freqtrade/exchange/okx.py b/freqtrade/exchange/okx.py index d1777006d..4640d5647 100644 --- a/freqtrade/exchange/okx.py +++ b/freqtrade/exchange/okx.py @@ -31,7 +31,7 @@ class Okx(Exchange): "ohlcv_candle_limit": 100, # Warning, special case with data prior to X months "stoploss_order_types": {"limit": "limit"}, "stoploss_on_exchange": True, - "stoploss_fetch_requires_stop_param": True, + "stoploss_query_requires_stop_flag": True, "trades_has_history": False, # Endpoint doesn't have a "since" parameter "ws_enabled": True, } From d5b296c75cf8c713a235719852bc4706518bd0f5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 16 Dec 2025 20:47:33 +0100 Subject: [PATCH 08/10] test: fix gate stoploss test --- tests/exchange/test_gate.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/exchange/test_gate.py b/tests/exchange/test_gate.py index 5346e0efc..66dfd13f8 100644 --- a/tests/exchange/test_gate.py +++ b/tests/exchange/test_gate.py @@ -16,9 +16,9 @@ def test_fetch_stoploss_order_gate(default_conf, mocker): exchange.fetch_stoploss_order("1234", "ETH/BTC") assert fetch_order_mock.call_count == 1 - assert fetch_order_mock.call_args_list[0][1]["order_id"] == "1234" - assert fetch_order_mock.call_args_list[0][1]["pair"] == "ETH/BTC" - assert fetch_order_mock.call_args_list[0][1]["params"] == {"stop": True} + assert fetch_order_mock.call_args_list[0][0][0] == "1234" + assert fetch_order_mock.call_args_list[0][0][1] == "ETH/BTC" + assert fetch_order_mock.call_args_list[0][0][2] == {"stop": True} default_conf["trading_mode"] = "futures" default_conf["margin_mode"] = "isolated" @@ -36,7 +36,7 @@ def test_fetch_stoploss_order_gate(default_conf, mocker): exchange.fetch_stoploss_order("1234", "ETH/BTC") assert exchange.fetch_order.call_count == 2 - assert exchange.fetch_order.call_args_list[0][1]["order_id"] == "1234" + assert exchange.fetch_order.call_args_list[0][0][0] == "1234" assert exchange.fetch_order.call_args_list[1][1]["order_id"] == "222555" From 278bd0e97d762983507eded604e043a963aa46eb Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 16 Dec 2025 21:02:36 +0100 Subject: [PATCH 09/10] chore: improve variable naming --- freqtrade/exchange/exchange.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index e01d341e6..9d6807fa2 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1692,19 +1692,18 @@ class Exchange: params = params or {} params["stop"] = True order = self.fetch_order(order_id, pair, params) - if (val := self.get_option("stoploss_algo_order_info_id")) and order.get( - "status", "open" - ) == "closed": + val = self.get_option("stoploss_algo_order_info_id") + if val and order.get("status", "open") == "closed": if new_orderid := order.get("info", {}).get(val): # Fetch real order, which was placed by the algo order. - order1 = self.fetch_order(order_id=new_orderid, pair=pair, params=None) - order1["id_stop"] = order1["id"] - order1["id"] = order_id - order1["type"] = "stoploss" - order1["stopPrice"] = order.get("stopPrice") - order1["status_stop"] = "triggered" + actual_order = self.fetch_order(order_id=new_orderid, pair=pair, params=None) + actual_order["id_stop"] = actual_order["id"] + actual_order["id"] = order_id + actual_order["type"] = "stoploss" + actual_order["stopPrice"] = order.get("stopPrice") + actual_order["status_stop"] = "triggered" - return order1 + return actual_order return order From 1143aba6718309ab928a0519c222035c194adf86 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 16 Dec 2025 21:02:48 +0100 Subject: [PATCH 10/10] test: simplify test call --- tests/exchange/test_okx.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/exchange/test_okx.py b/tests/exchange/test_okx.py index 5c8983858..ae52bd1ff 100644 --- a/tests/exchange/test_okx.py +++ b/tests/exchange/test_okx.py @@ -665,9 +665,10 @@ def test_stoploss_cancel_okx(mocker, default_conf): exchange.cancel_stoploss_order("1234", "ETH/USDT") assert co_mock.call_count == 1 - assert co_mock.call_args_list[0][0][0] == "1234" - assert co_mock.call_args_list[0][0][1] == "ETH/USDT" - assert co_mock.call_args_list[0][0][2] == {"stop": True} + args, _ = co_mock.call_args + assert args[0] == "1234" + assert args[1] == "ETH/USDT" + assert args[2] == {"stop": True} def test__get_stop_params_okx(mocker, default_conf):