Implement DDos backoff (1s)
This commit is contained in:
		| @@ -45,6 +45,13 @@ class TemporaryError(FreqtradeException): | |||||||
|     """ |     """ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class DDosProtection(TemporaryError): | ||||||
|  |     """ | ||||||
|  |     Temporary error caused by DDOS protection. | ||||||
|  |     Bot will wait for a second and then retry. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |  | ||||||
| class StrategyError(FreqtradeException): | class StrategyError(FreqtradeException): | ||||||
|     """ |     """ | ||||||
|     Errors with custom user-code deteced. |     Errors with custom user-code deteced. | ||||||
|   | |||||||
| @@ -4,8 +4,9 @@ from typing import Dict | |||||||
|  |  | ||||||
| import ccxt | import ccxt | ||||||
|  |  | ||||||
| from freqtrade.exceptions import (DependencyException, InvalidOrderException, | from freqtrade.exceptions import (DDosProtection, DependencyException, | ||||||
|                                   OperationalException, TemporaryError) |                                   InvalidOrderException, OperationalException, | ||||||
|  |                                   TemporaryError) | ||||||
| from freqtrade.exchange import Exchange | from freqtrade.exchange import Exchange | ||||||
|  |  | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
| @@ -88,6 +89,8 @@ class Binance(Exchange): | |||||||
|                 f'Could not create {ordertype} sell order on market {pair}. ' |                 f'Could not create {ordertype} sell order on market {pair}. ' | ||||||
|                 f'Tried to sell amount {amount} at rate {rate}. ' |                 f'Tried to sell amount {amount} at rate {rate}. ' | ||||||
|                 f'Message: {e}') from e |                 f'Message: {e}') from e | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError( |             raise TemporaryError( | ||||||
|                 f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e |                 f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
|  | import asyncio | ||||||
| import logging | import logging | ||||||
|  | import time | ||||||
|  |  | ||||||
| from freqtrade.exceptions import TemporaryError | from freqtrade.exceptions import DDosProtection, TemporaryError | ||||||
|  |  | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
|  |  | ||||||
| @@ -99,6 +101,8 @@ def retrier_async(f): | |||||||
|                 count -= 1 |                 count -= 1 | ||||||
|                 kwargs.update({'count': count}) |                 kwargs.update({'count': count}) | ||||||
|                 logger.warning('retrying %s() still for %s times', f.__name__, count) |                 logger.warning('retrying %s() still for %s times', f.__name__, count) | ||||||
|  |                 if isinstance(ex, DDosProtection): | ||||||
|  |                     await asyncio.sleep(1) | ||||||
|                 return await wrapper(*args, **kwargs) |                 return await wrapper(*args, **kwargs) | ||||||
|             else: |             else: | ||||||
|                 logger.warning('Giving up retrying: %s()', f.__name__) |                 logger.warning('Giving up retrying: %s()', f.__name__) | ||||||
| @@ -117,6 +121,8 @@ def retrier(f): | |||||||
|                 count -= 1 |                 count -= 1 | ||||||
|                 kwargs.update({'count': count}) |                 kwargs.update({'count': count}) | ||||||
|                 logger.warning('retrying %s() still for %s times', f.__name__, count) |                 logger.warning('retrying %s() still for %s times', f.__name__, count) | ||||||
|  |                 if isinstance(ex, DDosProtection): | ||||||
|  |                     time.sleep(1) | ||||||
|                 return wrapper(*args, **kwargs) |                 return wrapper(*args, **kwargs) | ||||||
|             else: |             else: | ||||||
|                 logger.warning('Giving up retrying: %s()', f.__name__) |                 logger.warning('Giving up retrying: %s()', f.__name__) | ||||||
|   | |||||||
| @@ -18,12 +18,13 @@ from ccxt.base.decimal_to_precision import (ROUND_DOWN, ROUND_UP, TICK_SIZE, | |||||||
|                                             TRUNCATE, decimal_to_precision) |                                             TRUNCATE, decimal_to_precision) | ||||||
| from pandas import DataFrame | from pandas import DataFrame | ||||||
|  |  | ||||||
|  | from freqtrade.constants import ListPairsWithTimeframes | ||||||
| from freqtrade.data.converter import ohlcv_to_dataframe, trades_dict_to_list | from freqtrade.data.converter import ohlcv_to_dataframe, trades_dict_to_list | ||||||
| from freqtrade.exceptions import (DependencyException, InvalidOrderException, | from freqtrade.exceptions import (DDosProtection, DependencyException, | ||||||
|                                   OperationalException, TemporaryError) |                                   InvalidOrderException, OperationalException, | ||||||
|  |                                   TemporaryError) | ||||||
| from freqtrade.exchange.common import BAD_EXCHANGES, retrier, retrier_async | from freqtrade.exchange.common import BAD_EXCHANGES, retrier, retrier_async | ||||||
| from freqtrade.misc import deep_merge_dicts, safe_value_fallback | from freqtrade.misc import deep_merge_dicts, safe_value_fallback | ||||||
| from freqtrade.constants import ListPairsWithTimeframes |  | ||||||
|  |  | ||||||
| CcxtModuleType = Any | CcxtModuleType = Any | ||||||
|  |  | ||||||
| @@ -527,6 +528,8 @@ class Exchange: | |||||||
|                 f'Could not create {ordertype} {side} order on market {pair}.' |                 f'Could not create {ordertype} {side} order on market {pair}.' | ||||||
|                 f'Tried to {side} amount {amount} at rate {rate}.' |                 f'Tried to {side} amount {amount} at rate {rate}.' | ||||||
|                 f'Message: {e}') from e |                 f'Message: {e}') from e | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError( |             raise TemporaryError( | ||||||
|                 f'Could not place {side} order due to {e.__class__.__name__}. Message: {e}') from e |                 f'Could not place {side} order due to {e.__class__.__name__}. Message: {e}') from e | ||||||
| @@ -606,6 +609,8 @@ class Exchange: | |||||||
|             balances.pop("used", None) |             balances.pop("used", None) | ||||||
|  |  | ||||||
|             return balances |             return balances | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError( |             raise TemporaryError( | ||||||
|                 f'Could not get balance due to {e.__class__.__name__}. Message: {e}') from e |                 f'Could not get balance due to {e.__class__.__name__}. Message: {e}') from e | ||||||
| @@ -620,6 +625,8 @@ class Exchange: | |||||||
|             raise OperationalException( |             raise OperationalException( | ||||||
|                 f'Exchange {self._api.name} does not support fetching tickers in batch. ' |                 f'Exchange {self._api.name} does not support fetching tickers in batch. ' | ||||||
|                 f'Message: {e}') from e |                 f'Message: {e}') from e | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError( |             raise TemporaryError( | ||||||
|                 f'Could not load tickers due to {e.__class__.__name__}. Message: {e}') from e |                 f'Could not load tickers due to {e.__class__.__name__}. Message: {e}') from e | ||||||
| @@ -633,6 +640,8 @@ class Exchange: | |||||||
|                 raise DependencyException(f"Pair {pair} not available") |                 raise DependencyException(f"Pair {pair} not available") | ||||||
|             data = self._api.fetch_ticker(pair) |             data = self._api.fetch_ticker(pair) | ||||||
|             return data |             return data | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError( |             raise TemporaryError( | ||||||
|                 f'Could not load ticker due to {e.__class__.__name__}. Message: {e}') from e |                 f'Could not load ticker due to {e.__class__.__name__}. Message: {e}') from e | ||||||
| @@ -766,6 +775,8 @@ class Exchange: | |||||||
|             raise OperationalException( |             raise OperationalException( | ||||||
|                 f'Exchange {self._api.name} does not support fetching historical ' |                 f'Exchange {self._api.name} does not support fetching historical ' | ||||||
|                 f'candle (OHLCV) data. Message: {e}') from e |                 f'candle (OHLCV) data. Message: {e}') from e | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError(f'Could not fetch historical candle (OHLCV) data ' |             raise TemporaryError(f'Could not fetch historical candle (OHLCV) data ' | ||||||
|                                  f'for pair {pair} due to {e.__class__.__name__}. ' |                                  f'for pair {pair} due to {e.__class__.__name__}. ' | ||||||
| @@ -802,6 +813,8 @@ class Exchange: | |||||||
|             raise OperationalException( |             raise OperationalException( | ||||||
|                 f'Exchange {self._api.name} does not support fetching historical trade data.' |                 f'Exchange {self._api.name} does not support fetching historical trade data.' | ||||||
|                 f'Message: {e}') from e |                 f'Message: {e}') from e | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError(f'Could not load trade history due to {e.__class__.__name__}. ' |             raise TemporaryError(f'Could not load trade history due to {e.__class__.__name__}. ' | ||||||
|                                  f'Message: {e}') from e |                                  f'Message: {e}') from e | ||||||
| @@ -948,6 +961,8 @@ class Exchange: | |||||||
|         except ccxt.InvalidOrder as e: |         except ccxt.InvalidOrder as e: | ||||||
|             raise InvalidOrderException( |             raise InvalidOrderException( | ||||||
|                 f'Could not cancel order. Message: {e}') from e |                 f'Could not cancel order. Message: {e}') from e | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError( |             raise TemporaryError( | ||||||
|                 f'Could not cancel order due to {e.__class__.__name__}. Message: {e}') from e |                 f'Could not cancel order due to {e.__class__.__name__}. Message: {e}') from e | ||||||
| @@ -1003,6 +1018,8 @@ class Exchange: | |||||||
|         except ccxt.InvalidOrder as e: |         except ccxt.InvalidOrder as e: | ||||||
|             raise InvalidOrderException( |             raise InvalidOrderException( | ||||||
|                 f'Tried to get an invalid order (id: {order_id}). Message: {e}') from e |                 f'Tried to get an invalid order (id: {order_id}). Message: {e}') from e | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError( |             raise TemporaryError( | ||||||
|                 f'Could not get order due to {e.__class__.__name__}. Message: {e}') from e |                 f'Could not get order due to {e.__class__.__name__}. Message: {e}') from e | ||||||
| @@ -1027,6 +1044,8 @@ class Exchange: | |||||||
|             raise OperationalException( |             raise OperationalException( | ||||||
|                 f'Exchange {self._api.name} does not support fetching order book.' |                 f'Exchange {self._api.name} does not support fetching order book.' | ||||||
|                 f'Message: {e}') from e |                 f'Message: {e}') from e | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError( |             raise TemporaryError( | ||||||
|                 f'Could not get order book due to {e.__class__.__name__}. Message: {e}') from e |                 f'Could not get order book due to {e.__class__.__name__}. Message: {e}') from e | ||||||
| @@ -1063,7 +1082,8 @@ class Exchange: | |||||||
|             matched_trades = [trade for trade in my_trades if trade['order'] == order_id] |             matched_trades = [trade for trade in my_trades if trade['order'] == order_id] | ||||||
|  |  | ||||||
|             return matched_trades |             return matched_trades | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError( |             raise TemporaryError( | ||||||
|                 f'Could not get trades due to {e.__class__.__name__}. Message: {e}') from e |                 f'Could not get trades due to {e.__class__.__name__}. Message: {e}') from e | ||||||
| @@ -1080,6 +1100,8 @@ class Exchange: | |||||||
|  |  | ||||||
|             return self._api.calculate_fee(symbol=symbol, type=type, side=side, amount=amount, |             return self._api.calculate_fee(symbol=symbol, type=type, side=side, amount=amount, | ||||||
|                                            price=price, takerOrMaker=taker_or_maker)['rate'] |                                            price=price, takerOrMaker=taker_or_maker)['rate'] | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError( |             raise TemporaryError( | ||||||
|                 f'Could not get fee info due to {e.__class__.__name__}. Message: {e}') from e |                 f'Could not get fee info due to {e.__class__.__name__}. Message: {e}') from e | ||||||
|   | |||||||
| @@ -4,8 +4,9 @@ from typing import Dict | |||||||
|  |  | ||||||
| import ccxt | import ccxt | ||||||
|  |  | ||||||
| from freqtrade.exceptions import (DependencyException, InvalidOrderException, | from freqtrade.exceptions import (DDosProtection, DependencyException, | ||||||
|                                   OperationalException, TemporaryError) |                                   InvalidOrderException, OperationalException, | ||||||
|  |                                   TemporaryError) | ||||||
| from freqtrade.exchange import Exchange | from freqtrade.exchange import Exchange | ||||||
| from freqtrade.exchange.common import retrier | from freqtrade.exchange.common import retrier | ||||||
|  |  | ||||||
| @@ -68,6 +69,8 @@ class Ftx(Exchange): | |||||||
|                 f'Could not create {ordertype} sell order on market {pair}. ' |                 f'Could not create {ordertype} sell order on market {pair}. ' | ||||||
|                 f'Tried to create stoploss with amount {amount} at stoploss {stop_price}. ' |                 f'Tried to create stoploss with amount {amount} at stoploss {stop_price}. ' | ||||||
|                 f'Message: {e}') from e |                 f'Message: {e}') from e | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError( |             raise TemporaryError( | ||||||
|                 f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e |                 f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e | ||||||
| @@ -96,6 +99,8 @@ class Ftx(Exchange): | |||||||
|         except ccxt.InvalidOrder as e: |         except ccxt.InvalidOrder as e: | ||||||
|             raise InvalidOrderException( |             raise InvalidOrderException( | ||||||
|                 f'Tried to get an invalid order (id: {order_id}). Message: {e}') from e |                 f'Tried to get an invalid order (id: {order_id}). Message: {e}') from e | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError( |             raise TemporaryError( | ||||||
|                 f'Could not get order due to {e.__class__.__name__}. Message: {e}') from e |                 f'Could not get order due to {e.__class__.__name__}. Message: {e}') from e | ||||||
| @@ -111,6 +116,8 @@ class Ftx(Exchange): | |||||||
|         except ccxt.InvalidOrder as e: |         except ccxt.InvalidOrder as e: | ||||||
|             raise InvalidOrderException( |             raise InvalidOrderException( | ||||||
|                 f'Could not cancel order. Message: {e}') from e |                 f'Could not cancel order. Message: {e}') from e | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError( |             raise TemporaryError( | ||||||
|                 f'Could not cancel order due to {e.__class__.__name__}. Message: {e}') from e |                 f'Could not cancel order due to {e.__class__.__name__}. Message: {e}') from e | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ from typing import Dict | |||||||
|  |  | ||||||
| import ccxt | import ccxt | ||||||
|  |  | ||||||
| from freqtrade.exceptions import (DependencyException, InvalidOrderException, | from freqtrade.exceptions import (DependencyException, InvalidOrderException, DDosProtection, | ||||||
|                                   OperationalException, TemporaryError) |                                   OperationalException, TemporaryError) | ||||||
| from freqtrade.exchange import Exchange | from freqtrade.exchange import Exchange | ||||||
| from freqtrade.exchange.common import retrier | from freqtrade.exchange.common import retrier | ||||||
| @@ -45,6 +45,8 @@ class Kraken(Exchange): | |||||||
|                 balances[bal]['free'] = balances[bal]['total'] - balances[bal]['used'] |                 balances[bal]['free'] = balances[bal]['total'] - balances[bal]['used'] | ||||||
|  |  | ||||||
|             return balances |             return balances | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError( |             raise TemporaryError( | ||||||
|                 f'Could not get balance due to {e.__class__.__name__}. Message: {e}') from e |                 f'Could not get balance due to {e.__class__.__name__}. Message: {e}') from e | ||||||
| @@ -93,6 +95,8 @@ class Kraken(Exchange): | |||||||
|                 f'Could not create {ordertype} sell order on market {pair}. ' |                 f'Could not create {ordertype} sell order on market {pair}. ' | ||||||
|                 f'Tried to create stoploss with amount {amount} at stoploss {stop_price}. ' |                 f'Tried to create stoploss with amount {amount} at stoploss {stop_price}. ' | ||||||
|                 f'Message: {e}') from e |                 f'Message: {e}') from e | ||||||
|  |         except ccxt.DDoSProtection as e: | ||||||
|  |             raise DDosProtection(e) from e | ||||||
|         except (ccxt.NetworkError, ccxt.ExchangeError) as e: |         except (ccxt.NetworkError, ccxt.ExchangeError) as e: | ||||||
|             raise TemporaryError( |             raise TemporaryError( | ||||||
|                 f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e |                 f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e | ||||||
|   | |||||||
| @@ -4,14 +4,14 @@ import copy | |||||||
| import logging | import logging | ||||||
| from datetime import datetime, timezone | from datetime import datetime, timezone | ||||||
| from random import randint | from random import randint | ||||||
| from unittest.mock import MagicMock, Mock, PropertyMock | from unittest.mock import MagicMock, Mock, PropertyMock, patch | ||||||
|  |  | ||||||
| import arrow | import arrow | ||||||
| import ccxt | import ccxt | ||||||
| import pytest | import pytest | ||||||
| from pandas import DataFrame | from pandas import DataFrame | ||||||
|  |  | ||||||
| from freqtrade.exceptions import (DependencyException, InvalidOrderException, | from freqtrade.exceptions import (DependencyException, InvalidOrderException, DDosProtection, | ||||||
|                                   OperationalException, TemporaryError) |                                   OperationalException, TemporaryError) | ||||||
| from freqtrade.exchange import Binance, Exchange, Kraken | from freqtrade.exchange import Binance, Exchange, Kraken | ||||||
| from freqtrade.exchange.common import API_RETRY_COUNT | from freqtrade.exchange.common import API_RETRY_COUNT | ||||||
| @@ -38,6 +38,14 @@ def get_mock_coro(return_value): | |||||||
|  |  | ||||||
| def ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, | def ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, | ||||||
|                            fun, mock_ccxt_fun, **kwargs): |                            fun, mock_ccxt_fun, **kwargs): | ||||||
|  |  | ||||||
|  |     with patch('freqtrade.exchange.common.time.sleep'): | ||||||
|  |         with pytest.raises(DDosProtection): | ||||||
|  |             api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.DDoSProtection("DDos")) | ||||||
|  |             exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) | ||||||
|  |             getattr(exchange, fun)(**kwargs) | ||||||
|  |         assert api_mock.__dict__[mock_ccxt_fun].call_count == API_RETRY_COUNT + 1 | ||||||
|  |  | ||||||
|     with pytest.raises(TemporaryError): |     with pytest.raises(TemporaryError): | ||||||
|         api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.NetworkError("DeaDBeef")) |         api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.NetworkError("DeaDBeef")) | ||||||
|         exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) |         exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) | ||||||
| @@ -52,6 +60,13 @@ def ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, | |||||||
|  |  | ||||||
|  |  | ||||||
| async def async_ccxt_exception(mocker, default_conf, api_mock, fun, mock_ccxt_fun, **kwargs): | async def async_ccxt_exception(mocker, default_conf, api_mock, fun, mock_ccxt_fun, **kwargs): | ||||||
|  |  | ||||||
|  |     with patch('freqtrade.exchange.common.asyncio.sleep'): | ||||||
|  |         with pytest.raises(DDosProtection): | ||||||
|  |             api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.DDoSProtection("DeadBeef")) | ||||||
|  |             exchange = get_patched_exchange(mocker, default_conf, api_mock) | ||||||
|  |             await getattr(exchange, fun)(**kwargs) | ||||||
|  |  | ||||||
|     with pytest.raises(TemporaryError): |     with pytest.raises(TemporaryError): | ||||||
|         api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.NetworkError("DeadBeef")) |         api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.NetworkError("DeadBeef")) | ||||||
|         exchange = get_patched_exchange(mocker, default_conf, api_mock) |         exchange = get_patched_exchange(mocker, default_conf, api_mock) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user