diff --git a/freqtrade/exchange/bybit.py b/freqtrade/exchange/bybit.py index d9b7c817f..ac6efb670 100644 --- a/freqtrade/exchange/bybit.py +++ b/freqtrade/exchange/bybit.py @@ -36,6 +36,9 @@ class Bybit(Exchange): "funding_fee_timeframe": "8h", "stoploss_on_exchange": True, "stoploss_order_types": {"limit": "limit", "market": "market"}, + "stop_price_param": "stopLossPrice", + # bybit response parsing fails to populate stopLossPrice + "stop_price_prop": "stopPrice", "stop_price_type_field": "triggerBy", "stop_price_type_value_mapping": { PriceType.LAST: "LastPrice", diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 348e556e5..89b162c0b 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -61,7 +61,8 @@ class Exchange: # or by specifying them in the configuration. _ft_has_default: Dict = { "stoploss_on_exchange": False, - "stop_price_param": "stopPrice", + "stop_price_param": "stopPrice", # Used for stoploss_on_exchange request + "stop_price_prop": "stopPrice", # Used for stoploss_on_exchange response parsing "order_time_in_force": ["GTC"], "ohlcv_params": {}, "ohlcv_candle_limit": 500, @@ -855,7 +856,7 @@ class Exchange: } if stop_loss: dry_order["info"] = {"stopPrice": dry_order["price"]} - dry_order[self._ft_has['stop_price_param']] = dry_order["price"] + dry_order[self._ft_has['stop_price_prop']] = dry_order["price"] # Workaround to avoid filling stoploss orders immediately dry_order["ft_order_type"] = "stoploss" orderbook: Optional[OrderBook] = None @@ -1007,7 +1008,7 @@ class Exchange: from freqtrade.persistence import Order order = Order.order_by_id(order_id) if order: - ccxt_order = order.to_ccxt_object(self._ft_has['stop_price_param']) + ccxt_order = order.to_ccxt_object(self._ft_has['stop_price_prop']) self._dry_run_open_orders[order_id] = ccxt_order return ccxt_order # Gracefully handle errors with dry-run orders. @@ -1115,7 +1116,7 @@ class Exchange: """ if not self._ft_has.get('stoploss_on_exchange'): raise OperationalException(f"stoploss is not implemented for {self.name}.") - price_param = self._ft_has['stop_price_param'] + price_param = self._ft_has['stop_price_prop'] return ( order.get(price_param, None) is None or ((side == "sell" and stop_loss > float(order[price_param])) or diff --git a/freqtrade/exchange/okx.py b/freqtrade/exchange/okx.py index b99911994..d52a61371 100644 --- a/freqtrade/exchange/okx.py +++ b/freqtrade/exchange/okx.py @@ -30,6 +30,7 @@ class Okx(Exchange): "stoploss_order_types": {"limit": "limit"}, "stoploss_on_exchange": True, "stop_price_param": "stopLossPrice", + "stop_price_prop": "stopLossPrice", } _ft_has_futures: Dict = { "tickers_have_quoteVolume": False, diff --git a/requirements.txt b/requirements.txt index e5874c026..efcb478ca 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ numpy==1.25.2; platform_machine == 'armv7l' pandas==2.0.3 pandas-ta==0.3.14b -ccxt==4.0.88 +ccxt==4.0.105 cryptography==41.0.3 aiohttp==3.8.5 SQLAlchemy==2.0.21 diff --git a/tests/exchange_online/conftest.py b/tests/exchange_online/conftest.py index 45da16dd5..c5f59ee0e 100644 --- a/tests/exchange_online/conftest.py +++ b/tests/exchange_online/conftest.py @@ -234,12 +234,12 @@ EXCHANGES = { "orderId": "1274754916287346280", "orderLinkId": "1666798627015730", "symbol": "SOLUSDT", - "createTime": "1674493798550", - "orderPrice": "15.5", - "orderQty": "1.1", - "orderType": "LIMIT", - "side": "BUY", - "status": "NEW", + "createdTime": "1674493798550", + "price": "15.5", + "qty": "1.1", + "orderType": "Limit", + "side": "Buy", + "orderStatus": "New", "timeInForce": "GTC", "accountId": "5555555", "execQty": "0", diff --git a/tests/exchange_online/test_ccxt_compat.py b/tests/exchange_online/test_ccxt_compat.py index e33875416..aa3dfdfae 100644 --- a/tests/exchange_online/test_ccxt_compat.py +++ b/tests/exchange_online/test_ccxt_compat.py @@ -58,8 +58,10 @@ class TestCCXTExchange: def test_ccxt_order_parse(self, exchange: EXCHANGE_FIXTURE_TYPE): exch, exchange_name = exchange if orders := EXCHANGES[exchange_name].get('sample_order'): + pair = 'SOL/USDT' for order in orders: - po = exch._api.parse_order(order) + market = exch._api.markets[pair] + po = exch._api.parse_order(order, market) assert isinstance(po['id'], str) assert po['id'] is not None if len(order.keys()) < 5: @@ -74,7 +76,7 @@ class TestCCXTExchange: if po['average'] is not None: assert isinstance(po['average'], float) assert po['average'] == 15.5 - assert po['symbol'] == 'SOL/USDT' + assert po['symbol'] == pair assert isinstance(po['amount'], float) assert po['amount'] == 1.1 assert isinstance(po['status'], str)