|
|
|
@@ -73,6 +73,7 @@ def test_init(default_conf, mocker, caplog):
|
|
|
|
|
|
|
|
|
|
def test_init_ccxt_kwargs(default_conf, mocker, caplog):
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
|
|
|
|
caplog.set_level(logging.INFO)
|
|
|
|
|
conf = copy.deepcopy(default_conf)
|
|
|
|
|
conf['exchange']['ccxt_async_config'] = {'aiohttp_trust_env': True}
|
|
|
|
@@ -121,9 +122,10 @@ def test_init_exception(default_conf, mocker):
|
|
|
|
|
|
|
|
|
|
def test_exchange_resolver(default_conf, mocker, caplog):
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=MagicMock()))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
|
|
|
|
exchange = ExchangeResolver.load_exchange('Bittrex', default_conf)
|
|
|
|
|
assert isinstance(exchange, Exchange)
|
|
|
|
|
assert log_has_re(r"No .* specific subclass found. Using the generic class instead.", caplog)
|
|
|
|
@@ -257,9 +259,10 @@ def test__load_markets(default_conf, mocker, caplog):
|
|
|
|
|
api_mock = MagicMock()
|
|
|
|
|
api_mock.load_markets = MagicMock(side_effect=ccxt.BaseError("SomeError"))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
|
|
|
|
Exchange(default_conf)
|
|
|
|
|
assert log_has('Unable to initialize markets. Reason: SomeError', caplog)
|
|
|
|
|
|
|
|
|
@@ -315,6 +318,44 @@ def test__reload_markets_exception(default_conf, mocker, caplog):
|
|
|
|
|
assert log_has_re(r"Could not reload markets.*", caplog)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("stake_currency", ['ETH', 'BTC', 'USDT'])
|
|
|
|
|
def test_validate_stake_currency(default_conf, stake_currency, mocker, caplog):
|
|
|
|
|
default_conf['stake_currency'] = stake_currency
|
|
|
|
|
api_mock = MagicMock()
|
|
|
|
|
type(api_mock).markets = PropertyMock(return_value={
|
|
|
|
|
'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'},
|
|
|
|
|
'XRP/ETH': {'quote': 'ETH'}, 'NEO/USDT': {'quote': 'USDT'},
|
|
|
|
|
})
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
|
|
|
|
Exchange(default_conf)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_validate_stake_currency_error(default_conf, mocker, caplog):
|
|
|
|
|
default_conf['stake_currency'] = 'XRP'
|
|
|
|
|
api_mock = MagicMock()
|
|
|
|
|
type(api_mock).markets = PropertyMock(return_value={
|
|
|
|
|
'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'},
|
|
|
|
|
'XRP/ETH': {'quote': 'ETH'}, 'NEO/USDT': {'quote': 'USDT'},
|
|
|
|
|
})
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
|
|
|
|
with pytest.raises(OperationalException,
|
|
|
|
|
match=r'XRP is not available as stake on .*'
|
|
|
|
|
'Available currencies are: BTC, ETH, USDT'):
|
|
|
|
|
Exchange(default_conf)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_get_quote_currencies(default_conf, mocker):
|
|
|
|
|
ex = get_patched_exchange(mocker, default_conf)
|
|
|
|
|
|
|
|
|
|
assert set(ex.get_quote_currencies()) == set(['USD', 'BTC', 'USDT'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_validate_pairs(default_conf, mocker): # test exchange.validate_pairs directly
|
|
|
|
|
api_mock = MagicMock()
|
|
|
|
|
type(api_mock).markets = PropertyMock(return_value={
|
|
|
|
@@ -324,8 +365,9 @@ def test_validate_pairs(default_conf, mocker): # test exchange.validate_pairs d
|
|
|
|
|
type(api_mock).id = id_mock
|
|
|
|
|
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
|
|
|
|
Exchange(default_conf)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -335,8 +377,9 @@ def test_validate_pairs_not_available(default_conf, mocker):
|
|
|
|
|
'XRP/BTC': {'inactive': True}
|
|
|
|
|
})
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
|
|
|
|
|
|
|
|
|
with pytest.raises(OperationalException, match=r'not available'):
|
|
|
|
|
Exchange(default_conf)
|
|
|
|
@@ -349,8 +392,9 @@ def test_validate_pairs_exception(default_conf, mocker, caplog):
|
|
|
|
|
|
|
|
|
|
type(api_mock).markets = PropertyMock(return_value={})
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', api_mock)
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
|
|
|
|
|
|
|
|
|
with pytest.raises(OperationalException, match=r'Pair ETH/BTC is not available on Binance'):
|
|
|
|
|
Exchange(default_conf)
|
|
|
|
@@ -368,8 +412,9 @@ def test_validate_pairs_restricted(default_conf, mocker, caplog):
|
|
|
|
|
'NEO/BTC': {'info': 'TestString'}, # info can also be a string ...
|
|
|
|
|
})
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
|
|
|
|
|
|
|
|
|
Exchange(default_conf)
|
|
|
|
|
assert log_has(f"Pair XRP/BTC is restricted for some users on this exchange."
|
|
|
|
@@ -377,8 +422,11 @@ def test_validate_pairs_restricted(default_conf, mocker, caplog):
|
|
|
|
|
f"on the exchange and eventually remove XRP/BTC from your whitelist.", caplog)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_validate_timeframes(default_conf, mocker):
|
|
|
|
|
default_conf["ticker_interval"] = "5m"
|
|
|
|
|
@pytest.mark.parametrize("timeframe", [
|
|
|
|
|
('5m'), ("1m"), ("15m"), ("1h")
|
|
|
|
|
])
|
|
|
|
|
def test_validate_timeframes(default_conf, mocker, timeframe):
|
|
|
|
|
default_conf["ticker_interval"] = timeframe
|
|
|
|
|
api_mock = MagicMock()
|
|
|
|
|
id_mock = PropertyMock(return_value='test_exchange')
|
|
|
|
|
type(api_mock).id = id_mock
|
|
|
|
@@ -390,7 +438,8 @@ def test_validate_timeframes(default_conf, mocker):
|
|
|
|
|
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
|
|
|
|
Exchange(default_conf)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -399,7 +448,8 @@ def test_validate_timeframes_failed(default_conf, mocker):
|
|
|
|
|
api_mock = MagicMock()
|
|
|
|
|
id_mock = PropertyMock(return_value='test_exchange')
|
|
|
|
|
type(api_mock).id = id_mock
|
|
|
|
|
timeframes = PropertyMock(return_value={'1m': '1m',
|
|
|
|
|
timeframes = PropertyMock(return_value={'15s': '15s',
|
|
|
|
|
'1m': '1m',
|
|
|
|
|
'5m': '5m',
|
|
|
|
|
'15m': '15m',
|
|
|
|
|
'1h': '1h'})
|
|
|
|
@@ -411,6 +461,11 @@ def test_validate_timeframes_failed(default_conf, mocker):
|
|
|
|
|
with pytest.raises(OperationalException,
|
|
|
|
|
match=r"Invalid ticker interval '3m'. This exchange supports.*"):
|
|
|
|
|
Exchange(default_conf)
|
|
|
|
|
default_conf["ticker_interval"] = "15s"
|
|
|
|
|
|
|
|
|
|
with pytest.raises(OperationalException,
|
|
|
|
|
match=r"Timeframes < 1m are currently not supported by Freqtrade."):
|
|
|
|
|
Exchange(default_conf)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_validate_timeframes_emulated_ohlcv_1(default_conf, mocker):
|
|
|
|
@@ -424,7 +479,8 @@ def test_validate_timeframes_emulated_ohlcv_1(default_conf, mocker):
|
|
|
|
|
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
|
|
|
|
with pytest.raises(OperationalException,
|
|
|
|
|
match=r'The ccxt library does not provide the list of timeframes '
|
|
|
|
|
r'for the exchange ".*" and this exchange '
|
|
|
|
@@ -445,6 +501,7 @@ def test_validate_timeframes_emulated_ohlcvi_2(default_conf, mocker):
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_markets',
|
|
|
|
|
MagicMock(return_value={'timeframes': None}))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
|
|
|
|
with pytest.raises(OperationalException,
|
|
|
|
|
match=r'The ccxt library does not provide the list of timeframes '
|
|
|
|
|
r'for the exchange ".*" and this exchange '
|
|
|
|
@@ -465,7 +522,8 @@ def test_validate_timeframes_not_in_config(default_conf, mocker):
|
|
|
|
|
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
|
|
|
|
Exchange(default_conf)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -475,8 +533,9 @@ def test_validate_order_types(default_conf, mocker):
|
|
|
|
|
type(api_mock).has = PropertyMock(return_value={'createMarketOrder': True})
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.name', 'Bittrex')
|
|
|
|
|
default_conf['order_types'] = {
|
|
|
|
|
'buy': 'limit',
|
|
|
|
@@ -517,8 +576,9 @@ def test_validate_order_types_not_in_config(default_conf, mocker):
|
|
|
|
|
api_mock = MagicMock()
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
|
|
|
|
|
|
|
|
|
conf = copy.deepcopy(default_conf)
|
|
|
|
|
Exchange(conf)
|
|
|
|
@@ -529,9 +589,10 @@ def test_validate_required_startup_candles(default_conf, mocker, caplog):
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value='Binance'))
|
|
|
|
|
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', api_mock)
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs')
|
|
|
|
|
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency')
|
|
|
|
|
|
|
|
|
|
default_conf['startup_candle_count'] = 20
|
|
|
|
|
ex = Exchange(default_conf)
|
|
|
|
@@ -1665,7 +1726,9 @@ def test_merge_ft_has_dict(default_conf, mocker):
|
|
|
|
|
_init_ccxt=MagicMock(return_value=MagicMock()),
|
|
|
|
|
_load_async_markets=MagicMock(),
|
|
|
|
|
validate_pairs=MagicMock(),
|
|
|
|
|
validate_timeframes=MagicMock())
|
|
|
|
|
validate_timeframes=MagicMock(),
|
|
|
|
|
validate_stakecurrency=MagicMock()
|
|
|
|
|
)
|
|
|
|
|
ex = Exchange(default_conf)
|
|
|
|
|
assert ex._ft_has == Exchange._ft_has_default
|
|
|
|
|
|
|
|
|
|