From a32aed2225544073eb020d91ce1806e04b2d4ce1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 19 Feb 2022 10:07:32 +0100 Subject: [PATCH] Update FTX stoploss code to avoid exception for stoploss-market orders closes #6430, closes #6392 --- freqtrade/exchange/ftx.py | 19 +++++++++++-------- tests/exchange/test_ftx.py | 21 +++++++++++++++++++-- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index e9eb2fe19..a8bf9abac 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -106,15 +106,18 @@ class Ftx(Exchange): if order[0].get('status') == 'closed': # Trigger order was triggered ... real_order_id = order[0].get('info', {}).get('orderId') + # OrderId may be None for stoploss-market orders + # But contains "average" in these cases. + if real_order_id: + order1 = self._api.fetch_order(real_order_id, pair) + self._log_exchange_response('fetch_stoploss_order1', order1) + # Fake type to stop - as this was really a stop order. + order1['id_stop'] = order1['id'] + order1['id'] = order_id + order1['type'] = 'stop' + order1['status_stop'] = 'triggered' + return order1 - order1 = self._api.fetch_order(real_order_id, pair) - self._log_exchange_response('fetch_stoploss_order1', order1) - # Fake type to stop - as this was really a stop order. - order1['id_stop'] = order1['id'] - order1['id'] = order_id - order1['type'] = 'stop' - order1['status_stop'] = 'triggered' - return order1 return order[0] else: raise InvalidOrderException(f"Could not get stoploss order for id {order_id}") diff --git a/tests/exchange/test_ftx.py b/tests/exchange/test_ftx.py index 3794bb79c..c2fb90c9d 100644 --- a/tests/exchange/test_ftx.py +++ b/tests/exchange/test_ftx.py @@ -125,7 +125,7 @@ def test_stoploss_adjust_ftx(mocker, default_conf): assert not exchange.stoploss_adjust(1501, order) -def test_fetch_stoploss_order(default_conf, mocker, limit_sell_order): +def test_fetch_stoploss_order_ftx(default_conf, mocker, limit_sell_order): default_conf['dry_run'] = True order = MagicMock() order.myid = 123 @@ -147,9 +147,15 @@ def test_fetch_stoploss_order(default_conf, mocker, limit_sell_order): with pytest.raises(InvalidOrderException, match=r"Could not get stoploss order for id X"): exchange.fetch_stoploss_order('X', 'TKN/BTC')['status'] - api_mock.fetch_orders = MagicMock(return_value=[{'id': 'X', 'status': 'closed'}]) + # stoploss Limit order + api_mock.fetch_orders = MagicMock(return_value=[ + {'id': 'X', 'status': 'closed', + 'info': { + 'orderId': 'mocked_limit_sell', + }}]) api_mock.fetch_order = MagicMock(return_value=limit_sell_order) + # No orderId field - no call to fetch_order resp = exchange.fetch_stoploss_order('X', 'TKN/BTC') assert resp assert api_mock.fetch_order.call_count == 1 @@ -158,6 +164,17 @@ def test_fetch_stoploss_order(default_conf, mocker, limit_sell_order): assert resp['type'] == 'stop' assert resp['status_stop'] == 'triggered' + # Stoploss market order + # Contains no new Order, but "average" instead + 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() + 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 + with pytest.raises(InvalidOrderException): api_mock.fetch_orders = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) exchange = get_patched_exchange(mocker, default_conf, api_mock, id='ftx')