Merge branch 'develop' into feature/volume-precision-pairlist
This commit is contained in:
@@ -12,12 +12,16 @@ import pytest
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade import DependencyException, OperationalException, TemporaryError
|
||||
from freqtrade.exchange import Exchange, Kraken
|
||||
from freqtrade.exchange import Exchange, Kraken, Binance
|
||||
from freqtrade.exchange.exchange import API_RETRY_COUNT
|
||||
from freqtrade.tests.conftest import get_patched_exchange, log_has, log_has_re
|
||||
from freqtrade.resolvers.exchange_resolver import ExchangeResolver
|
||||
|
||||
|
||||
# Make sure to always keep one exchange here which is NOT subclassed!!
|
||||
EXCHANGES = ['bittrex', 'binance', 'kraken', ]
|
||||
|
||||
|
||||
# Source: https://stackoverflow.com/questions/29881236/how-to-mock-asyncio-coroutines
|
||||
def get_mock_coro(return_value):
|
||||
async def mock_coro(*args, **kwargs):
|
||||
@@ -26,16 +30,17 @@ def get_mock_coro(return_value):
|
||||
return Mock(wraps=mock_coro)
|
||||
|
||||
|
||||
def ccxt_exceptionhandlers(mocker, default_conf, api_mock, fun, mock_ccxt_fun, **kwargs):
|
||||
def ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||
fun, mock_ccxt_fun, **kwargs):
|
||||
with pytest.raises(TemporaryError):
|
||||
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.NetworkError)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
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(OperationalException):
|
||||
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.BaseError)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
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 == 1
|
||||
|
||||
@@ -113,7 +118,7 @@ def test_exchange_resolver(default_conf, mocker, caplog):
|
||||
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())
|
||||
exchange = ExchangeResolver('Binance', default_conf).exchange
|
||||
exchange = ExchangeResolver('Bittrex', default_conf).exchange
|
||||
assert isinstance(exchange, Exchange)
|
||||
assert log_has_re(r"No .* specific subclass found. Using the generic class instead.",
|
||||
caplog.record_tuples)
|
||||
@@ -122,6 +127,15 @@ def test_exchange_resolver(default_conf, mocker, caplog):
|
||||
exchange = ExchangeResolver('Kraken', default_conf).exchange
|
||||
assert isinstance(exchange, Exchange)
|
||||
assert isinstance(exchange, Kraken)
|
||||
assert not isinstance(exchange, Binance)
|
||||
assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.",
|
||||
caplog.record_tuples)
|
||||
|
||||
exchange = ExchangeResolver('Binance', default_conf).exchange
|
||||
assert isinstance(exchange, Exchange)
|
||||
assert isinstance(exchange, Binance)
|
||||
assert not isinstance(exchange, Kraken)
|
||||
|
||||
assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.",
|
||||
caplog.record_tuples)
|
||||
|
||||
@@ -443,6 +457,58 @@ def test_exchange_has(default_conf, mocker):
|
||||
assert not exchange.exchange_has("deadbeef")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("side", [
|
||||
("buy"),
|
||||
("sell")
|
||||
])
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_dry_run_order(default_conf, mocker, side, exchange_name):
|
||||
default_conf['dry_run'] = True
|
||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||
|
||||
order = exchange.dry_run_order(
|
||||
pair='ETH/BTC', ordertype='limit', side=side, amount=1, rate=200)
|
||||
assert 'id' in order
|
||||
assert f'dry_run_{side}_' in order["id"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("side", [
|
||||
("buy"),
|
||||
("sell")
|
||||
])
|
||||
@pytest.mark.parametrize("ordertype,rate", [
|
||||
("market", None),
|
||||
("limit", 200),
|
||||
("stop_loss_limit", 200)
|
||||
])
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_create_order(default_conf, mocker, side, ordertype, rate, exchange_name):
|
||||
api_mock = MagicMock()
|
||||
order_id = 'test_prod_{}_{}'.format(side, randint(0, 10 ** 6))
|
||||
api_mock.create_order = MagicMock(return_value={
|
||||
'id': order_id,
|
||||
'info': {
|
||||
'foo': 'bar'
|
||||
}
|
||||
})
|
||||
default_conf['dry_run'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
|
||||
order = exchange.create_order(
|
||||
pair='ETH/BTC', ordertype=ordertype, side=side, amount=1, rate=200)
|
||||
|
||||
assert 'id' in order
|
||||
assert 'info' in order
|
||||
assert order['id'] == order_id
|
||||
assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
|
||||
assert api_mock.create_order.call_args[0][1] == ordertype
|
||||
assert api_mock.create_order.call_args[0][2] == side
|
||||
assert api_mock.create_order.call_args[0][3] == 1
|
||||
assert api_mock.create_order.call_args[0][4] is rate
|
||||
|
||||
|
||||
def test_buy_dry_run(default_conf, mocker):
|
||||
default_conf['dry_run'] = True
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
@@ -453,7 +519,8 @@ def test_buy_dry_run(default_conf, mocker):
|
||||
assert 'dry_run_buy_' in order['id']
|
||||
|
||||
|
||||
def test_buy_prod(default_conf, mocker):
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_buy_prod(default_conf, mocker, exchange_name):
|
||||
api_mock = MagicMock()
|
||||
order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6))
|
||||
order_type = 'market'
|
||||
@@ -467,7 +534,7 @@ def test_buy_prod(default_conf, mocker):
|
||||
default_conf['dry_run'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
|
||||
order = exchange.buy(pair='ETH/BTC', ordertype=order_type,
|
||||
amount=1, rate=200, time_in_force=time_in_force)
|
||||
@@ -498,25 +565,25 @@ def test_buy_prod(default_conf, mocker):
|
||||
# test exception handling
|
||||
with pytest.raises(DependencyException):
|
||||
api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
exchange.buy(pair='ETH/BTC', ordertype=order_type,
|
||||
amount=1, rate=200, time_in_force=time_in_force)
|
||||
|
||||
with pytest.raises(DependencyException):
|
||||
api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
exchange.buy(pair='ETH/BTC', ordertype=order_type,
|
||||
amount=1, rate=200, time_in_force=time_in_force)
|
||||
|
||||
with pytest.raises(TemporaryError):
|
||||
api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
exchange.buy(pair='ETH/BTC', ordertype=order_type,
|
||||
amount=1, rate=200, time_in_force=time_in_force)
|
||||
|
||||
with pytest.raises(OperationalException):
|
||||
api_mock.create_order = MagicMock(side_effect=ccxt.BaseError)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
exchange.buy(pair='ETH/BTC', ordertype=order_type,
|
||||
amount=1, rate=200, time_in_force=time_in_force)
|
||||
|
||||
@@ -621,7 +688,8 @@ def test_sell_dry_run(default_conf, mocker):
|
||||
assert 'dry_run_sell_' in order['id']
|
||||
|
||||
|
||||
def test_sell_prod(default_conf, mocker):
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_sell_prod(default_conf, mocker, exchange_name):
|
||||
api_mock = MagicMock()
|
||||
order_id = 'test_prod_sell_{}'.format(randint(0, 10 ** 6))
|
||||
order_type = 'market'
|
||||
@@ -635,7 +703,7 @@ def test_sell_prod(default_conf, mocker):
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
|
||||
mocker.patch('freqtrade.exchange.Exchange.symbol_price_prec', lambda s, x, y: y)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
|
||||
order = exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
|
||||
|
||||
@@ -660,22 +728,22 @@ def test_sell_prod(default_conf, mocker):
|
||||
# test exception handling
|
||||
with pytest.raises(DependencyException):
|
||||
api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
|
||||
|
||||
with pytest.raises(DependencyException):
|
||||
api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
|
||||
|
||||
with pytest.raises(TemporaryError):
|
||||
api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
|
||||
|
||||
with pytest.raises(OperationalException):
|
||||
api_mock.create_order = MagicMock(side_effect=ccxt.BaseError)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
|
||||
|
||||
|
||||
@@ -686,23 +754,24 @@ def test_get_balance_dry_run(default_conf, mocker):
|
||||
assert exchange.get_balance(currency='BTC') == 999.9
|
||||
|
||||
|
||||
def test_get_balance_prod(default_conf, mocker):
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_get_balance_prod(default_conf, mocker, exchange_name):
|
||||
api_mock = MagicMock()
|
||||
api_mock.fetch_balance = MagicMock(return_value={'BTC': {'free': 123.4}})
|
||||
default_conf['dry_run'] = False
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
|
||||
assert exchange.get_balance(currency='BTC') == 123.4
|
||||
|
||||
with pytest.raises(OperationalException):
|
||||
api_mock.fetch_balance = MagicMock(side_effect=ccxt.BaseError)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
|
||||
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)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_balances', MagicMock(return_value={}))
|
||||
exchange.get_balance(currency='BTC')
|
||||
|
||||
@@ -713,7 +782,8 @@ def test_get_balances_dry_run(default_conf, mocker):
|
||||
assert exchange.get_balances() == {}
|
||||
|
||||
|
||||
def test_get_balances_prod(default_conf, mocker):
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_get_balances_prod(default_conf, mocker, exchange_name):
|
||||
balance_item = {
|
||||
'free': 10.0,
|
||||
'total': 10.0,
|
||||
@@ -727,17 +797,18 @@ def test_get_balances_prod(default_conf, mocker):
|
||||
'3ST': balance_item
|
||||
})
|
||||
default_conf['dry_run'] = False
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
assert len(exchange.get_balances()) == 3
|
||||
assert exchange.get_balances()['1ST']['free'] == 10.0
|
||||
assert exchange.get_balances()['1ST']['total'] == 10.0
|
||||
assert exchange.get_balances()['1ST']['used'] == 0.0
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||
"get_balances", "fetch_balance")
|
||||
|
||||
|
||||
def test_get_tickers(default_conf, mocker):
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_get_tickers(default_conf, mocker, exchange_name):
|
||||
api_mock = MagicMock()
|
||||
tick = {'ETH/BTC': {
|
||||
'symbol': 'ETH/BTC',
|
||||
@@ -752,7 +823,7 @@ def test_get_tickers(default_conf, mocker):
|
||||
}
|
||||
}
|
||||
api_mock.fetch_tickers = MagicMock(return_value=tick)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
# retrieve original ticker
|
||||
tickers = exchange.get_tickers()
|
||||
|
||||
@@ -763,20 +834,21 @@ def test_get_tickers(default_conf, mocker):
|
||||
assert tickers['BCH/BTC']['bid'] == 0.6
|
||||
assert tickers['BCH/BTC']['ask'] == 0.5
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||
"get_tickers", "fetch_tickers")
|
||||
|
||||
with pytest.raises(OperationalException):
|
||||
api_mock.fetch_tickers = MagicMock(side_effect=ccxt.NotSupported)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
exchange.get_tickers()
|
||||
|
||||
api_mock.fetch_tickers = MagicMock(return_value={})
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
exchange.get_tickers()
|
||||
|
||||
|
||||
def test_get_ticker(default_conf, mocker):
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_get_ticker(default_conf, mocker, exchange_name):
|
||||
api_mock = MagicMock()
|
||||
tick = {
|
||||
'symbol': 'ETH/BTC',
|
||||
@@ -786,7 +858,7 @@ def test_get_ticker(default_conf, mocker):
|
||||
}
|
||||
api_mock.fetch_ticker = MagicMock(return_value=tick)
|
||||
api_mock.markets = {'ETH/BTC': {}}
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
# retrieve original ticker
|
||||
ticker = exchange.get_ticker(pair='ETH/BTC')
|
||||
|
||||
@@ -801,7 +873,7 @@ def test_get_ticker(default_conf, mocker):
|
||||
'last': 42,
|
||||
}
|
||||
api_mock.fetch_ticker = MagicMock(return_value=tick)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
|
||||
# if not caching the result we should get the same ticker
|
||||
# if not fetching a new result we should get the cached ticker
|
||||
@@ -820,20 +892,21 @@ def test_get_ticker(default_conf, mocker):
|
||||
exchange.get_ticker(pair='ETH/BTC', refresh=False)
|
||||
assert api_mock.fetch_ticker.call_count == 0
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||
"get_ticker", "fetch_ticker",
|
||||
pair='ETH/BTC', refresh=True)
|
||||
|
||||
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, id=exchange_name)
|
||||
exchange.get_ticker(pair='ETH/BTC', refresh=True)
|
||||
|
||||
with pytest.raises(DependencyException, match=r'Pair XRP/ETH not available'):
|
||||
exchange.get_ticker(pair='XRP/ETH', refresh=True)
|
||||
|
||||
|
||||
def test_get_history(default_conf, mocker, caplog):
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_get_history(default_conf, mocker, caplog, exchange_name):
|
||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||
tick = [
|
||||
[
|
||||
arrow.utcnow().timestamp * 1000, # unix timestamp ms
|
||||
@@ -912,7 +985,8 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None:
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test__async_get_candle_history(default_conf, mocker, caplog):
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
async def test__async_get_candle_history(default_conf, mocker, caplog, exchange_name):
|
||||
tick = [
|
||||
[
|
||||
arrow.utcnow().timestamp * 1000, # unix timestamp ms
|
||||
@@ -925,11 +999,10 @@ async def test__async_get_candle_history(default_conf, mocker, caplog):
|
||||
]
|
||||
|
||||
caplog.set_level(logging.DEBUG)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||
# Monkey-patch async function
|
||||
exchange._api_async.fetch_ohlcv = get_mock_coro(tick)
|
||||
|
||||
exchange = Exchange(default_conf)
|
||||
pair = 'ETH/BTC'
|
||||
res = await exchange._async_get_candle_history(pair, "5m")
|
||||
assert type(res) is tuple
|
||||
@@ -948,7 +1021,7 @@ async def test__async_get_candle_history(default_conf, mocker, caplog):
|
||||
api_mock = MagicMock()
|
||||
with pytest.raises(OperationalException, match=r'Could not fetch ticker data*'):
|
||||
api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.BaseError)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
await exchange._async_get_candle_history(pair, "5m",
|
||||
(arrow.utcnow().timestamp - 2000) * 1000)
|
||||
|
||||
@@ -1001,12 +1074,13 @@ def test_refresh_latest_ohlcv_inv_result(default_conf, mocker, caplog):
|
||||
assert log_has("Async code raised an exception: TypeError", caplog.record_tuples)
|
||||
|
||||
|
||||
def test_get_order_book(default_conf, mocker, order_book_l2):
|
||||
default_conf['exchange']['name'] = 'binance'
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_get_order_book(default_conf, mocker, order_book_l2, exchange_name):
|
||||
default_conf['exchange']['name'] = exchange_name
|
||||
api_mock = MagicMock()
|
||||
|
||||
api_mock.fetch_l2_order_book = order_book_l2
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
order_book = exchange.get_order_book(pair='ETH/BTC', limit=10)
|
||||
assert 'bids' in order_book
|
||||
assert 'asks' in order_book
|
||||
@@ -1014,19 +1088,20 @@ def test_get_order_book(default_conf, mocker, order_book_l2):
|
||||
assert len(order_book['asks']) == 10
|
||||
|
||||
|
||||
def test_get_order_book_exception(default_conf, mocker):
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_get_order_book_exception(default_conf, mocker, exchange_name):
|
||||
api_mock = MagicMock()
|
||||
with pytest.raises(OperationalException):
|
||||
api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.NotSupported)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
exchange.get_order_book(pair='ETH/BTC', limit=50)
|
||||
with pytest.raises(TemporaryError):
|
||||
api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.NetworkError)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
exchange.get_order_book(pair='ETH/BTC', limit=50)
|
||||
with pytest.raises(OperationalException):
|
||||
api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.BaseError)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
exchange.get_order_book(pair='ETH/BTC', limit=50)
|
||||
|
||||
|
||||
@@ -1039,8 +1114,9 @@ def make_fetch_ohlcv_mock(data):
|
||||
return fetch_ohlcv_mock
|
||||
|
||||
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
@pytest.mark.asyncio
|
||||
async def test___async_get_candle_history_sort(default_conf, mocker):
|
||||
async def test___async_get_candle_history_sort(default_conf, mocker, exchange_name):
|
||||
def sort_data(data, key):
|
||||
return sorted(data, key=key)
|
||||
|
||||
@@ -1058,7 +1134,7 @@ async def test___async_get_candle_history_sort(default_conf, mocker):
|
||||
[1527830700000, 0.07652, 0.07652, 0.07651, 0.07652, 10.04822687],
|
||||
[1527830400000, 0.07649, 0.07651, 0.07649, 0.07651, 2.5734867]
|
||||
]
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||
exchange._api_async.fetch_ohlcv = get_mock_coro(tick)
|
||||
sort_mock = mocker.patch('freqtrade.exchange.exchange.sorted', MagicMock(side_effect=sort_data))
|
||||
# Test the ticker history sort
|
||||
@@ -1120,36 +1196,39 @@ async def test___async_get_candle_history_sort(default_conf, mocker):
|
||||
assert ticks[9][5] == 2.31452783
|
||||
|
||||
|
||||
def test_cancel_order_dry_run(default_conf, mocker):
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_cancel_order_dry_run(default_conf, mocker, exchange_name):
|
||||
default_conf['dry_run'] = True
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||
assert exchange.cancel_order(order_id='123', pair='TKN/BTC') is None
|
||||
|
||||
|
||||
# Ensure that if not dry_run, we should call API
|
||||
def test_cancel_order(default_conf, mocker):
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_cancel_order(default_conf, mocker, exchange_name):
|
||||
default_conf['dry_run'] = False
|
||||
api_mock = MagicMock()
|
||||
api_mock.cancel_order = MagicMock(return_value=123)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
assert exchange.cancel_order(order_id='_', pair='TKN/BTC') == 123
|
||||
|
||||
with pytest.raises(DependencyException):
|
||||
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, id=exchange_name)
|
||||
exchange.cancel_order(order_id='_', pair='TKN/BTC')
|
||||
assert api_mock.cancel_order.call_count == API_RETRY_COUNT + 1
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||
"cancel_order", "cancel_order",
|
||||
order_id='_', pair='TKN/BTC')
|
||||
|
||||
|
||||
def test_get_order(default_conf, mocker):
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_get_order(default_conf, mocker, exchange_name):
|
||||
default_conf['dry_run'] = True
|
||||
order = MagicMock()
|
||||
order.myid = 123
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||
exchange._dry_run_open_orders['X'] = order
|
||||
print(exchange.get_order('X', 'TKN/BTC'))
|
||||
assert exchange.get_order('X', 'TKN/BTC').myid == 123
|
||||
@@ -1157,67 +1236,32 @@ def test_get_order(default_conf, mocker):
|
||||
default_conf['dry_run'] = False
|
||||
api_mock = MagicMock()
|
||||
api_mock.fetch_order = MagicMock(return_value=456)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
assert exchange.get_order('X', 'TKN/BTC') == 456
|
||||
|
||||
with pytest.raises(DependencyException):
|
||||
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, id=exchange_name)
|
||||
exchange.get_order(order_id='_', pair='TKN/BTC')
|
||||
assert api_mock.fetch_order.call_count == API_RETRY_COUNT + 1
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||
'get_order', 'fetch_order',
|
||||
order_id='_', pair='TKN/BTC')
|
||||
|
||||
|
||||
def test_name(default_conf, mocker):
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_name(default_conf, mocker, exchange_name):
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
||||
default_conf['exchange']['name'] = 'binance'
|
||||
default_conf['exchange']['name'] = exchange_name
|
||||
exchange = Exchange(default_conf)
|
||||
|
||||
assert exchange.name == 'Binance'
|
||||
assert exchange.name == exchange_name.title()
|
||||
assert exchange.id == exchange_name
|
||||
|
||||
|
||||
def test_id(default_conf, mocker):
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
||||
default_conf['exchange']['name'] = 'binance'
|
||||
exchange = Exchange(default_conf)
|
||||
assert exchange.id == 'binance'
|
||||
|
||||
|
||||
def test_get_pair_detail_url(default_conf, mocker, caplog):
|
||||
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
||||
default_conf['exchange']['name'] = 'binance'
|
||||
exchange = Exchange(default_conf)
|
||||
|
||||
url = exchange.get_pair_detail_url('TKN/ETH')
|
||||
assert 'TKN' in url
|
||||
assert 'ETH' in url
|
||||
|
||||
url = exchange.get_pair_detail_url('LOOONG/BTC')
|
||||
assert 'LOOONG' in url
|
||||
assert 'BTC' in url
|
||||
|
||||
default_conf['exchange']['name'] = 'bittrex'
|
||||
exchange = Exchange(default_conf)
|
||||
|
||||
url = exchange.get_pair_detail_url('TKN/ETH')
|
||||
assert 'TKN' in url
|
||||
assert 'ETH' in url
|
||||
|
||||
url = exchange.get_pair_detail_url('LOOONG/BTC')
|
||||
assert 'LOOONG' in url
|
||||
assert 'BTC' in url
|
||||
|
||||
default_conf['exchange']['name'] = 'poloniex'
|
||||
exchange = Exchange(default_conf)
|
||||
url = exchange.get_pair_detail_url('LOOONG/BTC')
|
||||
assert '' == url
|
||||
assert log_has('Could not get exchange url for Poloniex', caplog.record_tuples)
|
||||
|
||||
|
||||
def test_get_trades_for_order(default_conf, mocker):
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_get_trades_for_order(default_conf, mocker, exchange_name):
|
||||
order_id = 'ABCD-ABCD'
|
||||
since = datetime(2018, 5, 5)
|
||||
default_conf["dry_run"] = False
|
||||
@@ -1244,13 +1288,13 @@ def test_get_trades_for_order(default_conf, mocker):
|
||||
'amount': 0.2340606,
|
||||
'fee': {'cost': 0.06179, 'currency': 'BTC'}
|
||||
}])
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
|
||||
orders = exchange.get_trades_for_order(order_id, 'LTC/BTC', since)
|
||||
assert len(orders) == 1
|
||||
assert orders[0]['price'] == 165
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||
'get_trades_for_order', 'fetch_my_trades',
|
||||
order_id=order_id, pair='LTC/BTC', since=since)
|
||||
|
||||
@@ -1258,10 +1302,11 @@ def test_get_trades_for_order(default_conf, mocker):
|
||||
assert exchange.get_trades_for_order(order_id, 'LTC/BTC', since) == []
|
||||
|
||||
|
||||
def test_get_markets(default_conf, mocker, markets):
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_get_markets(default_conf, mocker, markets, exchange_name):
|
||||
api_mock = MagicMock()
|
||||
api_mock.fetch_markets = markets
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
ret = exchange.get_markets()
|
||||
assert isinstance(ret, list)
|
||||
assert len(ret) == 9
|
||||
@@ -1269,11 +1314,12 @@ def test_get_markets(default_conf, mocker, markets):
|
||||
assert ret[0]["id"] == "ethbtc"
|
||||
assert ret[0]["symbol"] == "ETH/BTC"
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||
'get_markets', 'fetch_markets')
|
||||
|
||||
|
||||
def test_get_fee(default_conf, mocker):
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_get_fee(default_conf, mocker, exchange_name):
|
||||
api_mock = MagicMock()
|
||||
api_mock.calculate_fee = MagicMock(return_value={
|
||||
'type': 'taker',
|
||||
@@ -1281,11 +1327,11 @@ def test_get_fee(default_conf, mocker):
|
||||
'rate': 0.025,
|
||||
'cost': 0.05
|
||||
})
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||
|
||||
assert exchange.get_fee() == 0.025
|
||||
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||
'get_fee', 'calculate_fee')
|
||||
|
||||
|
||||
|
@@ -51,7 +51,6 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
|
||||
assert {
|
||||
'trade_id': 1,
|
||||
'pair': 'ETH/BTC',
|
||||
'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
|
||||
'date': ANY,
|
||||
'open_rate': 1.099e-05,
|
||||
'close_rate': None,
|
||||
@@ -72,7 +71,6 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
|
||||
assert {
|
||||
'trade_id': 1,
|
||||
'pair': 'ETH/BTC',
|
||||
'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
|
||||
'date': ANY,
|
||||
'open_rate': 1.099e-05,
|
||||
'close_rate': None,
|
||||
|
@@ -5,7 +5,7 @@
|
||||
import re
|
||||
from datetime import datetime
|
||||
from random import randint
|
||||
from unittest.mock import MagicMock, ANY
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import arrow
|
||||
import pytest
|
||||
@@ -183,7 +183,6 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None:
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
get_ticker=ticker,
|
||||
get_pair_detail_url=MagicMock(),
|
||||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
@@ -195,7 +194,6 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None:
|
||||
_rpc_trade_status=MagicMock(return_value=[{
|
||||
'trade_id': 1,
|
||||
'pair': 'ETH/BTC',
|
||||
'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
|
||||
'date': arrow.utcnow(),
|
||||
'open_rate': 1.099e-05,
|
||||
'close_rate': None,
|
||||
@@ -270,7 +268,7 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No
|
||||
telegram._status(bot=MagicMock(), update=update)
|
||||
|
||||
assert msg_mock.call_count == 1
|
||||
assert '[ETH/BTC]' in msg_mock.call_args_list[0][0][0]
|
||||
assert 'ETH/BTC' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker) -> None:
|
||||
@@ -721,7 +719,6 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
|
||||
'exchange': 'Bittrex',
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'profit',
|
||||
'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
|
||||
'limit': 1.172e-05,
|
||||
'amount': 90.99181073703367,
|
||||
'open_rate': 1.099e-05,
|
||||
@@ -776,7 +773,6 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
|
||||
'exchange': 'Bittrex',
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'loss',
|
||||
'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
|
||||
'limit': 1.044e-05,
|
||||
'amount': 90.99181073703367,
|
||||
'open_rate': 1.099e-05,
|
||||
@@ -796,7 +792,6 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker
|
||||
return_value=15000.0)
|
||||
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_pair_detail_url', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
get_ticker=ticker,
|
||||
@@ -823,7 +818,6 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker
|
||||
'exchange': 'Bittrex',
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'loss',
|
||||
'market_url': ANY,
|
||||
'limit': 1.098e-05,
|
||||
'amount': 90.99181073703367,
|
||||
'open_rate': 1.099e-05,
|
||||
@@ -1100,7 +1094,6 @@ def test_send_msg_buy_notification(default_conf, mocker) -> None:
|
||||
'type': RPCMessageType.BUY_NOTIFICATION,
|
||||
'exchange': 'Bittrex',
|
||||
'pair': 'ETH/BTC',
|
||||
'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
|
||||
'limit': 1.099e-05,
|
||||
'stake_amount': 0.001,
|
||||
'stake_amount_fiat': 0.0,
|
||||
@@ -1108,7 +1101,7 @@ def test_send_msg_buy_notification(default_conf, mocker) -> None:
|
||||
'fiat_currency': 'USD'
|
||||
})
|
||||
assert msg_mock.call_args[0][0] \
|
||||
== '*Bittrex:* Buying [ETH/BTC](https://bittrex.com/Market/Index?MarketName=BTC-ETH)\n' \
|
||||
== '*Bittrex:* Buying ETH/BTC\n' \
|
||||
'with limit `0.00001099\n' \
|
||||
'(0.001000 BTC,0.000 USD)`'
|
||||
|
||||
@@ -1129,7 +1122,6 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
|
||||
'exchange': 'Binance',
|
||||
'pair': 'KEY/ETH',
|
||||
'gain': 'loss',
|
||||
'market_url': 'https://www.binance.com/tradeDetail.html?symbol=KEY_ETH',
|
||||
'limit': 3.201e-05,
|
||||
'amount': 1333.3333333333335,
|
||||
'open_rate': 7.5e-05,
|
||||
@@ -1141,8 +1133,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
|
||||
'sell_reason': SellType.STOP_LOSS.value
|
||||
})
|
||||
assert msg_mock.call_args[0][0] \
|
||||
== ('*Binance:* Selling [KEY/ETH]'
|
||||
'(https://www.binance.com/tradeDetail.html?symbol=KEY_ETH)\n'
|
||||
== ('*Binance:* Selling KEY/ETH\n'
|
||||
'*Limit:* `0.00003201`\n'
|
||||
'*Amount:* `1333.33333333`\n'
|
||||
'*Open Rate:* `0.00007500`\n'
|
||||
@@ -1156,7 +1147,6 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
|
||||
'exchange': 'Binance',
|
||||
'pair': 'KEY/ETH',
|
||||
'gain': 'loss',
|
||||
'market_url': 'https://www.binance.com/tradeDetail.html?symbol=KEY_ETH',
|
||||
'limit': 3.201e-05,
|
||||
'amount': 1333.3333333333335,
|
||||
'open_rate': 7.5e-05,
|
||||
@@ -1167,8 +1157,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
|
||||
'sell_reason': SellType.STOP_LOSS.value
|
||||
})
|
||||
assert msg_mock.call_args[0][0] \
|
||||
== ('*Binance:* Selling [KEY/ETH]'
|
||||
'(https://www.binance.com/tradeDetail.html?symbol=KEY_ETH)\n'
|
||||
== ('*Binance:* Selling KEY/ETH\n'
|
||||
'*Limit:* `0.00003201`\n'
|
||||
'*Amount:* `1333.33333333`\n'
|
||||
'*Open Rate:* `0.00007500`\n'
|
||||
@@ -1256,7 +1245,6 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None:
|
||||
'type': RPCMessageType.BUY_NOTIFICATION,
|
||||
'exchange': 'Bittrex',
|
||||
'pair': 'ETH/BTC',
|
||||
'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
|
||||
'limit': 1.099e-05,
|
||||
'stake_amount': 0.001,
|
||||
'stake_amount_fiat': 0.0,
|
||||
@@ -1264,7 +1252,7 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None:
|
||||
'fiat_currency': None
|
||||
})
|
||||
assert msg_mock.call_args[0][0] \
|
||||
== '*Bittrex:* Buying [ETH/BTC](https://bittrex.com/Market/Index?MarketName=BTC-ETH)\n' \
|
||||
== '*Bittrex:* Buying ETH/BTC\n' \
|
||||
'with limit `0.00001099\n' \
|
||||
'(0.001000 BTC)`'
|
||||
|
||||
@@ -1284,7 +1272,6 @@ def test_send_msg_sell_notification_no_fiat(default_conf, mocker) -> None:
|
||||
'exchange': 'Binance',
|
||||
'pair': 'KEY/ETH',
|
||||
'gain': 'loss',
|
||||
'market_url': 'https://www.binance.com/tradeDetail.html?symbol=KEY_ETH',
|
||||
'limit': 3.201e-05,
|
||||
'amount': 1333.3333333333335,
|
||||
'open_rate': 7.5e-05,
|
||||
@@ -1296,8 +1283,7 @@ def test_send_msg_sell_notification_no_fiat(default_conf, mocker) -> None:
|
||||
'sell_reason': SellType.STOP_LOSS.value
|
||||
})
|
||||
assert msg_mock.call_args[0][0] \
|
||||
== '*Binance:* Selling [KEY/ETH]' \
|
||||
'(https://www.binance.com/tradeDetail.html?symbol=KEY_ETH)\n' \
|
||||
== '*Binance:* Selling KEY/ETH\n' \
|
||||
'*Limit:* `0.00003201`\n' \
|
||||
'*Amount:* `1333.33333333`\n' \
|
||||
'*Open Rate:* `0.00007500`\n' \
|
||||
|
@@ -48,7 +48,6 @@ def test_send_msg(default_conf, mocker):
|
||||
'type': RPCMessageType.BUY_NOTIFICATION,
|
||||
'exchange': 'Bittrex',
|
||||
'pair': 'ETH/BTC',
|
||||
'market_url': "http://mockedurl/ETH_BTC",
|
||||
'limit': 0.005,
|
||||
'stake_amount': 0.8,
|
||||
'stake_amount_fiat': 500,
|
||||
@@ -73,7 +72,6 @@ def test_send_msg(default_conf, mocker):
|
||||
'exchange': 'Bittrex',
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': "profit",
|
||||
'market_url': "http://mockedurl/ETH_BTC",
|
||||
'limit': 0.005,
|
||||
'amount': 0.8,
|
||||
'open_rate': 0.004,
|
||||
@@ -127,7 +125,6 @@ def test_exception_send_msg(default_conf, mocker, caplog):
|
||||
'type': RPCMessageType.BUY_NOTIFICATION,
|
||||
'exchange': 'Bittrex',
|
||||
'pair': 'ETH/BTC',
|
||||
'market_url': "http://mockedurl/ETH_BTC",
|
||||
'limit': 0.005,
|
||||
'stake_amount': 0.8,
|
||||
'stake_amount_fiat': 500,
|
||||
|
@@ -16,7 +16,7 @@ def test_parse_args_none() -> None:
|
||||
|
||||
def test_parse_args_defaults() -> None:
|
||||
args = Arguments([], '').get_parsed_arg()
|
||||
assert args.config == 'config.json'
|
||||
assert args.config == ['config.json']
|
||||
assert args.strategy_path is None
|
||||
assert args.datadir is None
|
||||
assert args.loglevel == 0
|
||||
@@ -24,10 +24,15 @@ def test_parse_args_defaults() -> None:
|
||||
|
||||
def test_parse_args_config() -> None:
|
||||
args = Arguments(['-c', '/dev/null'], '').get_parsed_arg()
|
||||
assert args.config == '/dev/null'
|
||||
assert args.config == ['/dev/null']
|
||||
|
||||
args = Arguments(['--config', '/dev/null'], '').get_parsed_arg()
|
||||
assert args.config == '/dev/null'
|
||||
assert args.config == ['/dev/null']
|
||||
|
||||
args = Arguments(['--config', '/dev/null',
|
||||
'--config', '/dev/zero'],
|
||||
'').get_parsed_arg()
|
||||
assert args.config == ['/dev/null', '/dev/zero']
|
||||
|
||||
|
||||
def test_parse_args_db_url() -> None:
|
||||
@@ -139,7 +144,7 @@ def test_parse_args_backtesting_custom() -> None:
|
||||
'TestStrategy'
|
||||
]
|
||||
call_args = Arguments(args, '').get_parsed_arg()
|
||||
assert call_args.config == 'test_conf.json'
|
||||
assert call_args.config == ['test_conf.json']
|
||||
assert call_args.live is True
|
||||
assert call_args.loglevel == 0
|
||||
assert call_args.subparser == 'backtesting'
|
||||
@@ -158,7 +163,7 @@ def test_parse_args_hyperopt_custom() -> None:
|
||||
'--spaces', 'buy'
|
||||
]
|
||||
call_args = Arguments(args, '').get_parsed_arg()
|
||||
assert call_args.config == 'test_conf.json'
|
||||
assert call_args.config == ['test_conf.json']
|
||||
assert call_args.epochs == 20
|
||||
assert call_args.loglevel == 0
|
||||
assert call_args.subparser == 'hyperopt'
|
||||
|
@@ -1,15 +1,15 @@
|
||||
# pragma pylint: disable=missing-docstring, protected-access, invalid-name
|
||||
|
||||
import json
|
||||
from argparse import Namespace
|
||||
import logging
|
||||
from argparse import Namespace
|
||||
from copy import deepcopy
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
from jsonschema import validate, ValidationError, Draft4Validator
|
||||
from jsonschema import Draft4Validator, ValidationError, validate
|
||||
|
||||
from freqtrade import constants
|
||||
from freqtrade import OperationalException
|
||||
from freqtrade import OperationalException, constants
|
||||
from freqtrade.arguments import Arguments
|
||||
from freqtrade.configuration import Configuration, set_loggers
|
||||
from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL
|
||||
@@ -50,18 +50,49 @@ def test_load_config_file(default_conf, mocker, caplog) -> None:
|
||||
validated_conf = configuration._load_config_file('somefile')
|
||||
assert file_mock.call_count == 1
|
||||
assert validated_conf.items() >= default_conf.items()
|
||||
assert 'internals' in validated_conf
|
||||
assert log_has('Validating configuration ...', caplog.record_tuples)
|
||||
|
||||
|
||||
def test_load_config_max_open_trades_zero(default_conf, mocker, caplog) -> None:
|
||||
default_conf['max_open_trades'] = 0
|
||||
file_mock = mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
||||
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
||||
read_data=json.dumps(default_conf)
|
||||
))
|
||||
|
||||
Configuration(Namespace())._load_config_file('somefile')
|
||||
assert file_mock.call_count == 1
|
||||
args = Arguments([], '').get_parsed_arg()
|
||||
configuration = Configuration(args)
|
||||
validated_conf = configuration.load_config()
|
||||
|
||||
assert validated_conf['max_open_trades'] == 0
|
||||
assert 'internals' in validated_conf
|
||||
assert log_has('Validating configuration ...', caplog.record_tuples)
|
||||
|
||||
|
||||
def test_load_config_combine_dicts(default_conf, mocker, caplog) -> None:
|
||||
conf1 = deepcopy(default_conf)
|
||||
conf2 = deepcopy(default_conf)
|
||||
del conf1['exchange']['key']
|
||||
del conf1['exchange']['secret']
|
||||
del conf2['exchange']['name']
|
||||
conf2['exchange']['pair_whitelist'] += ['NANO/BTC']
|
||||
|
||||
config_files = [conf1, conf2]
|
||||
|
||||
configsmock = MagicMock(side_effect=config_files)
|
||||
mocker.patch('freqtrade.configuration.Configuration._load_config_file', configsmock)
|
||||
|
||||
arg_list = ['-c', 'test_conf.json', '--config', 'test2_conf.json', ]
|
||||
args = Arguments(arg_list, '').get_parsed_arg()
|
||||
configuration = Configuration(args)
|
||||
validated_conf = configuration.load_config()
|
||||
|
||||
exchange_conf = default_conf['exchange']
|
||||
assert validated_conf['exchange']['name'] == exchange_conf['name']
|
||||
assert validated_conf['exchange']['key'] == exchange_conf['key']
|
||||
assert validated_conf['exchange']['secret'] == exchange_conf['secret']
|
||||
assert validated_conf['exchange']['pair_whitelist'] != conf1['exchange']['pair_whitelist']
|
||||
assert validated_conf['exchange']['pair_whitelist'] == conf2['exchange']['pair_whitelist']
|
||||
|
||||
assert 'internals' in validated_conf
|
||||
assert log_has('Validating configuration ...', caplog.record_tuples)
|
||||
|
||||
|
||||
|
@@ -1872,7 +1872,6 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, moc
|
||||
'exchange': 'Bittrex',
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'profit',
|
||||
'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
|
||||
'limit': 1.172e-05,
|
||||
'amount': 90.99181073703367,
|
||||
'open_rate': 1.099e-05,
|
||||
@@ -1919,7 +1918,6 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets,
|
||||
'exchange': 'Bittrex',
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'loss',
|
||||
'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
|
||||
'limit': 1.044e-05,
|
||||
'amount': 90.99181073703367,
|
||||
'open_rate': 1.099e-05,
|
||||
@@ -1974,7 +1972,6 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe
|
||||
'exchange': 'Bittrex',
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'loss',
|
||||
'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
|
||||
'limit': 1.08801e-05,
|
||||
'amount': 90.99181073703367,
|
||||
'open_rate': 1.099e-05,
|
||||
@@ -2146,7 +2143,6 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee,
|
||||
'exchange': 'Bittrex',
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'profit',
|
||||
'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
|
||||
'limit': 1.172e-05,
|
||||
'amount': 90.99181073703367,
|
||||
'open_rate': 1.099e-05,
|
||||
@@ -2194,7 +2190,6 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee,
|
||||
'exchange': 'Bittrex',
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'loss',
|
||||
'market_url': 'https://bittrex.com/Market/Index?MarketName=BTC-ETH',
|
||||
'limit': 1.044e-05,
|
||||
'amount': 90.99181073703367,
|
||||
'open_rate': 1.099e-05,
|
||||
|
@@ -22,7 +22,7 @@ def test_parse_args_backtesting(mocker) -> None:
|
||||
main(['backtesting'])
|
||||
assert backtesting_mock.call_count == 1
|
||||
call_args = backtesting_mock.call_args[0][0]
|
||||
assert call_args.config == 'config.json'
|
||||
assert call_args.config == ['config.json']
|
||||
assert call_args.live is False
|
||||
assert call_args.loglevel == 0
|
||||
assert call_args.subparser == 'backtesting'
|
||||
@@ -35,7 +35,7 @@ def test_main_start_hyperopt(mocker) -> None:
|
||||
main(['hyperopt'])
|
||||
assert hyperopt_mock.call_count == 1
|
||||
call_args = hyperopt_mock.call_args[0][0]
|
||||
assert call_args.config == 'config.json'
|
||||
assert call_args.config == ['config.json']
|
||||
assert call_args.loglevel == 0
|
||||
assert call_args.subparser == 'hyperopt'
|
||||
assert call_args.func is not None
|
||||
|
@@ -629,3 +629,48 @@ def test_adjust_stop_loss(limit_buy_order, limit_sell_order, fee):
|
||||
assert round(trade.stop_loss, 8) == 1.26
|
||||
assert trade.max_rate == 1.4
|
||||
assert trade.initial_stop_loss == 0.95
|
||||
|
||||
|
||||
def test_get_open(default_conf, fee):
|
||||
init(default_conf)
|
||||
|
||||
# Simulate dry_run entries
|
||||
trade = Trade(
|
||||
pair='ETH/BTC',
|
||||
stake_amount=0.001,
|
||||
amount=123.0,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
open_rate=0.123,
|
||||
exchange='bittrex',
|
||||
open_order_id='dry_run_buy_12345'
|
||||
)
|
||||
Trade.session.add(trade)
|
||||
|
||||
trade = Trade(
|
||||
pair='ETC/BTC',
|
||||
stake_amount=0.001,
|
||||
amount=123.0,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
open_rate=0.123,
|
||||
exchange='bittrex',
|
||||
is_open=False,
|
||||
open_order_id='dry_run_sell_12345'
|
||||
)
|
||||
Trade.session.add(trade)
|
||||
|
||||
# Simulate prod entry
|
||||
trade = Trade(
|
||||
pair='ETC/BTC',
|
||||
stake_amount=0.001,
|
||||
amount=123.0,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
open_rate=0.123,
|
||||
exchange='bittrex',
|
||||
open_order_id='prod_buy_12345'
|
||||
)
|
||||
Trade.session.add(trade)
|
||||
|
||||
assert len(Trade.get_open_trades()) == 2
|
||||
|
Reference in New Issue
Block a user