From 4177809f7131fbe64b86169c7dd75e44b0500815 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 1 Aug 2025 20:19:43 +0200 Subject: [PATCH 01/10] feat(bitget): stoploss order fetching attempts --- freqtrade/exchange/bitget.py | 67 +++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index 89a549ad1..fa13f75a2 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -1,9 +1,18 @@ import logging from datetime import timedelta +import ccxt + from freqtrade.enums import CandleType +from freqtrade.exceptions import ( + DDosProtection, + OperationalException, + RetryableOrderError, + TemporaryError, +) from freqtrade.exchange import Exchange -from freqtrade.exchange.exchange_types import FtHas +from freqtrade.exchange.common import API_RETRY_COUNT, retrier +from freqtrade.exchange.exchange_types import CcxtOrder, FtHas from freqtrade.util.datetime_helpers import dt_now, dt_ts @@ -21,6 +30,10 @@ class Bitget(Exchange): """ _ft_has: FtHas = { + "stoploss_on_exchange": True, + "stop_price_param": "stopPrice", + "stop_price_prop": "stopPrice", + "stoploss_order_types": {"limit": "limit", "market": "market"}, "ohlcv_candle_limit": 200, # 200 for historical candles, 1000 for recent ones. "order_time_in_force": ["GTC", "FOK", "IOC", "PO"], } @@ -50,3 +63,55 @@ class Bitget(Exchange): return 1000 return super().ohlcv_candle_limit(timeframe, candle_type, since_ms) + + def _convert_stop_order(self, pair: str, order_id: str, order: CcxtOrder) -> CcxtOrder: + if ( + order.get("status", "open") == "closed" + and (real_order_id := order.get("info", {}).get("ordId")) is not None + ): + # Once a order triggered, we fetch the regular followup order. + order_reg = self.fetch_order(real_order_id, pair) + self._log_exchange_response("fetch_stoploss_order1", order_reg) + order_reg["id_stop"] = order_reg["id"] + order_reg["id"] = order_id + order_reg["type"] = "stoploss" + order_reg["status_stop"] = "triggered" + return order_reg + order = self._order_contracts_to_amount(order) + order["type"] = "stoploss" + return order + + def _fetch_stop_order_fallback(self, order_id: str, pair: str) -> CcxtOrder: + params2 = { + "stop": True, + } + for method in ( + self._api.fetch_open_orders, + self._api.fetch_closed_orders, + ): + try: + orders = method(pair, params=params2) + orders_f = [order for order in orders if order["id"] == order_id] + if orders_f: + order = orders_f[0] + return self._convert_stop_order(pair, order_id, order) + except (ccxt.OrderNotFound, ccxt.InvalidOrder): + pass + except ccxt.DDoSProtection as e: + raise DDosProtection(e) from e + except (ccxt.OperationFailed, ccxt.ExchangeError) as e: + raise TemporaryError( + f"Could not get order due to {e.__class__.__name__}. Message: {e}" + ) from e + except ccxt.BaseError as e: + raise OperationalException(e) from e + raise RetryableOrderError(f"StoplossOrder not found (pair: {pair} id: {order_id}).") + + @retrier(retries=API_RETRY_COUNT) + def fetch_stoploss_order( + self, order_id: str, pair: str, params: dict | None = None + ) -> CcxtOrder: + if self._config["dry_run"]: + return self.fetch_dry_run_order(order_id) + + return self._fetch_stop_order_fallback(order_id, pair) From 8df0ce3fb5b04cfb110980d8ab6410aa49d8231f Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 1 Aug 2025 20:20:17 +0200 Subject: [PATCH 02/10] docs: add stoploss on exchange bitget --- docs/exchanges.md | 4 ++++ docs/stoploss.md | 1 + 2 files changed, 5 insertions(+) diff --git a/docs/exchanges.md b/docs/exchanges.md index 1f3b80fd1..d8008506c 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -344,6 +344,10 @@ Bitget requires a passphrase for each api key, you will therefore need to add th Bitget supports [time_in_force](configuration.md#understand-order_time_in_force). +!!! Tip "Stoploss on Exchange" + Bitget supports `stoploss_on_exchange` and can use both stop-loss-market and stop-loss-limit orders. It provides great advantages, so we recommend to benefit from it. + You can use either `"limit"` or `"market"` in the `order_types.stoploss` configuration setting to decide which type of stoploss shall be used. + ## Hyperliquid !!! Tip "Stoploss on Exchange" diff --git a/docs/stoploss.md b/docs/stoploss.md index bb7a2003f..f1a1e487d 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -31,6 +31,7 @@ The Order-type will be ignored if only one mode is available. | Binance | limit | | Binance Futures | market, limit | | Bingx | market, limit | +| Bitget | market, limit | | HTX | limit | | kraken | market, limit | | Gate | limit | From 95f58b2b8c6e48de55bd9caa13fc1a9d4e5f5507 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 5 Aug 2025 06:20:23 +0200 Subject: [PATCH 03/10] feat(bitget): add stoploss support properly --- freqtrade/exchange/bitget.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index fa13f75a2..d07e6fc6b 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -87,13 +87,14 @@ class Bitget(Exchange): } for method in ( self._api.fetch_open_orders, - self._api.fetch_closed_orders, + self._api.fetch_canceled_and_closed_orders, ): try: orders = method(pair, params=params2) orders_f = [order for order in orders if order["id"] == order_id] if orders_f: order = orders_f[0] + self._log_exchange_response("get_stop_order_fallback", order) return self._convert_stop_order(pair, order_id, order) except (ccxt.OrderNotFound, ccxt.InvalidOrder): pass From e36df7eff5832271dc7a13d7c6ed204f6527bd07 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 5 Aug 2025 06:23:21 +0200 Subject: [PATCH 04/10] feat(bitget): correctly cancel stop orders --- freqtrade/exchange/bitget.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index d07e6fc6b..5b443805d 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -116,3 +116,6 @@ class Bitget(Exchange): return self.fetch_dry_run_order(order_id) 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}) From aaecbc2206f36fd76ff5d823708012095be73158 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 5 Aug 2025 06:34:45 +0200 Subject: [PATCH 05/10] test(bitget): add test for stoploss emulation --- tests/exchange/test_bitget.py | 76 +++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 tests/exchange/test_bitget.py diff --git a/tests/exchange/test_bitget.py b/tests/exchange/test_bitget.py new file mode 100644 index 000000000..93f656836 --- /dev/null +++ b/tests/exchange/test_bitget.py @@ -0,0 +1,76 @@ +from datetime import UTC, datetime, timedelta +from unittest.mock import AsyncMock, MagicMock, PropertyMock + +import ccxt +import pytest + +from freqtrade.enums import CandleType, MarginMode, TradingMode +from freqtrade.exceptions import RetryableOrderError, TemporaryError +from freqtrade.exchange.common import API_RETRY_COUNT +from freqtrade.exchange.exchange import timeframe_to_minutes +from tests.conftest import EXMS, get_patched_exchange, log_has +from tests.exchange.test_exchange import ccxt_exceptionhandlers + + +@pytest.mark.usefixtures("init_persistence") +def test_fetch_stoploss_order_bitget(default_conf, mocker): + default_conf["dry_run"] = False + mocker.patch("freqtrade.exchange.common.time.sleep") + api_mock = MagicMock() + + exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bitget") + + api_mock.fetch_open_orders = MagicMock(return_value=[]) + api_mock.fetch_canceled_and_closed_orders = MagicMock(return_value=[]) + + with pytest.raises(RetryableOrderError): + exchange.fetch_stoploss_order("1234", "ETH/BTC") + assert api_mock.fetch_open_orders.call_count == API_RETRY_COUNT + 1 + assert api_mock.fetch_canceled_and_closed_orders.call_count == API_RETRY_COUNT + 1 + + api_mock.fetch_open_orders.reset_mock() + api_mock.fetch_canceled_and_closed_orders.reset_mock() + + api_mock.fetch_canceled_and_closed_orders = MagicMock( + return_value=[{"id": "1234", "status": "closed", "info": {"ordId": "123455"}}] + ) + mocker.patch(f"{EXMS}.fetch_order", MagicMock(return_value={"id": "123455"})) + resp = exchange.fetch_stoploss_order("1234", "ETH/BTC") + assert api_mock.fetch_open_orders.call_count == 1 + assert api_mock.fetch_canceled_and_closed_orders.call_count == 1 + + assert resp["id"] == "1234" + assert resp["id_stop"] == "123455" + assert resp["type"] == "stoploss" + + default_conf["dry_run"] = True + exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bitget") + dro_mock = mocker.patch(f"{EXMS}.fetch_dry_run_order", MagicMock(return_value={"id": "123455"})) + + api_mock.fetch_open_orders.reset_mock() + api_mock.fetch_canceled_and_closed_orders.reset_mock() + resp = exchange.fetch_stoploss_order("1234", "ETH/BTC") + + assert api_mock.fetch_open_orders.call_count == 0 + assert api_mock.fetch_canceled_and_closed_orders.call_count == 0 + assert dro_mock.call_count == 1 + + +def test_fetch_stoploss_order_bitget_exceptions(default_conf_usdt, mocker): + default_conf_usdt["dry_run"] = False + api_mock = MagicMock() + + # Test emulation of the stoploss getters + api_mock.fetch_canceled_and_closed_orders = MagicMock(return_value=[]) + + ccxt_exceptionhandlers( + mocker, + default_conf_usdt, + api_mock, + "bitget", + "fetch_stoploss_order", + "fetch_open_orders", + retries=API_RETRY_COUNT + 1, + order_id="12345", + pair="ETH/USDT", + ) From 8cc46648230e000f32bad7d8dd479a3cc7e913cf Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 5 Aug 2025 06:37:45 +0200 Subject: [PATCH 06/10] chore: fix imports --- tests/exchange/test_bitget.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/exchange/test_bitget.py b/tests/exchange/test_bitget.py index 93f656836..394504788 100644 --- a/tests/exchange/test_bitget.py +++ b/tests/exchange/test_bitget.py @@ -1,14 +1,10 @@ -from datetime import UTC, datetime, timedelta -from unittest.mock import AsyncMock, MagicMock, PropertyMock +from unittest.mock import MagicMock -import ccxt import pytest -from freqtrade.enums import CandleType, MarginMode, TradingMode -from freqtrade.exceptions import RetryableOrderError, TemporaryError +from freqtrade.exceptions import RetryableOrderError from freqtrade.exchange.common import API_RETRY_COUNT -from freqtrade.exchange.exchange import timeframe_to_minutes -from tests.conftest import EXMS, get_patched_exchange, log_has +from tests.conftest import EXMS, get_patched_exchange from tests.exchange.test_exchange import ccxt_exceptionhandlers From 83de41ad0e1f70b97df4ac91ab5cae72510e4dcc Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 8 Aug 2025 06:00:35 +0200 Subject: [PATCH 07/10] test(bitget): add test for bitget ohlcv limit --- tests/exchange/test_bitget.py | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/exchange/test_bitget.py b/tests/exchange/test_bitget.py index 394504788..cd3c774dd 100644 --- a/tests/exchange/test_bitget.py +++ b/tests/exchange/test_bitget.py @@ -1,9 +1,12 @@ +from datetime import timedelta from unittest.mock import MagicMock import pytest +from freqtrade.enums import CandleType from freqtrade.exceptions import RetryableOrderError from freqtrade.exchange.common import API_RETRY_COUNT +from freqtrade.util import dt_now, dt_ts from tests.conftest import EXMS, get_patched_exchange from tests.exchange.test_exchange import ccxt_exceptionhandlers @@ -70,3 +73,48 @@ def test_fetch_stoploss_order_bitget_exceptions(default_conf_usdt, mocker): order_id="12345", pair="ETH/USDT", ) + + +def test_bitget_ohlcv_candle_limit(mocker, default_conf_usdt): + api_mock = MagicMock() + api_mock.options = { + "fetchOHLCV": { + "maxRecentDaysPerTimeframe": { + "1m": 30, + "5m": 30, + "15m": 30, + "30m": 30, + "1h": 60, + "4h": 60, + "1d": 60, + } + } + } + + exch = get_patched_exchange(mocker, default_conf_usdt, api_mock, exchange="bitget") + timeframes = ("1m", "5m", "1h") + + for timeframe in timeframes: + assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT) == 1000 + assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES) == 1000 + assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK) == 200 + assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE) == 200 + + start_time = dt_ts(dt_now() - timedelta(days=17)) + assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == 1000 + assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == 1000 + assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 200 + assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200 + start_time = dt_ts(dt_now() - timedelta(days=48)) + length = 200 if timeframe in ("1m", "5m") else 1000 + assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == length + assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == length + assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 200 + assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200 + + start_time = dt_ts(dt_now() - timedelta(days=61)) + length = 200 + assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == length + assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == length + assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 200 + assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200 From 1a3c913176069de842bf394a8e158d990e44ea9d Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 8 Aug 2025 06:10:32 +0200 Subject: [PATCH 08/10] feat(bitget): Align bitget candle limits --- freqtrade/exchange/bitget.py | 2 +- tests/exchange/test_bitget.py | 9 +++++---- tests/exchange_online/test_ccxt_compat.py | 8 ++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index 5b443805d..28abbb10e 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -57,7 +57,7 @@ class Bitget(Exchange): timeframe_map = self._api.options["fetchOHLCV"]["maxRecentDaysPerTimeframe"] days = timeframe_map.get(timeframe, 30) - if candle_type in (CandleType.FUTURES, CandleType.SPOT) and ( + if candle_type in (CandleType.FUTURES, CandleType.SPOT, CandleType.MARK) and ( not since_ms or dt_ts(dt_now() - timedelta(days=days)) < since_ms ): return 1000 diff --git a/tests/exchange/test_bitget.py b/tests/exchange/test_bitget.py index cd3c774dd..f6ec2970a 100644 --- a/tests/exchange/test_bitget.py +++ b/tests/exchange/test_bitget.py @@ -76,6 +76,7 @@ def test_fetch_stoploss_order_bitget_exceptions(default_conf_usdt, mocker): def test_bitget_ohlcv_candle_limit(mocker, default_conf_usdt): + # This test is also a live test - so we're sure our limits are correct. api_mock = MagicMock() api_mock.options = { "fetchOHLCV": { @@ -97,24 +98,24 @@ def test_bitget_ohlcv_candle_limit(mocker, default_conf_usdt): for timeframe in timeframes: assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT) == 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES) == 1000 - assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK) == 200 + assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK) == 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE) == 200 start_time = dt_ts(dt_now() - timedelta(days=17)) assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == 1000 - assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 200 + assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200 start_time = dt_ts(dt_now() - timedelta(days=48)) length = 200 if timeframe in ("1m", "5m") else 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == length assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == length - assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 200 + assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == length assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200 start_time = dt_ts(dt_now() - timedelta(days=61)) length = 200 assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == length assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == length - assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 200 + assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == length assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200 diff --git a/tests/exchange_online/test_ccxt_compat.py b/tests/exchange_online/test_ccxt_compat.py index 2e8028ab7..c71e4809b 100644 --- a/tests/exchange_online/test_ccxt_compat.py +++ b/tests/exchange_online/test_ccxt_compat.py @@ -541,24 +541,24 @@ class TestCCXTExchange: for timeframe in timeframes: assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT) == 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES) == 1000 - assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK) == 200 + assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK) == 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE) == 200 start_time = dt_ts(dt_now() - timedelta(days=17)) assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == 1000 - assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 200 + assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200 start_time = dt_ts(dt_now() - timedelta(days=48)) length = 200 if timeframe in ("1m", "5m") else 1000 assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == length assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == length - assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 200 + assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == length assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200 start_time = dt_ts(dt_now() - timedelta(days=61)) length = 200 assert exch.ohlcv_candle_limit(timeframe, CandleType.SPOT, start_time) == length assert exch.ohlcv_candle_limit(timeframe, CandleType.FUTURES, start_time) == length - assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == 200 + assert exch.ohlcv_candle_limit(timeframe, CandleType.MARK, start_time) == length assert exch.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 200 From 38107664b55177f85233d46525657308c1e4c23d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 9 Aug 2025 15:47:29 +0200 Subject: [PATCH 09/10] feat(bitget): Add triggered stoploss detection logic --- freqtrade/exchange/bitget.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index 28abbb10e..b6b347c34 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -65,18 +65,25 @@ class Bitget(Exchange): return super().ohlcv_candle_limit(timeframe, candle_type, since_ms) def _convert_stop_order(self, pair: str, order_id: str, order: CcxtOrder) -> CcxtOrder: - if ( - order.get("status", "open") == "closed" - and (real_order_id := order.get("info", {}).get("ordId")) is not None - ): - # Once a order triggered, we fetch the regular followup order. - order_reg = self.fetch_order(real_order_id, pair) - self._log_exchange_response("fetch_stoploss_order1", order_reg) - order_reg["id_stop"] = order_reg["id"] - order_reg["id"] = order_id - order_reg["type"] = "stoploss" - order_reg["status_stop"] = "triggered" - return order_reg + if order.get("status", "open") == "closed": + # Use orderID as cliendOrderId filter to fetch the regular followup order. + # Could be done with "fetch_order" - but clientOid as filter doesn't seem to work + # https://www.bitget.com/api-doc/spot/trade/Get-Order-Info + + for method in ( + self._api.fetch_canceled_and_closed_orders, + self._api.fetch_open_orders, + ): + orders = method(pair) + orders_f = [order for order in orders if order["clientOrderId"] == order_id] + if orders_f: + order_reg = orders_f[0] + self._log_exchange_response("fetch_stoploss_order1", order_reg) + order_reg["id_stop"] = order_reg["id"] + order_reg["id"] = order_id + order_reg["type"] = "stoploss" + order_reg["status_stop"] = "triggered" + return order_reg order = self._order_contracts_to_amount(order) order["type"] = "stoploss" return order From b8883b790e8341f447c4b4161deb7276d462ca69 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 10 Aug 2025 16:35:53 +0200 Subject: [PATCH 10/10] test: adjust stoploss order getting for bitget --- tests/exchange/test_bitget.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/exchange/test_bitget.py b/tests/exchange/test_bitget.py index f6ec2970a..fdfaf7a7e 100644 --- a/tests/exchange/test_bitget.py +++ b/tests/exchange/test_bitget.py @@ -31,15 +31,16 @@ def test_fetch_stoploss_order_bitget(default_conf, mocker): api_mock.fetch_canceled_and_closed_orders.reset_mock() api_mock.fetch_canceled_and_closed_orders = MagicMock( - return_value=[{"id": "1234", "status": "closed", "info": {"ordId": "123455"}}] + return_value=[{"id": "1234", "status": "closed", "clientOrderId": "123455"}] ) - mocker.patch(f"{EXMS}.fetch_order", MagicMock(return_value={"id": "123455"})) + api_mock.fetch_open_orders = MagicMock(return_value=[{"id": "50110", "clientOrderId": "1234"}]) + resp = exchange.fetch_stoploss_order("1234", "ETH/BTC") - assert api_mock.fetch_open_orders.call_count == 1 - assert api_mock.fetch_canceled_and_closed_orders.call_count == 1 + assert api_mock.fetch_open_orders.call_count == 2 + assert api_mock.fetch_canceled_and_closed_orders.call_count == 2 assert resp["id"] == "1234" - assert resp["id_stop"] == "123455" + assert resp["id_stop"] == "50110" assert resp["type"] == "stoploss" default_conf["dry_run"] = True