diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 07dc45a3e..0b0b6d3da 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -73,11 +73,11 @@ def default_conf(): "key": "key", "secret": "secret", "pair_whitelist": [ - "BTC_ETH", - "BTC_TKN", - "BTC_TRST", - "BTC_SWT", - "BTC_BCC" + "ETH/BTC", + "TKN/BTC", + "TRST/BTC", + "SWT/BTC", + "BCC/BTC" ] }, "telegram": { @@ -160,13 +160,14 @@ def health(): def limit_buy_order(): return { 'id': 'mocked_limit_buy', - 'type': 'LIMIT_BUY', + 'type': 'limit', + 'side': 'buy', 'pair': 'mocked', - 'opened': str(arrow.utcnow().datetime), - 'rate': 0.00001099, + 'datetime': arrow.utcnow().isoformat(), + 'price': 0.00001099, 'amount': 90.99181073, 'remaining': 0.0, - 'closed': str(arrow.utcnow().datetime), + 'status': 'closed' } @@ -174,12 +175,14 @@ def limit_buy_order(): def limit_buy_order_old(): return { 'id': 'mocked_limit_buy_old', - 'type': 'LIMIT_BUY', - 'pair': 'BTC_ETH', - 'opened': str(arrow.utcnow().shift(minutes=-601).datetime), - 'rate': 0.00001099, + 'type': 'limit', + 'side': 'buy', + 'pair': 'mocked', + 'datetime': str(arrow.utcnow().shift(minutes=-601).datetime), + 'price': 0.00001099, 'amount': 90.99181073, 'remaining': 90.99181073, + 'status': 'open' } @@ -187,12 +190,14 @@ def limit_buy_order_old(): def limit_sell_order_old(): return { 'id': 'mocked_limit_sell_old', - 'type': 'LIMIT_SELL', - 'pair': 'BTC_ETH', - 'opened': str(arrow.utcnow().shift(minutes=-601).datetime), - 'rate': 0.00001099, + 'type': 'limit', + 'side': 'sell', + 'pair': 'ETH/BTC', + 'datetime': arrow.utcnow().shift(minutes=-601).isoformat(), + 'price': 0.00001099, 'amount': 90.99181073, 'remaining': 90.99181073, + 'status': 'open' } @@ -200,12 +205,14 @@ def limit_sell_order_old(): def limit_buy_order_old_partial(): return { 'id': 'mocked_limit_buy_old_partial', - 'type': 'LIMIT_BUY', - 'pair': 'BTC_ETH', - 'opened': str(arrow.utcnow().shift(minutes=-601).datetime), - 'rate': 0.00001099, + 'type': 'limit', + 'side': 'buy', + 'pair': 'ETH/BTC', + 'datetime': arrow.utcnow().shift(minutes=-601).isoformat(), + 'price': 0.00001099, 'amount': 90.99181073, 'remaining': 67.99181073, + 'status': 'open' } @@ -213,16 +220,47 @@ def limit_buy_order_old_partial(): def limit_sell_order(): return { 'id': 'mocked_limit_sell', - 'type': 'LIMIT_SELL', + 'type': 'limit', + 'side': 'sell', 'pair': 'mocked', - 'opened': str(arrow.utcnow().datetime), - 'rate': 0.00001173, + 'datetime': arrow.utcnow().isoformat(), + 'price': 0.00001173, 'amount': 90.99181073, 'remaining': 0.0, - 'closed': str(arrow.utcnow().datetime), + 'status': 'closed' } +@pytest.fixture +def ticker_history_api(): + return [ + [ + 1511686200000, # unix timestamp ms + 8.794e-05, # open + 8.948e-05, # high + 8.794e-05, # low + 8.88e-05, # close + 0.0877869, # volume (in quote currency) + ], + [ + 1511686500000, + 8.88e-05, + 8.942e-05, + 8.88e-05, + 8.893e-05, + 0.05874751, + ], + [ + 1511686800, + 8.891e-05, + 8.893e-05, + 8.875e-05, + 8.877e-05, + 0.7039405 + ] + ] + + @pytest.fixture def ticker_history(): return [ @@ -299,133 +337,3 @@ def result(): # return the open-order-id # See tests in rpc/main that could use this - -@pytest.fixture -def get_market_summaries_data(): - """ - This fixture is a real result from exchange.get_market_summaries() but reduced to only - 8 entries. 4 BTC, 4 USTD - :return: JSON market summaries - """ - return [ - { - 'Ask': 1.316e-05, - 'BaseVolume': 5.72599471, - 'Bid': 1.3e-05, - 'Created': '2014-04-14T00:00:00', - 'High': 1.414e-05, - 'Last': 1.298e-05, - 'Low': 1.282e-05, - 'MarketName': 'BTC-XWC', - 'OpenBuyOrders': 2000, - 'OpenSellOrders': 1484, - 'PrevDay': 1.376e-05, - 'TimeStamp': '2018-02-05T01:32:40.493', - 'Volume': 424041.21418375 - }, - { - 'Ask': 0.00627051, - 'BaseVolume': 93.23302388, - 'Bid': 0.00618192, - 'Created': '2016-10-20T04:48:30.387', - 'High': 0.00669897, - 'Last': 0.00618192, - 'Low': 0.006, - 'MarketName': 'BTC-XZC', - 'OpenBuyOrders': 343, - 'OpenSellOrders': 2037, - 'PrevDay': 0.00668229, - 'TimeStamp': '2018-02-05T01:32:43.383', - 'Volume': 14863.60730702 - }, - { - 'Ask': 0.01137247, - 'BaseVolume': 383.55922657, - 'Bid': 0.01136006, - 'Created': '2016-11-15T20:29:59.73', - 'High': 0.012, - 'Last': 0.01137247, - 'Low': 0.01119883, - 'MarketName': 'BTC-ZCL', - 'OpenBuyOrders': 1332, - 'OpenSellOrders': 5317, - 'PrevDay': 0.01179603, - 'TimeStamp': '2018-02-05T01:32:42.773', - 'Volume': 33308.07358285 - }, - { - 'Ask': 0.04155821, - 'BaseVolume': 274.75369074, - 'Bid': 0.04130002, - 'Created': '2016-10-28T17:13:10.833', - 'High': 0.04354429, - 'Last': 0.041585, - 'Low': 0.0413, - 'MarketName': 'BTC-ZEC', - 'OpenBuyOrders': 863, - 'OpenSellOrders': 5579, - 'PrevDay': 0.0429, - 'TimeStamp': '2018-02-05T01:32:43.21', - 'Volume': 6479.84033259 - }, - { - 'Ask': 210.99999999, - 'BaseVolume': 615132.70989532, - 'Bid': 210.05503736, - 'Created': '2017-07-21T01:08:49.397', - 'High': 257.396, - 'Last': 211.0, - 'Low': 209.05333589, - 'MarketName': 'USDT-XMR', - 'OpenBuyOrders': 180, - 'OpenSellOrders': 1203, - 'PrevDay': 247.93528899, - 'TimeStamp': '2018-02-05T01:32:43.117', - 'Volume': 2688.17410793 - }, - { - 'Ask': 0.79589979, - 'BaseVolume': 9349557.01853031, - 'Bid': 0.789226, - 'Created': '2017-07-14T17:10:10.737', - 'High': 0.977, - 'Last': 0.79589979, - 'Low': 0.781, - 'MarketName': 'USDT-XRP', - 'OpenBuyOrders': 1075, - 'OpenSellOrders': 6508, - 'PrevDay': 0.93300218, - 'TimeStamp': '2018-02-05T01:32:42.383', - 'Volume': 10801663.00788851 - }, - { - 'Ask': 0.05154982, - 'BaseVolume': 2311087.71232136, - 'Bid': 0.05040107, - 'Created': '2017-12-29T19:29:18.357', - 'High': 0.06668561, - 'Last': 0.0508, - 'Low': 0.05006731, - 'MarketName': 'USDT-XVG', - 'OpenBuyOrders': 655, - 'OpenSellOrders': 5544, - 'PrevDay': 0.0627, - 'TimeStamp': '2018-02-05T01:32:41.507', - 'Volume': 40031424.2152716 - }, - { - 'Ask': 332.65500022, - 'BaseVolume': 562911.87455665, - 'Bid': 330.00000001, - 'Created': '2017-07-14T17:10:10.673', - 'High': 401.59999999, - 'Last': 332.65500019, - 'Low': 330.0, - 'MarketName': 'USDT-ZEC', - 'OpenBuyOrders': 161, - 'OpenSellOrders': 1731, - 'PrevDay': 391.42, - 'TimeStamp': '2018-02-05T01:32:42.947', - 'Volume': 1571.09647946 - } - ] diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index f2874f2da..eacc641d5 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -2,15 +2,15 @@ # pragma pylint: disable=protected-access import logging from random import randint -from unittest.mock import MagicMock +from unittest.mock import MagicMock, PropertyMock +import ccxt import pytest -from requests.exceptions import RequestException -import freqtrade.exchange as exchange -from freqtrade import OperationalException +from freqtrade import OperationalException, DependencyException, NetworkException from freqtrade.exchange import init, validate_pairs, buy, sell, get_balance, get_balances, \ get_ticker, get_ticker_history, cancel_order, get_name, get_fee +import freqtrade.exchange as exchange from freqtrade.tests.conftest import log_has API_INIT = False @@ -42,9 +42,12 @@ def test_init_exception(default_conf): def test_validate_pairs(default_conf, mocker): api_mock = MagicMock() - api_mock.get_markets = MagicMock(return_value=[ - 'BTC_ETH', 'BTC_TKN', 'BTC_TRST', 'BTC_SWT', 'BTC_BCC', - ]) + api_mock.load_markets = MagicMock(return_value={ + 'ETH/BTC': '', 'TKN/BTC': '', 'TRST/BTC': '', 'SWT/BTC': '', 'BCC/BTC': '' + }) + id_mock = PropertyMock(return_value='test_exchange') + type(api_mock).id = id_mock + mocker.patch('freqtrade.exchange._API', api_mock) mocker.patch.dict('freqtrade.exchange._CONF', default_conf) validate_pairs(default_conf['exchange']['pair_whitelist']) @@ -52,7 +55,7 @@ def test_validate_pairs(default_conf, mocker): def test_validate_pairs_not_available(default_conf, mocker): api_mock = MagicMock() - api_mock.get_markets = MagicMock(return_value=[]) + api_mock.load_markets = MagicMock(return_value={}) mocker.patch('freqtrade.exchange._API', api_mock) mocker.patch.dict('freqtrade.exchange._CONF', default_conf) with pytest.raises(OperationalException, match=r'not available'): @@ -61,8 +64,9 @@ def test_validate_pairs_not_available(default_conf, mocker): def test_validate_pairs_not_compatible(default_conf, mocker): api_mock = MagicMock() - api_mock.get_markets = MagicMock( - return_value=['BTC_ETH', 'BTC_TKN', 'BTC_TRST', 'BTC_SWT']) + api_mock.load_markets = MagicMock(return_value={ + 'ETH/BTC': '', 'TKN/BTC': '', 'TRST/BTC': '', 'SWT/BTC': '', 'BCC/BTC': '' + }) default_conf['stake_currency'] = 'ETH' mocker.patch('freqtrade.exchange._API', api_mock) mocker.patch.dict('freqtrade.exchange._CONF', default_conf) @@ -73,10 +77,9 @@ def test_validate_pairs_not_compatible(default_conf, mocker): def test_validate_pairs_exception(default_conf, mocker, caplog): caplog.set_level(logging.INFO) api_mock = MagicMock() - api_mock.get_markets = MagicMock(side_effect=RequestException()) + api_mock.load_markets = MagicMock(side_effect=ccxt.BaseError()) mocker.patch('freqtrade.exchange._API', api_mock) - # with pytest.raises(RequestException, match=r'Unable to validate pairs'): validate_pairs(default_conf['exchange']['pair_whitelist']) assert log_has('Unable to validate pairs (assuming they are correct). Reason: ', caplog.record_tuples) @@ -86,38 +89,100 @@ def test_buy_dry_run(default_conf, mocker): default_conf['dry_run'] = True mocker.patch.dict('freqtrade.exchange._CONF', default_conf) - assert 'dry_run_buy_' in buy(pair='BTC_ETH', rate=200, amount=1) + order = buy(pair='ETH/BTC', rate=200, amount=1) + assert 'id' in order + assert 'dry_run_buy_' in order['id'] def test_buy_prod(default_conf, mocker): api_mock = MagicMock() - api_mock.buy = MagicMock( - return_value='dry_run_buy_{}'.format(randint(0, 10**6))) + order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) + api_mock.create_limit_buy_order = MagicMock(return_value={ + 'id': order_id, + 'info': { + 'foo': 'bar' + } + }) mocker.patch('freqtrade.exchange._API', api_mock) default_conf['dry_run'] = False mocker.patch.dict('freqtrade.exchange._CONF', default_conf) - assert 'dry_run_buy_' in buy(pair='BTC_ETH', rate=200, amount=1) + order = buy(pair='ETH/BTC', rate=200, amount=1) + assert 'id' in order + assert 'info' in order + assert order['id'] == order_id + + # test exception handling + with pytest.raises(DependencyException): + api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.InsufficientFunds) + mocker.patch('freqtrade.exchange._API', api_mock) + buy(pair='ETH/BTC', rate=200, amount=1) + + with pytest.raises(DependencyException): + api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.InvalidOrder) + mocker.patch('freqtrade.exchange._API', api_mock) + buy(pair='ETH/BTC', rate=200, amount=1) + + with pytest.raises(NetworkException): + api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.NetworkError) + mocker.patch('freqtrade.exchange._API', api_mock) + buy(pair='ETH/BTC', rate=200, amount=1) + + with pytest.raises(OperationalException): + api_mock.create_limit_buy_order = MagicMock(side_effect=ccxt.BaseError) + mocker.patch('freqtrade.exchange._API', api_mock) + buy(pair='ETH/BTC', rate=200, amount=1) def test_sell_dry_run(default_conf, mocker): default_conf['dry_run'] = True mocker.patch.dict('freqtrade.exchange._CONF', default_conf) - assert 'dry_run_sell_' in sell(pair='BTC_ETH', rate=200, amount=1) + order = sell(pair='ETH/BTC', rate=200, amount=1) + assert 'id' in order + assert 'dry_run_sell_' in order['id'] def test_sell_prod(default_conf, mocker): api_mock = MagicMock() - api_mock.sell = MagicMock( - return_value='dry_run_sell_{}'.format(randint(0, 10**6))) + order_id = 'test_prod_sell_{}'.format(randint(0, 10 ** 6)) + api_mock.create_limit_sell_order = MagicMock(return_value={ + 'id': order_id, + 'info': { + 'foo': 'bar' + } + }) mocker.patch('freqtrade.exchange._API', api_mock) default_conf['dry_run'] = False mocker.patch.dict('freqtrade.exchange._CONF', default_conf) - assert 'dry_run_sell_' in sell(pair='BTC_ETH', rate=200, amount=1) + order = sell(pair='ETH/BTC', rate=200, amount=1) + assert 'id' in order + assert 'info' in order + assert order['id'] == order_id + + # test exception handling + with pytest.raises(DependencyException): + api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.InsufficientFunds) + mocker.patch('freqtrade.exchange._API', api_mock) + sell(pair='ETH/BTC', rate=200, amount=1) + + with pytest.raises(DependencyException): + api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.InvalidOrder) + mocker.patch('freqtrade.exchange._API', api_mock) + sell(pair='ETH/BTC', rate=200, amount=1) + + with pytest.raises(NetworkException): + api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.NetworkError) + mocker.patch('freqtrade.exchange._API', api_mock) + sell(pair='ETH/BTC', rate=200, amount=1) + + with pytest.raises(OperationalException): + api_mock.create_limit_sell_order = MagicMock(side_effect=ccxt.BaseError) + mocker.patch('freqtrade.exchange._API', api_mock) + sell(pair='ETH/BTC', rate=200, amount=1) def test_get_balance_dry_run(default_conf, mocker): @@ -129,7 +194,7 @@ def test_get_balance_dry_run(default_conf, mocker): def test_get_balance_prod(default_conf, mocker): api_mock = MagicMock() - api_mock.get_balance = MagicMock(return_value=123.4) + api_mock.fetch_balance = MagicMock(return_value={'BTC': {'free': 123.4}}) mocker.patch('freqtrade.exchange._API', api_mock) default_conf['dry_run'] = False @@ -137,36 +202,51 @@ def test_get_balance_prod(default_conf, mocker): assert get_balance(currency='BTC') == 123.4 + with pytest.raises(OperationalException): + api_mock.fetch_balance = MagicMock(side_effect=ccxt.BaseError) + mocker.patch('freqtrade.exchange._API', api_mock) + get_balance(currency='BTC') + def test_get_balances_dry_run(default_conf, mocker): default_conf['dry_run'] = True mocker.patch.dict('freqtrade.exchange._CONF', default_conf) - assert get_balances() == [] + assert get_balances() == {} def test_get_balances_prod(default_conf, mocker): balance_item = { - 'Currency': '1ST', - 'Balance': 10.0, - 'Available': 10.0, - 'Pending': 0.0, - 'CryptoAddress': None + 'free': 10.0, + 'total': 10.0, + 'used': 0.0 } api_mock = MagicMock() - api_mock.get_balances = MagicMock( - return_value=[balance_item, balance_item, balance_item]) + api_mock.fetch_balance = MagicMock(return_value={ + '1ST': balance_item, + '2ST': balance_item, + '3ST': balance_item + }) mocker.patch('freqtrade.exchange._API', api_mock) default_conf['dry_run'] = False mocker.patch.dict('freqtrade.exchange._CONF', default_conf) assert len(get_balances()) == 3 - assert get_balances()[0]['Currency'] == '1ST' - assert get_balances()[0]['Balance'] == 10.0 - assert get_balances()[0]['Available'] == 10.0 - assert get_balances()[0]['Pending'] == 0.0 + assert get_balances()['1ST']['free'] == 10.0 + assert get_balances()['1ST']['total'] == 10.0 + assert get_balances()['1ST']['used'] == 0.0 + + with pytest.raises(NetworkException): + api_mock.fetch_balance = MagicMock(side_effect=ccxt.NetworkError) + mocker.patch('freqtrade.exchange._API', api_mock) + get_balances() + + with pytest.raises(OperationalException): + api_mock.fetch_balance = MagicMock(side_effect=ccxt.BaseError) + mocker.patch('freqtrade.exchange._API', api_mock) + get_balances() # This test is somewhat redundant with @@ -174,57 +254,135 @@ def test_get_balances_prod(default_conf, mocker): def test_get_ticker(default_conf, mocker): maybe_init_api(default_conf, mocker) api_mock = MagicMock() - tick = {"success": True, 'result': {'Bid': 0.00001098, 'Ask': 0.00001099, 'Last': 0.0001}} - api_mock.get_ticker = MagicMock(return_value=tick) - mocker.patch('freqtrade.exchange.bittrex._API', api_mock) + tick = { + 'symbol': 'ETH/BTC', + 'bid': 0.00001098, + 'ask': 0.00001099, + 'last': 0.0001, + } + api_mock.fetch_ticker = MagicMock(return_value=tick) + mocker.patch('freqtrade.exchange._API', api_mock) # retrieve original ticker - ticker = get_ticker(pair='BTC_ETH') + ticker = get_ticker(pair='ETH/BTC') assert ticker['bid'] == 0.00001098 assert ticker['ask'] == 0.00001099 # change the ticker - tick = {"success": True, 'result': {"Bid": 0.5, "Ask": 1, "Last": 42}} - api_mock.get_ticker = MagicMock(return_value=tick) - mocker.patch('freqtrade.exchange.bittrex._API', api_mock) + tick = { + 'symbol': 'ETH/BTC', + 'bid': 0.5, + 'ask': 1, + 'last': 42, + } + api_mock.fetch_ticker = MagicMock(return_value=tick) + mocker.patch('freqtrade.exchange._API', api_mock) # if not caching the result we should get the same ticker # if not fetching a new result we should get the cached ticker - ticker = get_ticker(pair='BTC_ETH', refresh=False) + ticker = get_ticker(pair='ETH/BTC', refresh=False) assert ticker['bid'] == 0.00001098 assert ticker['ask'] == 0.00001099 # force ticker refresh - ticker = get_ticker(pair='BTC_ETH', refresh=True) + ticker = get_ticker(pair='ETH/BTC', refresh=True) assert ticker['bid'] == 0.5 assert ticker['ask'] == 1 + # change the ticker to a different pair which should not be cached + tick = { + 'symbol': 'LTC/BTC', + 'bid': 2, + 'ask': 3, + 'last': 4, + } + api_mock.fetch_ticker = MagicMock(return_value=tick, refresh=False) + mocker.patch('freqtrade.exchange._API', api_mock) + ticker = get_ticker(pair='LTC/BTC', refresh=False) + assert ticker['bid'] == 2 + assert ticker['ask'] == 3 + + # check that ETH/BTC is still cached + ticker = get_ticker(pair='ETH/BTC', refresh=False) + assert ticker['bid'] == 0.5 + assert ticker['ask'] == 1 + + with pytest.raises(NetworkException): + api_mock.fetch_ticker = MagicMock(side_effect=ccxt.NetworkError) + mocker.patch('freqtrade.exchange._API', api_mock) + get_ticker(pair='ETH/BTC', refresh=True) + + with pytest.raises(OperationalException): + api_mock.fetch_ticker = MagicMock(side_effect=ccxt.BaseError) + mocker.patch('freqtrade.exchange._API', api_mock) + get_ticker(pair='ETH/BTC', refresh=True) + def test_get_ticker_history(default_conf, mocker): api_mock = MagicMock() - tick = 123 - api_mock.get_ticker_history = MagicMock(return_value=tick) + tick = [ + [ + 1511686200000, # unix timestamp ms + 1, # open + 2, # high + 3, # low + 4, # close + 5, # volume (in quote currency) + ] + ] + has = PropertyMock(return_value={'fetchOHLCV': True}) + type(api_mock).has = has + api_mock.fetch_ohlcv = MagicMock(return_value=tick) mocker.patch('freqtrade.exchange._API', api_mock) # retrieve original ticker - ticks = get_ticker_history('BTC_ETH', int(default_conf['ticker_interval'])) - assert ticks == 123 + ticks = get_ticker_history('ETH/BTC', default_conf['ticker_interval']) + assert ticks[0]['O'] == 1 + assert ticks[0]['H'] == 2 + assert ticks[0]['L'] == 3 + assert ticks[0]['C'] == 4 + assert ticks[0]['V'] == 5 # change the ticker - tick = 999 - api_mock.get_ticker_history = MagicMock(return_value=tick) + new_tick = [ + [ + 1511686200000, # unix timestamp ms + 6, # open + 7, # high + 8, # low + 9, # close + 10, # volume (in quote currency) + ] + ] + api_mock.get_ticker_history = MagicMock(return_value=new_tick) mocker.patch('freqtrade.exchange._API', api_mock) # ensure caching will still return the original ticker - ticks = get_ticker_history('BTC_ETH', int(default_conf['ticker_interval'])) - assert ticks == 123 + ticks = get_ticker_history('ETH/BTC', default_conf['ticker_interval']) + assert ticks[0]['O'] == 1 + assert ticks[0]['H'] == 2 + assert ticks[0]['L'] == 3 + assert ticks[0]['C'] == 4 + assert ticks[0]['V'] == 5 + + with pytest.raises(NetworkException): + api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.NetworkError) + mocker.patch('freqtrade.exchange._API', api_mock) + # new symbol to get around cache + get_ticker_history('ABCD/BTC', default_conf['ticker_interval']) + + with pytest.raises(OperationalException): + api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.BaseError) + mocker.patch('freqtrade.exchange._API', api_mock) + # new symbol to get around cache + get_ticker_history('EFGH/BTC', default_conf['ticker_interval']) def test_cancel_order_dry_run(default_conf, mocker): default_conf['dry_run'] = True mocker.patch.dict('freqtrade.exchange._CONF', default_conf) - assert cancel_order(order_id='123') is None + assert cancel_order(order_id='123', pair='TKN/BTC') is None # Ensure that if not dry_run, we should call API @@ -234,7 +392,22 @@ def test_cancel_order(default_conf, mocker): api_mock = MagicMock() api_mock.cancel_order = MagicMock(return_value=123) mocker.patch('freqtrade.exchange._API', api_mock) - assert cancel_order(order_id='_') == 123 + assert cancel_order(order_id='_', pair='TKN/BTC') == 123 + + with pytest.raises(NetworkException): + api_mock.cancel_order = MagicMock(side_effect=ccxt.NetworkError) + mocker.patch('freqtrade.exchange._API', api_mock) + cancel_order(order_id='_', pair='TKN/BTC') + + with pytest.raises(DependencyException): + api_mock.cancel_order = MagicMock(side_effect=ccxt.InvalidOrder) + mocker.patch('freqtrade.exchange._API', api_mock) + cancel_order(order_id='_', pair='TKN/BTC') + + with pytest.raises(OperationalException): + api_mock.cancel_order = MagicMock(side_effect=ccxt.BaseError) + mocker.patch('freqtrade.exchange._API', api_mock) + cancel_order(order_id='_', pair='TKN/BTC') def test_get_order(default_conf, mocker): @@ -243,44 +416,48 @@ def test_get_order(default_conf, mocker): order = MagicMock() order.myid = 123 exchange._DRY_RUN_OPEN_ORDERS['X'] = order - print(exchange.get_order('X')) - assert exchange.get_order('X').myid == 123 + print(exchange.get_order('X', 'TKN/BTC')) + assert exchange.get_order('X', 'TKN/BTC').myid == 123 default_conf['dry_run'] = False mocker.patch.dict('freqtrade.exchange._CONF', default_conf) api_mock = MagicMock() - api_mock.get_order = MagicMock(return_value=456) + api_mock.fetch_order = MagicMock(return_value=456) mocker.patch('freqtrade.exchange._API', api_mock) - assert exchange.get_order('X') == 456 + assert exchange.get_order('X', 'TKN/BTC') == 456 + + with pytest.raises(NetworkException): + api_mock.fetch_order = MagicMock(side_effect=ccxt.NetworkError) + mocker.patch('freqtrade.exchange._API', api_mock) + exchange.get_order(order_id='_', pair='TKN/BTC') + + with pytest.raises(DependencyException): + api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder) + mocker.patch('freqtrade.exchange._API', api_mock) + exchange.get_order(order_id='_', pair='TKN/BTC') + + with pytest.raises(OperationalException): + api_mock.fetch_order = MagicMock(side_effect=ccxt.BaseError) + mocker.patch('freqtrade.exchange._API', api_mock) + exchange.get_order(order_id='_', pair='TKN/BTC') def test_get_name(default_conf, mocker): mocker.patch('freqtrade.exchange.validate_pairs', side_effect=lambda s: True) - default_conf['exchange']['name'] = 'bittrex' + default_conf['exchange']['name'] = 'binance' init(default_conf) - assert get_name() == 'Bittrex' + assert get_name() == 'Binance' def test_get_fee(default_conf, mocker): - mocker.patch('freqtrade.exchange.validate_pairs', - side_effect=lambda s: True) - init(default_conf) - - assert get_fee() == 0.0025 - - -def test_exchange_misc(mocker): api_mock = MagicMock() + api_mock.calculate_fee = MagicMock(return_value={ + 'type': 'taker', + 'currency': 'BTC', + 'rate': 0.025, + 'cost': 0.05 + }) mocker.patch('freqtrade.exchange._API', api_mock) - exchange.get_markets() - assert api_mock.get_markets.call_count == 1 - exchange.get_market_summaries() - assert api_mock.get_market_summaries.call_count == 1 - api_mock.name = 123 - assert exchange.get_name() == 123 - api_mock.fee = 456 - assert exchange.get_fee() == 456 - exchange.get_wallet_health() - assert api_mock.get_wallet_health.call_count == 1 + assert get_fee() == 0.025