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