From c6124180fe14bcdf9f78676d021c2e8e6880d1e7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 28 Jun 2020 19:45:42 +0200 Subject: [PATCH] Fix bug when fetching orders fails --- freqtrade/exceptions.py | 7 +++++++ freqtrade/exchange/common.py | 14 ++++++++------ freqtrade/exchange/exchange.py | 5 ++++- tests/exchange/test_exchange.py | 12 ++++++++++++ tests/test_freqtradebot.py | 2 +- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/freqtrade/exceptions.py b/freqtrade/exceptions.py index 995a2cdb7..c85fccc4b 100644 --- a/freqtrade/exceptions.py +++ b/freqtrade/exceptions.py @@ -37,6 +37,13 @@ class InvalidOrderException(FreqtradeException): """ +class RetryableOrderError(InvalidOrderException): + """ + This is returned when the order is not found. + This Error will be repeated with increasing backof (in line with DDosError). + """ + + class ExchangeError(DependencyException): """ Error raised out of the exchange. diff --git a/freqtrade/exchange/common.py b/freqtrade/exchange/common.py index d3ba8cef6..9b7d8dfea 100644 --- a/freqtrade/exchange/common.py +++ b/freqtrade/exchange/common.py @@ -3,7 +3,8 @@ import logging import time from functools import wraps -from freqtrade.exceptions import DDosProtection, TemporaryError +from freqtrade.exceptions import (DDosProtection, RetryableOrderError, + TemporaryError) logger = logging.getLogger(__name__) @@ -109,8 +110,8 @@ def retrier_async(f): count -= 1 kwargs.update({'count': count}) logger.warning('retrying %s() still for %s times', f.__name__, count) - if isinstance(ex, DDosProtection): - await asyncio.sleep(1) + if isinstance(ex, DDosProtection) or isinstance(ex, RetryableOrderError): + await asyncio.sleep(calculate_backoff(count + 1, API_RETRY_COUNT)) return await wrapper(*args, **kwargs) else: logger.warning('Giving up retrying: %s()', f.__name__) @@ -125,14 +126,15 @@ def retrier(_func=None, retries=API_RETRY_COUNT): count = kwargs.pop('count', retries) try: return f(*args, **kwargs) - except TemporaryError as ex: + except (TemporaryError, RetryableOrderError) as ex: logger.warning('%s() returned exception: "%s"', f.__name__, ex) if count > 0: count -= 1 kwargs.update({'count': count}) logger.warning('retrying %s() still for %s times', f.__name__, count) - if isinstance(ex, DDosProtection): - time.sleep(calculate_backoff(count, retries)) + if isinstance(ex, DDosProtection) or isinstance(ex, RetryableOrderError): + # increasing backoff + time.sleep(calculate_backoff(count + 1, retries)) return wrapper(*args, **kwargs) else: logger.warning('Giving up retrying: %s()', f.__name__) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index daa73ca35..a3a548176 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -22,7 +22,7 @@ from freqtrade.constants import ListPairsWithTimeframes from freqtrade.data.converter import ohlcv_to_dataframe, trades_dict_to_list from freqtrade.exceptions import (DDosProtection, ExchangeError, InvalidOrderException, OperationalException, - TemporaryError) + RetryableOrderError, TemporaryError) from freqtrade.exchange.common import BAD_EXCHANGES, retrier, retrier_async from freqtrade.misc import deep_merge_dicts, safe_value_fallback @@ -1015,6 +1015,9 @@ class Exchange: f'Tried to get an invalid dry-run-order (id: {order_id}). Message: {e}') from e try: return self._api.fetch_order(order_id, pair) + except ccxt.OrderNotFound as e: + raise RetryableOrderError( + f'Order not found (id: {order_id}). Message: {e}') from e except ccxt.InvalidOrder as e: raise InvalidOrderException( f'Tried to get an invalid order (id: {order_id}). Message: {e}') from e diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 8c397fe68..66f88d82f 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1888,6 +1888,18 @@ def test_fetch_order(default_conf, mocker, exchange_name): exchange.fetch_order(order_id='_', pair='TKN/BTC') assert api_mock.fetch_order.call_count == 1 + api_mock.fetch_order = MagicMock(side_effect=ccxt.OrderNotFound("Order not found")) + exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) + with patch('freqtrade.exchange.common.time.sleep') as tm: + with pytest.raises(InvalidOrderException): + exchange.fetch_order(order_id='_', pair='TKN/BTC') + # Ensure backoff is called + assert tm.call_args_list[0][0][0] == 1 + assert tm.call_args_list[1][0][0] == 2 + assert tm.call_args_list[2][0][0] == 5 + assert tm.call_args_list[3][0][0] == 10 + assert api_mock.fetch_order.call_count == API_RETRY_COUNT + 1 + ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, 'fetch_order', 'fetch_order', order_id='_', pair='TKN/BTC') diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index ef48bdc34..654e6ca4f 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2078,7 +2078,7 @@ def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_ord 'freqtrade.exchange.Exchange', validate_pairs=MagicMock(), fetch_ticker=ticker, - fetch_order=MagicMock(side_effect=DependencyException), + fetch_order=MagicMock(side_effect=ExchangeError), cancel_order=cancel_order_mock, get_fee=fee )