Merge pull request #966 from freqtrade/feat/revamp_exchangetest

Rewrite standard ccxt exception handling
This commit is contained in:
Michael Egger 2018-07-01 19:47:57 +02:00 committed by GitHub
commit f91263c8ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -14,13 +14,29 @@ from freqtrade.exchange import Exchange, API_RETRY_COUNT
from freqtrade.tests.conftest import log_has, get_patched_exchange from freqtrade.tests.conftest import log_has, get_patched_exchange
def ccxt_exceptionhandlers(mocker, default_conf, api_mock, fun, mock_ccxt_fun, **kwargs):
"""Function to test ccxt exception handling """
with pytest.raises(TemporaryError):
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.NetworkError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
getattr(exchange, fun)(**kwargs)
assert api_mock.__dict__[mock_ccxt_fun].call_count == API_RETRY_COUNT + 1
with pytest.raises(OperationalException):
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.BaseError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
getattr(exchange, fun)(**kwargs)
assert api_mock.__dict__[mock_ccxt_fun].call_count == 1
def test_init(default_conf, mocker, caplog): def test_init(default_conf, mocker, caplog):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
get_patched_exchange(mocker, default_conf) get_patched_exchange(mocker, default_conf)
assert log_has('Instance is running with dry_run enabled', caplog.record_tuples) assert log_has('Instance is running with dry_run enabled', caplog.record_tuples)
def test_init_exception(default_conf): def test_init_exception(default_conf, mocker):
default_conf['exchange']['name'] = 'wrong_exchange_name' default_conf['exchange']['name'] = 'wrong_exchange_name'
with pytest.raises( with pytest.raises(
@ -28,6 +44,13 @@ def test_init_exception(default_conf):
match='Exchange {} is not supported'.format(default_conf['exchange']['name'])): match='Exchange {} is not supported'.format(default_conf['exchange']['name'])):
Exchange(default_conf) Exchange(default_conf)
default_conf['exchange']['name'] = 'binance'
with pytest.raises(
OperationalException,
match='Exchange {} is not supported'.format(default_conf['exchange']['name'])):
mocker.patch("ccxt.binance", MagicMock(side_effect=AttributeError))
Exchange(default_conf)
def test_validate_pairs(default_conf, mocker): def test_validate_pairs(default_conf, mocker):
api_mock = MagicMock() api_mock = MagicMock()
@ -97,6 +120,20 @@ def test_validate_pairs_stake_exception(default_conf, mocker, caplog):
Exchange(conf) Exchange(conf)
def test_exchangehas(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf)
assert not exchange.exchange_has('ASDFASDF')
api_mock = MagicMock()
type(api_mock).has = PropertyMock(return_value={'deadbeef': True})
exchange = get_patched_exchange(mocker, default_conf, api_mock)
assert exchange.exchange_has("deadbeef")
type(api_mock).has = PropertyMock(return_value={'deadbeef': False})
exchange = get_patched_exchange(mocker, default_conf, api_mock)
assert not exchange.exchange_has("deadbeef")
def test_buy_dry_run(default_conf, mocker): def test_buy_dry_run(default_conf, mocker):
default_conf['dry_run'] = True default_conf['dry_run'] = True
exchange = get_patched_exchange(mocker, default_conf) exchange = get_patched_exchange(mocker, default_conf)
@ -216,6 +253,11 @@ def test_get_balance_prod(default_conf, mocker):
exchange.get_balance(currency='BTC') exchange.get_balance(currency='BTC')
with pytest.raises(TemporaryError, match=r'.*balance due to malformed exchange response:.*'):
exchange = get_patched_exchange(mocker, default_conf, api_mock)
mocker.patch('freqtrade.exchange.Exchange.get_balances', MagicMock(return_value={}))
exchange.get_balance(currency='BTC')
def test_get_balances_dry_run(default_conf, mocker): def test_get_balances_dry_run(default_conf, mocker):
default_conf['dry_run'] = True default_conf['dry_run'] = True
@ -243,17 +285,8 @@ def test_get_balances_prod(default_conf, mocker):
assert exchange.get_balances()['1ST']['total'] == 10.0 assert exchange.get_balances()['1ST']['total'] == 10.0
assert exchange.get_balances()['1ST']['used'] == 0.0 assert exchange.get_balances()['1ST']['used'] == 0.0
with pytest.raises(TemporaryError): ccxt_exceptionhandlers(mocker, default_conf, api_mock,
api_mock.fetch_balance = MagicMock(side_effect=ccxt.NetworkError) "get_balances", "fetch_balance")
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.get_balances()
assert api_mock.fetch_balance.call_count == API_RETRY_COUNT + 1
with pytest.raises(OperationalException):
api_mock.fetch_balance = MagicMock(side_effect=ccxt.BaseError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.get_balances()
assert api_mock.fetch_balance.call_count == 1
def test_get_tickers(default_conf, mocker): def test_get_tickers(default_conf, mocker):
@ -282,15 +315,8 @@ def test_get_tickers(default_conf, mocker):
assert tickers['BCH/BTC']['bid'] == 0.6 assert tickers['BCH/BTC']['bid'] == 0.6
assert tickers['BCH/BTC']['ask'] == 0.5 assert tickers['BCH/BTC']['ask'] == 0.5
with pytest.raises(TemporaryError): # test retrier ccxt_exceptionhandlers(mocker, default_conf, api_mock,
api_mock.fetch_tickers = MagicMock(side_effect=ccxt.NetworkError) "get_tickers", "fetch_tickers")
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.get_tickers()
with pytest.raises(OperationalException):
api_mock.fetch_tickers = MagicMock(side_effect=ccxt.BaseError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.get_tickers()
with pytest.raises(OperationalException): with pytest.raises(OperationalException):
api_mock.fetch_tickers = MagicMock(side_effect=ccxt.NotSupported) api_mock.fetch_tickers = MagicMock(side_effect=ccxt.NotSupported)
@ -345,15 +371,9 @@ def test_get_ticker(default_conf, mocker):
exchange.get_ticker(pair='ETH/BTC', refresh=False) exchange.get_ticker(pair='ETH/BTC', refresh=False)
assert api_mock.fetch_ticker.call_count == 0 assert api_mock.fetch_ticker.call_count == 0
with pytest.raises(TemporaryError): # test retrier ccxt_exceptionhandlers(mocker, default_conf, api_mock,
api_mock.fetch_ticker = MagicMock(side_effect=ccxt.NetworkError) "get_ticker", "fetch_ticker",
exchange = get_patched_exchange(mocker, default_conf, api_mock) pair='ETH/BTC', refresh=True)
exchange.get_ticker(pair='ETH/BTC', refresh=True)
with pytest.raises(OperationalException):
api_mock.fetch_ticker = MagicMock(side_effect=ccxt.BaseError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.get_ticker(pair='ETH/BTC', refresh=True)
api_mock.fetch_ticker = MagicMock(return_value={}) api_mock.fetch_ticker = MagicMock(return_value={})
exchange = get_patched_exchange(mocker, default_conf, api_mock) exchange = get_patched_exchange(mocker, default_conf, api_mock)
@ -416,17 +436,14 @@ def test_get_ticker_history(default_conf, mocker):
assert ticks[0][4] == 9 assert ticks[0][4] == 9
assert ticks[0][5] == 10 assert ticks[0][5] == 10
with pytest.raises(TemporaryError): # test retrier ccxt_exceptionhandlers(mocker, default_conf, api_mock,
api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.NetworkError) "get_ticker_history", "fetch_ohlcv",
exchange = get_patched_exchange(mocker, default_conf, api_mock) pair='ABCD/BTC', tick_interval=default_conf['ticker_interval'])
# new symbol to get around cache
exchange.get_ticker_history('ABCD/BTC', default_conf['ticker_interval'])
with pytest.raises(OperationalException): with pytest.raises(OperationalException, match=r'Exchange .* does not support.*'):
api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.BaseError) api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.NotSupported)
exchange = get_patched_exchange(mocker, default_conf, api_mock) exchange = get_patched_exchange(mocker, default_conf, api_mock)
# new symbol to get around cache exchange.get_ticker_history(pair='ABCD/BTC', tick_interval=default_conf['ticker_interval'])
exchange.get_ticker_history('EFGH/BTC', default_conf['ticker_interval'])
def test_get_ticker_history_sort(default_conf, mocker): def test_get_ticker_history_sort(default_conf, mocker):
@ -515,24 +532,15 @@ def test_cancel_order(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf, api_mock) exchange = get_patched_exchange(mocker, default_conf, api_mock)
assert exchange.cancel_order(order_id='_', pair='TKN/BTC') == 123 assert exchange.cancel_order(order_id='_', pair='TKN/BTC') == 123
with pytest.raises(TemporaryError):
api_mock.cancel_order = MagicMock(side_effect=ccxt.NetworkError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.cancel_order(order_id='_', pair='TKN/BTC')
assert api_mock.cancel_order.call_count == API_RETRY_COUNT + 1
with pytest.raises(DependencyException): with pytest.raises(DependencyException):
api_mock.cancel_order = MagicMock(side_effect=ccxt.InvalidOrder) api_mock.cancel_order = MagicMock(side_effect=ccxt.InvalidOrder)
exchange = get_patched_exchange(mocker, default_conf, api_mock) exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.cancel_order(order_id='_', pair='TKN/BTC') exchange.cancel_order(order_id='_', pair='TKN/BTC')
assert api_mock.cancel_order.call_count == API_RETRY_COUNT + 1 assert api_mock.cancel_order.call_count == API_RETRY_COUNT + 1
with pytest.raises(OperationalException): ccxt_exceptionhandlers(mocker, default_conf, api_mock,
api_mock.cancel_order = MagicMock(side_effect=ccxt.BaseError) "cancel_order", "cancel_order",
exchange = get_patched_exchange(mocker, default_conf, api_mock) order_id='_', pair='TKN/BTC')
exchange.cancel_order(order_id='_', pair='TKN/BTC')
assert api_mock.cancel_order.call_count == 1
def test_get_order(default_conf, mocker): def test_get_order(default_conf, mocker):
@ -550,23 +558,15 @@ def test_get_order(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf, api_mock) exchange = get_patched_exchange(mocker, default_conf, api_mock)
assert exchange.get_order('X', 'TKN/BTC') == 456 assert exchange.get_order('X', 'TKN/BTC') == 456
with pytest.raises(TemporaryError):
api_mock.fetch_order = MagicMock(side_effect=ccxt.NetworkError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.get_order(order_id='_', pair='TKN/BTC')
assert api_mock.fetch_order.call_count == API_RETRY_COUNT + 1
with pytest.raises(DependencyException): with pytest.raises(DependencyException):
api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder) api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder)
exchange = get_patched_exchange(mocker, default_conf, api_mock) exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.get_order(order_id='_', pair='TKN/BTC') exchange.get_order(order_id='_', pair='TKN/BTC')
assert api_mock.fetch_order.call_count == API_RETRY_COUNT + 1 assert api_mock.fetch_order.call_count == API_RETRY_COUNT + 1
with pytest.raises(OperationalException): ccxt_exceptionhandlers(mocker, default_conf, api_mock,
api_mock.fetch_order = MagicMock(side_effect=ccxt.BaseError) 'get_order', 'fetch_order',
exchange = get_patched_exchange(mocker, default_conf, api_mock) order_id='_', pair='TKN/BTC')
exchange.get_order(order_id='_', pair='TKN/BTC')
assert api_mock.fetch_order.call_count == 1
def test_name(default_conf, mocker): def test_name(default_conf, mocker):
@ -651,19 +651,12 @@ def test_get_trades_for_order(default_conf, mocker):
assert len(orders) == 1 assert len(orders) == 1
assert orders[0]['price'] == 165 assert orders[0]['price'] == 165
# test Exceptions ccxt_exceptionhandlers(mocker, default_conf, api_mock,
with pytest.raises(OperationalException): 'get_trades_for_order', 'fetch_my_trades',
api_mock = MagicMock() order_id=order_id, pair='LTC/BTC', since=since)
api_mock.fetch_my_trades = MagicMock(side_effect=ccxt.BaseError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.get_trades_for_order(order_id, 'LTC/BTC', since)
with pytest.raises(TemporaryError): mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=False))
api_mock = MagicMock() assert exchange.get_trades_for_order(order_id, 'LTC/BTC', since) == []
api_mock.fetch_my_trades = MagicMock(side_effect=ccxt.NetworkError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.get_trades_for_order(order_id, 'LTC/BTC', since)
assert api_mock.fetch_my_trades.call_count == API_RETRY_COUNT + 1
def test_get_markets(default_conf, mocker, markets): def test_get_markets(default_conf, mocker, markets):
@ -677,19 +670,8 @@ def test_get_markets(default_conf, mocker, markets):
assert ret[0]["id"] == "ethbtc" assert ret[0]["id"] == "ethbtc"
assert ret[0]["symbol"] == "ETH/BTC" assert ret[0]["symbol"] == "ETH/BTC"
# test Exceptions ccxt_exceptionhandlers(mocker, default_conf, api_mock,
with pytest.raises(OperationalException): 'get_markets', 'fetch_markets')
api_mock = MagicMock()
api_mock.fetch_markets = MagicMock(side_effect=ccxt.BaseError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.get_markets()
with pytest.raises(TemporaryError):
api_mock = MagicMock()
api_mock.fetch_markets = MagicMock(side_effect=ccxt.NetworkError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.get_markets()
assert api_mock.fetch_markets.call_count == API_RETRY_COUNT + 1
def test_get_fee(default_conf, mocker): def test_get_fee(default_conf, mocker):
@ -704,19 +686,8 @@ def test_get_fee(default_conf, mocker):
assert exchange.get_fee() == 0.025 assert exchange.get_fee() == 0.025
# test Exceptions ccxt_exceptionhandlers(mocker, default_conf, api_mock,
with pytest.raises(OperationalException): 'get_fee', 'calculate_fee')
api_mock = MagicMock()
api_mock.calculate_fee = MagicMock(side_effect=ccxt.BaseError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.get_fee()
with pytest.raises(TemporaryError):
api_mock = MagicMock()
api_mock.calculate_fee = MagicMock(side_effect=ccxt.NetworkError)
exchange = get_patched_exchange(mocker, default_conf, api_mock)
exchange.get_fee()
assert api_mock.calculate_fee.call_count == API_RETRY_COUNT + 1
def test_get_amount_lots(default_conf, mocker): def test_get_amount_lots(default_conf, mocker):