From d35b2e3b8f545aee81742a8698d0db319210fa40 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 13 Jun 2021 11:06:34 +0200 Subject: [PATCH] Update ftx stoploss logic to properly detect correct trades closes #5045 --- freqtrade/exchange/ftx.py | 13 ++++++++++++- freqtrade/freqtradebot.py | 9 +++++++-- tests/exchange/test_ftx.py | 17 ++++++++++++++--- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 105389828..3184c2524 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -100,6 +100,17 @@ class Ftx(Exchange): order = [order for order in orders if order['id'] == order_id] if len(order) == 1: + if order[0].get('status') == 'closed': + # Trigger order was triggered ... + real_order_id = order[0].get('info', {}).get('orderId') + + order1 = self._api.fetch_order(real_order_id, pair) + # 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}") @@ -134,5 +145,5 @@ class Ftx(Exchange): def get_order_id_conditional(self, order: Dict[str, Any]) -> str: if order['type'] == 'stop': - return safe_value_fallback2(order['info'], order, 'orderId', 'id') + return safe_value_fallback2(order, order, 'id_stop', 'id') return order['id'] diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7aca0fcb2..a2e7fcb5d 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -816,8 +816,13 @@ class FreqtradeBot(LoggingMixin): logger.warning('Stoploss order was cancelled, but unable to recreate one.') # Finally we check if stoploss on exchange should be moved up because of trailing. - if stoploss_order and (self.config.get('trailing_stop', False) - or self.config.get('use_custom_stoploss', False)): + # Triggered Orders are now real orders - so don't replace stoploss anymore + if ( + stoploss_order + and stoploss_order.get('status_stop') != 'triggered' + and (self.config.get('trailing_stop', False) + or self.config.get('use_custom_stoploss', False)) + ): # if trailing stoploss is enabled we check if stoploss value has changed # in which case we cancel stoploss order and put another one with new # value immediately diff --git a/tests/exchange/test_ftx.py b/tests/exchange/test_ftx.py index 63d99acdf..3794bb79c 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): +def test_fetch_stoploss_order(default_conf, mocker, limit_sell_order): default_conf['dry_run'] = True order = MagicMock() order.myid = 123 @@ -147,6 +147,17 @@ def test_fetch_stoploss_order(default_conf, mocker): 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'}]) + api_mock.fetch_order = MagicMock(return_value=limit_sell_order) + + resp = exchange.fetch_stoploss_order('X', 'TKN/BTC') + assert resp + assert api_mock.fetch_order.call_count == 1 + assert resp['id_stop'] == 'mocked_limit_sell' + assert resp['id'] == 'X' + assert resp['type'] == 'stop' + assert resp['status_stop'] == 'triggered' + 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') @@ -165,8 +176,8 @@ def test_get_order_id(mocker, default_conf): 'type': STOPLOSS_ORDERTYPE, 'price': 1500, 'id': '1111', + 'id_stop': '1234', 'info': { - 'orderId': '1234' } } assert exchange.get_order_id_conditional(order) == '1234' @@ -175,8 +186,8 @@ def test_get_order_id(mocker, default_conf): 'type': 'limit', 'price': 1500, 'id': '1111', + 'id_stop': '1234', 'info': { - 'orderId': '1234' } } assert exchange.get_order_id_conditional(order) == '1111'