From b5c5a95b64f8c35b2606da65114d6c65d66ec758 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 9 Aug 2022 20:09:35 +0200 Subject: [PATCH] FTX: Use conditionalOrders endpoint to get proper stop-market order id closes #7165 --- freqtrade/exchange/ftx.py | 14 +++++++++++--- tests/exchange/test_ftx.py | 18 +++++++++++++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 9ee6894f1..b3c219542 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -1,6 +1,6 @@ """ FTX exchange subclass """ import logging -from typing import Any, Dict, List, Tuple +from typing import Any, Dict, List, Optional, Tuple import ccxt @@ -116,9 +116,17 @@ class Ftx(Exchange): if len(order) == 1: if order[0].get('status') == 'closed': # Trigger order was triggered ... - real_order_id = order[0].get('info', {}).get('orderId') + real_order_id: Optional[str] = order[0].get('info', {}).get('orderId') # OrderId may be None for stoploss-market orders - # But contains "average" in these cases. + # So we need to get it through the endpoint + # /conditional_orders/{conditional_order_id}/triggers + if not real_order_id: + res = self._api.privateGetConditionalOrdersConditionalOrderIdTriggers( + params={'conditional_order_id': order_id}) + self._log_exchange_response('fetch_stoploss_order2', res) + real_order_id = res['result'][0]['orderId'] if res.get( + 'result', []) else None + if real_order_id: order1 = self._api.fetch_order(real_order_id, pair) self._log_exchange_response('fetch_stoploss_order1', order1) diff --git a/tests/exchange/test_ftx.py b/tests/exchange/test_ftx.py index 5a83b964a..5213c1b36 100644 --- a/tests/exchange/test_ftx.py +++ b/tests/exchange/test_ftx.py @@ -203,7 +203,7 @@ def test_fetch_stoploss_order_ftx(default_conf, mocker, limit_sell_order, limit_ 'info': { 'orderId': 'mocked_limit_sell', }}]) - api_mock.fetch_order = MagicMock(return_value=limit_sell_order) + api_mock.fetch_order = MagicMock(return_value=limit_sell_order.copy()) # No orderId field - no call to fetch_order resp = exchange.fetch_stoploss_order('X', 'TKN/BTC') @@ -219,11 +219,23 @@ def test_fetch_stoploss_order_ftx(default_conf, mocker, limit_sell_order, limit_ order = {'id': 'X', 'status': 'closed', 'info': {'orderId': None}, 'average': 0.254} api_mock.fetch_orders = MagicMock(return_value=[order]) api_mock.fetch_order.reset_mock() + api_mock.privateGetConditionalOrdersConditionalOrderIdTriggers = MagicMock( + return_value={'result': [ + {'orderId': 'mocked_market_sell', 'type': 'market', 'side': 'sell', 'price': 0.254} + ]}) resp = exchange.fetch_stoploss_order('X', 'TKN/BTC') assert resp # fetch_order not called (no regular order ID) - assert api_mock.fetch_order.call_count == 0 - assert order == order + assert api_mock.fetch_order.call_count == 1 + api_mock.privateGetConditionalOrdersConditionalOrderIdTriggers.call_count == 1 + expected_resp = limit_sell_order.copy() + expected_resp.update({ + 'id_stop': 'X', + 'id': 'X', + 'type': 'stop', + 'status_stop': 'triggered', + }) + assert expected_resp == resp with pytest.raises(InvalidOrderException): api_mock.fetch_orders = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))