Merge pull request #1595 from freqtrade/binance_subclass
Create binance Subclass and parametrize exchange-tests
This commit is contained in:
commit
ee0e381d65
@ -1,2 +1,3 @@
|
|||||||
from freqtrade.exchange.exchange import Exchange # noqa: F401
|
from freqtrade.exchange.exchange import Exchange # noqa: F401
|
||||||
from freqtrade.exchange.kraken import Kraken # noqa: F401
|
from freqtrade.exchange.kraken import Kraken # noqa: F401
|
||||||
|
from freqtrade.exchange.binance import Binance # noqa: F401
|
||||||
|
26
freqtrade/exchange/binance.py
Normal file
26
freqtrade/exchange/binance.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
""" Binance exchange subclass """
|
||||||
|
import logging
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
from freqtrade.exchange import Exchange
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Binance(Exchange):
|
||||||
|
|
||||||
|
_ft_has: Dict = {
|
||||||
|
"stoploss_on_exchange": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_order_book(self, pair: str, limit: int = 100) -> dict:
|
||||||
|
"""
|
||||||
|
get order book level 2 from exchange
|
||||||
|
|
||||||
|
20180619: binance support limits but only on specific range
|
||||||
|
"""
|
||||||
|
limit_range = [5, 10, 20, 50, 100, 500, 1000]
|
||||||
|
# get next-higher step in the limit_range list
|
||||||
|
limit = min(list(filter(lambda x: limit <= x, limit_range)))
|
||||||
|
|
||||||
|
return super(Binance, self).get_order_book(pair, limit)
|
@ -24,7 +24,7 @@ API_RETRY_COUNT = 4
|
|||||||
# Urls to exchange markets, insert quote and base with .format()
|
# Urls to exchange markets, insert quote and base with .format()
|
||||||
_EXCHANGE_URLS = {
|
_EXCHANGE_URLS = {
|
||||||
ccxt.bittrex.__name__: '/Market/Index?MarketName={quote}-{base}',
|
ccxt.bittrex.__name__: '/Market/Index?MarketName={quote}-{base}',
|
||||||
ccxt.binance.__name__: '/tradeDetail.html?symbol={base}_{quote}'
|
ccxt.binance.__name__: '/tradeDetail.html?symbol={base}_{quote}',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -69,11 +69,17 @@ class Exchange(object):
|
|||||||
_conf: Dict = {}
|
_conf: Dict = {}
|
||||||
_params: Dict = {}
|
_params: Dict = {}
|
||||||
|
|
||||||
|
# Dict to specify which options each exchange implements
|
||||||
|
# TODO: this should be merged with attributes from subclasses
|
||||||
|
# To avoid having to copy/paste this to all subclasses.
|
||||||
|
_ft_has = {
|
||||||
|
"stoploss_on_exchange": False,
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, config: dict) -> None:
|
def __init__(self, config: dict) -> None:
|
||||||
"""
|
"""
|
||||||
Initializes this module with the given config,
|
Initializes this module with the given config,
|
||||||
it does basic validation whether the specified
|
it does basic validation whether the specified exchange and pairs are valid.
|
||||||
exchange and pairs are valid.
|
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self._conf.update(config)
|
self._conf.update(config)
|
||||||
@ -236,8 +242,8 @@ class Exchange(object):
|
|||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
f'Exchange {self.name} does not support market orders.')
|
f'Exchange {self.name} does not support market orders.')
|
||||||
|
|
||||||
if order_types.get('stoploss_on_exchange'):
|
if (order_types.get("stoploss_on_exchange")
|
||||||
if self.name != 'Binance':
|
and not self._ft_has.get("stoploss_on_exchange", False)):
|
||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
'On exchange stoploss is not supported for %s.' % self.name
|
'On exchange stoploss is not supported for %s.' % self.name
|
||||||
)
|
)
|
||||||
@ -368,6 +374,7 @@ class Exchange(object):
|
|||||||
"""
|
"""
|
||||||
creates a stoploss limit order.
|
creates a stoploss limit order.
|
||||||
NOTICE: it is not supported by all exchanges. only binance is tested for now.
|
NOTICE: it is not supported by all exchanges. only binance is tested for now.
|
||||||
|
TODO: implementation maybe needs to be moved to the binance subclass
|
||||||
"""
|
"""
|
||||||
ordertype = "stop_loss_limit"
|
ordertype = "stop_loss_limit"
|
||||||
|
|
||||||
@ -614,18 +621,8 @@ class Exchange(object):
|
|||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
20180619: bittrex doesnt support limits -.-
|
20180619: bittrex doesnt support limits -.-
|
||||||
20180619: binance support limits but only on specific range
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if self._api.name == 'Binance':
|
|
||||||
limit_range = [5, 10, 20, 50, 100, 500, 1000]
|
|
||||||
# get next-higher step in the limit_range list
|
|
||||||
limit = min(list(filter(lambda x: limit <= x, limit_range)))
|
|
||||||
# above script works like loop below (but with slightly better performance):
|
|
||||||
# for limitx in limit_range:
|
|
||||||
# if limit <= limitx:
|
|
||||||
# limit = limitx
|
|
||||||
# break
|
|
||||||
|
|
||||||
return self._api.fetch_l2_order_book(pair, limit)
|
return self._api.fetch_l2_order_book(pair, limit)
|
||||||
except ccxt.NotSupported as e:
|
except ccxt.NotSupported as e:
|
||||||
|
@ -12,12 +12,16 @@ import pytest
|
|||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade import DependencyException, OperationalException, TemporaryError
|
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.exchange.exchange import API_RETRY_COUNT
|
||||||
from freqtrade.tests.conftest import get_patched_exchange, log_has, log_has_re
|
from freqtrade.tests.conftest import get_patched_exchange, log_has, log_has_re
|
||||||
from freqtrade.resolvers.exchange_resolver import ExchangeResolver
|
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
|
# Source: https://stackoverflow.com/questions/29881236/how-to-mock-asyncio-coroutines
|
||||||
def get_mock_coro(return_value):
|
def get_mock_coro(return_value):
|
||||||
async def mock_coro(*args, **kwargs):
|
async def mock_coro(*args, **kwargs):
|
||||||
@ -26,16 +30,17 @@ def get_mock_coro(return_value):
|
|||||||
return Mock(wraps=mock_coro)
|
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):
|
with pytest.raises(TemporaryError):
|
||||||
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.NetworkError)
|
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)
|
getattr(exchange, fun)(**kwargs)
|
||||||
assert api_mock.__dict__[mock_ccxt_fun].call_count == API_RETRY_COUNT + 1
|
assert api_mock.__dict__[mock_ccxt_fun].call_count == API_RETRY_COUNT + 1
|
||||||
|
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.BaseError)
|
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)
|
getattr(exchange, fun)(**kwargs)
|
||||||
assert api_mock.__dict__[mock_ccxt_fun].call_count == 1
|
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._load_async_markets', MagicMock())
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', 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 isinstance(exchange, Exchange)
|
||||||
assert log_has_re(r"No .* specific subclass found. Using the generic class instead.",
|
assert log_has_re(r"No .* specific subclass found. Using the generic class instead.",
|
||||||
caplog.record_tuples)
|
caplog.record_tuples)
|
||||||
@ -122,6 +127,15 @@ def test_exchange_resolver(default_conf, mocker, caplog):
|
|||||||
exchange = ExchangeResolver('Kraken', default_conf).exchange
|
exchange = ExchangeResolver('Kraken', default_conf).exchange
|
||||||
assert isinstance(exchange, Exchange)
|
assert isinstance(exchange, Exchange)
|
||||||
assert isinstance(exchange, Kraken)
|
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.",
|
assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.",
|
||||||
caplog.record_tuples)
|
caplog.record_tuples)
|
||||||
|
|
||||||
@ -447,9 +461,10 @@ def test_exchange_has(default_conf, mocker):
|
|||||||
("buy"),
|
("buy"),
|
||||||
("sell")
|
("sell")
|
||||||
])
|
])
|
||||||
def test_dry_run_order(default_conf, mocker, side):
|
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||||
|
def test_dry_run_order(default_conf, mocker, side, exchange_name):
|
||||||
default_conf['dry_run'] = True
|
default_conf['dry_run'] = True
|
||||||
exchange = get_patched_exchange(mocker, default_conf)
|
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||||
|
|
||||||
order = exchange.dry_run_order(
|
order = exchange.dry_run_order(
|
||||||
pair='ETH/BTC', ordertype='limit', side=side, amount=1, rate=200)
|
pair='ETH/BTC', ordertype='limit', side=side, amount=1, rate=200)
|
||||||
@ -466,7 +481,8 @@ def test_dry_run_order(default_conf, mocker, side):
|
|||||||
("limit", 200),
|
("limit", 200),
|
||||||
("stop_loss_limit", 200)
|
("stop_loss_limit", 200)
|
||||||
])
|
])
|
||||||
def test_create_order(default_conf, mocker, side, ordertype, rate):
|
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||||
|
def test_create_order(default_conf, mocker, side, ordertype, rate, exchange_name):
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
order_id = 'test_prod_{}_{}'.format(side, randint(0, 10 ** 6))
|
order_id = 'test_prod_{}_{}'.format(side, randint(0, 10 ** 6))
|
||||||
api_mock.create_order = MagicMock(return_value={
|
api_mock.create_order = MagicMock(return_value={
|
||||||
@ -478,7 +494,7 @@ def test_create_order(default_conf, mocker, side, ordertype, rate):
|
|||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
|
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)
|
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.create_order(
|
order = exchange.create_order(
|
||||||
pair='ETH/BTC', ordertype=ordertype, side=side, amount=1, rate=200)
|
pair='ETH/BTC', ordertype=ordertype, side=side, amount=1, rate=200)
|
||||||
@ -503,7 +519,8 @@ def test_buy_dry_run(default_conf, mocker):
|
|||||||
assert 'dry_run_buy_' in order['id']
|
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()
|
api_mock = MagicMock()
|
||||||
order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6))
|
order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6))
|
||||||
order_type = 'market'
|
order_type = 'market'
|
||||||
@ -517,7 +534,7 @@ def test_buy_prod(default_conf, mocker):
|
|||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
mocker.patch('freqtrade.exchange.Exchange.symbol_amount_prec', lambda s, x, y: y)
|
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)
|
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,
|
order = exchange.buy(pair='ETH/BTC', ordertype=order_type,
|
||||||
amount=1, rate=200, time_in_force=time_in_force)
|
amount=1, rate=200, time_in_force=time_in_force)
|
||||||
@ -548,25 +565,25 @@ def test_buy_prod(default_conf, mocker):
|
|||||||
# test exception handling
|
# test exception handling
|
||||||
with pytest.raises(DependencyException):
|
with pytest.raises(DependencyException):
|
||||||
api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds)
|
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,
|
exchange.buy(pair='ETH/BTC', ordertype=order_type,
|
||||||
amount=1, rate=200, time_in_force=time_in_force)
|
amount=1, rate=200, time_in_force=time_in_force)
|
||||||
|
|
||||||
with pytest.raises(DependencyException):
|
with pytest.raises(DependencyException):
|
||||||
api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder)
|
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,
|
exchange.buy(pair='ETH/BTC', ordertype=order_type,
|
||||||
amount=1, rate=200, time_in_force=time_in_force)
|
amount=1, rate=200, time_in_force=time_in_force)
|
||||||
|
|
||||||
with pytest.raises(TemporaryError):
|
with pytest.raises(TemporaryError):
|
||||||
api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError)
|
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,
|
exchange.buy(pair='ETH/BTC', ordertype=order_type,
|
||||||
amount=1, rate=200, time_in_force=time_in_force)
|
amount=1, rate=200, time_in_force=time_in_force)
|
||||||
|
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
api_mock.create_order = MagicMock(side_effect=ccxt.BaseError)
|
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,
|
exchange.buy(pair='ETH/BTC', ordertype=order_type,
|
||||||
amount=1, rate=200, time_in_force=time_in_force)
|
amount=1, rate=200, time_in_force=time_in_force)
|
||||||
|
|
||||||
@ -671,7 +688,8 @@ def test_sell_dry_run(default_conf, mocker):
|
|||||||
assert 'dry_run_sell_' in order['id']
|
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()
|
api_mock = MagicMock()
|
||||||
order_id = 'test_prod_sell_{}'.format(randint(0, 10 ** 6))
|
order_id = 'test_prod_sell_{}'.format(randint(0, 10 ** 6))
|
||||||
order_type = 'market'
|
order_type = 'market'
|
||||||
@ -685,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_amount_prec', lambda s, x, y: y)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.symbol_price_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)
|
order = exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
|
||||||
|
|
||||||
@ -710,22 +728,22 @@ def test_sell_prod(default_conf, mocker):
|
|||||||
# test exception handling
|
# test exception handling
|
||||||
with pytest.raises(DependencyException):
|
with pytest.raises(DependencyException):
|
||||||
api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds)
|
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)
|
exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
|
||||||
|
|
||||||
with pytest.raises(DependencyException):
|
with pytest.raises(DependencyException):
|
||||||
api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder)
|
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)
|
exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
|
||||||
|
|
||||||
with pytest.raises(TemporaryError):
|
with pytest.raises(TemporaryError):
|
||||||
api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError)
|
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)
|
exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
|
||||||
|
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
api_mock.create_order = MagicMock(side_effect=ccxt.BaseError)
|
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)
|
exchange.sell(pair='ETH/BTC', ordertype=order_type, amount=1, rate=200)
|
||||||
|
|
||||||
|
|
||||||
@ -736,23 +754,24 @@ def test_get_balance_dry_run(default_conf, mocker):
|
|||||||
assert exchange.get_balance(currency='BTC') == 999.9
|
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 = MagicMock()
|
||||||
api_mock.fetch_balance = MagicMock(return_value={'BTC': {'free': 123.4}})
|
api_mock.fetch_balance = MagicMock(return_value={'BTC': {'free': 123.4}})
|
||||||
default_conf['dry_run'] = False
|
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
|
assert exchange.get_balance(currency='BTC') == 123.4
|
||||||
|
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
api_mock.fetch_balance = MagicMock(side_effect=ccxt.BaseError)
|
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')
|
exchange.get_balance(currency='BTC')
|
||||||
|
|
||||||
with pytest.raises(TemporaryError, match=r'.*balance due to malformed exchange response:.*'):
|
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={}))
|
mocker.patch('freqtrade.exchange.Exchange.get_balances', MagicMock(return_value={}))
|
||||||
exchange.get_balance(currency='BTC')
|
exchange.get_balance(currency='BTC')
|
||||||
|
|
||||||
@ -763,7 +782,8 @@ def test_get_balances_dry_run(default_conf, mocker):
|
|||||||
assert exchange.get_balances() == {}
|
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 = {
|
balance_item = {
|
||||||
'free': 10.0,
|
'free': 10.0,
|
||||||
'total': 10.0,
|
'total': 10.0,
|
||||||
@ -777,17 +797,18 @@ def test_get_balances_prod(default_conf, mocker):
|
|||||||
'3ST': balance_item
|
'3ST': balance_item
|
||||||
})
|
})
|
||||||
default_conf['dry_run'] = False
|
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 len(exchange.get_balances()) == 3
|
||||||
assert exchange.get_balances()['1ST']['free'] == 10.0
|
assert exchange.get_balances()['1ST']['free'] == 10.0
|
||||||
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
|
||||||
|
|
||||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||||
"get_balances", "fetch_balance")
|
"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()
|
api_mock = MagicMock()
|
||||||
tick = {'ETH/BTC': {
|
tick = {'ETH/BTC': {
|
||||||
'symbol': 'ETH/BTC',
|
'symbol': 'ETH/BTC',
|
||||||
@ -802,7 +823,7 @@ def test_get_tickers(default_conf, mocker):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
api_mock.fetch_tickers = MagicMock(return_value=tick)
|
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
|
# retrieve original ticker
|
||||||
tickers = exchange.get_tickers()
|
tickers = exchange.get_tickers()
|
||||||
|
|
||||||
@ -813,20 +834,21 @@ 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
|
||||||
|
|
||||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||||
"get_tickers", "fetch_tickers")
|
"get_tickers", "fetch_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)
|
||||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||||
exchange.get_tickers()
|
exchange.get_tickers()
|
||||||
|
|
||||||
api_mock.fetch_tickers = MagicMock(return_value={})
|
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()
|
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()
|
api_mock = MagicMock()
|
||||||
tick = {
|
tick = {
|
||||||
'symbol': 'ETH/BTC',
|
'symbol': 'ETH/BTC',
|
||||||
@ -836,7 +858,7 @@ def test_get_ticker(default_conf, mocker):
|
|||||||
}
|
}
|
||||||
api_mock.fetch_ticker = MagicMock(return_value=tick)
|
api_mock.fetch_ticker = MagicMock(return_value=tick)
|
||||||
api_mock.markets = {'ETH/BTC': {}}
|
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
|
# retrieve original ticker
|
||||||
ticker = exchange.get_ticker(pair='ETH/BTC')
|
ticker = exchange.get_ticker(pair='ETH/BTC')
|
||||||
|
|
||||||
@ -851,7 +873,7 @@ def test_get_ticker(default_conf, mocker):
|
|||||||
'last': 42,
|
'last': 42,
|
||||||
}
|
}
|
||||||
api_mock.fetch_ticker = MagicMock(return_value=tick)
|
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 caching the result we should get the same ticker
|
||||||
# if not fetching a new result we should get the cached ticker
|
# if not fetching a new result we should get the cached ticker
|
||||||
@ -870,20 +892,21 @@ 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
|
||||||
|
|
||||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||||
"get_ticker", "fetch_ticker",
|
"get_ticker", "fetch_ticker",
|
||||||
pair='ETH/BTC', refresh=True)
|
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, id=exchange_name)
|
||||||
exchange.get_ticker(pair='ETH/BTC', refresh=True)
|
exchange.get_ticker(pair='ETH/BTC', refresh=True)
|
||||||
|
|
||||||
with pytest.raises(DependencyException, match=r'Pair XRP/ETH not available'):
|
with pytest.raises(DependencyException, match=r'Pair XRP/ETH not available'):
|
||||||
exchange.get_ticker(pair='XRP/ETH', refresh=True)
|
exchange.get_ticker(pair='XRP/ETH', refresh=True)
|
||||||
|
|
||||||
|
|
||||||
def test_get_history(default_conf, mocker, caplog):
|
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||||
exchange = get_patched_exchange(mocker, default_conf)
|
def test_get_history(default_conf, mocker, caplog, exchange_name):
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||||
tick = [
|
tick = [
|
||||||
[
|
[
|
||||||
arrow.utcnow().timestamp * 1000, # unix timestamp ms
|
arrow.utcnow().timestamp * 1000, # unix timestamp ms
|
||||||
@ -962,7 +985,8 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@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 = [
|
tick = [
|
||||||
[
|
[
|
||||||
arrow.utcnow().timestamp * 1000, # unix timestamp ms
|
arrow.utcnow().timestamp * 1000, # unix timestamp ms
|
||||||
@ -975,11 +999,10 @@ async def test__async_get_candle_history(default_conf, mocker, caplog):
|
|||||||
]
|
]
|
||||||
|
|
||||||
caplog.set_level(logging.DEBUG)
|
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
|
# Monkey-patch async function
|
||||||
exchange._api_async.fetch_ohlcv = get_mock_coro(tick)
|
exchange._api_async.fetch_ohlcv = get_mock_coro(tick)
|
||||||
|
|
||||||
exchange = Exchange(default_conf)
|
|
||||||
pair = 'ETH/BTC'
|
pair = 'ETH/BTC'
|
||||||
res = await exchange._async_get_candle_history(pair, "5m")
|
res = await exchange._async_get_candle_history(pair, "5m")
|
||||||
assert type(res) is tuple
|
assert type(res) is tuple
|
||||||
@ -998,7 +1021,7 @@ async def test__async_get_candle_history(default_conf, mocker, caplog):
|
|||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
with pytest.raises(OperationalException, match=r'Could not fetch ticker data*'):
|
with pytest.raises(OperationalException, match=r'Could not fetch ticker data*'):
|
||||||
api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.BaseError)
|
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",
|
await exchange._async_get_candle_history(pair, "5m",
|
||||||
(arrow.utcnow().timestamp - 2000) * 1000)
|
(arrow.utcnow().timestamp - 2000) * 1000)
|
||||||
|
|
||||||
@ -1051,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)
|
assert log_has("Async code raised an exception: TypeError", caplog.record_tuples)
|
||||||
|
|
||||||
|
|
||||||
def test_get_order_book(default_conf, mocker, order_book_l2):
|
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||||
default_conf['exchange']['name'] = 'binance'
|
def test_get_order_book(default_conf, mocker, order_book_l2, exchange_name):
|
||||||
|
default_conf['exchange']['name'] = exchange_name
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
|
|
||||||
api_mock.fetch_l2_order_book = order_book_l2
|
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)
|
order_book = exchange.get_order_book(pair='ETH/BTC', limit=10)
|
||||||
assert 'bids' in order_book
|
assert 'bids' in order_book
|
||||||
assert 'asks' in order_book
|
assert 'asks' in order_book
|
||||||
@ -1064,19 +1088,20 @@ def test_get_order_book(default_conf, mocker, order_book_l2):
|
|||||||
assert len(order_book['asks']) == 10
|
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()
|
api_mock = MagicMock()
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.NotSupported)
|
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)
|
exchange.get_order_book(pair='ETH/BTC', limit=50)
|
||||||
with pytest.raises(TemporaryError):
|
with pytest.raises(TemporaryError):
|
||||||
api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.NetworkError)
|
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)
|
exchange.get_order_book(pair='ETH/BTC', limit=50)
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.BaseError)
|
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)
|
exchange.get_order_book(pair='ETH/BTC', limit=50)
|
||||||
|
|
||||||
|
|
||||||
@ -1089,8 +1114,9 @@ def make_fetch_ohlcv_mock(data):
|
|||||||
return fetch_ohlcv_mock
|
return fetch_ohlcv_mock
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||||
@pytest.mark.asyncio
|
@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):
|
def sort_data(data, key):
|
||||||
return sorted(data, key=key)
|
return sorted(data, key=key)
|
||||||
|
|
||||||
@ -1108,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],
|
[1527830700000, 0.07652, 0.07652, 0.07651, 0.07652, 10.04822687],
|
||||||
[1527830400000, 0.07649, 0.07651, 0.07649, 0.07651, 2.5734867]
|
[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)
|
exchange._api_async.fetch_ohlcv = get_mock_coro(tick)
|
||||||
sort_mock = mocker.patch('freqtrade.exchange.exchange.sorted', MagicMock(side_effect=sort_data))
|
sort_mock = mocker.patch('freqtrade.exchange.exchange.sorted', MagicMock(side_effect=sort_data))
|
||||||
# Test the ticker history sort
|
# Test the ticker history sort
|
||||||
@ -1170,36 +1196,39 @@ async def test___async_get_candle_history_sort(default_conf, mocker):
|
|||||||
assert ticks[9][5] == 2.31452783
|
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
|
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
|
assert exchange.cancel_order(order_id='123', pair='TKN/BTC') is None
|
||||||
|
|
||||||
|
|
||||||
# Ensure that if not dry_run, we should call API
|
# 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
|
default_conf['dry_run'] = False
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
api_mock.cancel_order = MagicMock(return_value=123)
|
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
|
assert exchange.cancel_order(order_id='_', pair='TKN/BTC') == 123
|
||||||
|
|
||||||
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, id=exchange_name)
|
||||||
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
|
||||||
|
|
||||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||||
"cancel_order", "cancel_order",
|
"cancel_order", "cancel_order",
|
||||||
order_id='_', pair='TKN/BTC')
|
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
|
default_conf['dry_run'] = True
|
||||||
order = MagicMock()
|
order = MagicMock()
|
||||||
order.myid = 123
|
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
|
exchange._dry_run_open_orders['X'] = order
|
||||||
print(exchange.get_order('X', 'TKN/BTC'))
|
print(exchange.get_order('X', 'TKN/BTC'))
|
||||||
assert exchange.get_order('X', 'TKN/BTC').myid == 123
|
assert exchange.get_order('X', 'TKN/BTC').myid == 123
|
||||||
@ -1207,33 +1236,28 @@ def test_get_order(default_conf, mocker):
|
|||||||
default_conf['dry_run'] = False
|
default_conf['dry_run'] = False
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
api_mock.fetch_order = MagicMock(return_value=456)
|
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
|
assert exchange.get_order('X', 'TKN/BTC') == 456
|
||||||
|
|
||||||
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, id=exchange_name)
|
||||||
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
|
||||||
|
|
||||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||||
'get_order', 'fetch_order',
|
'get_order', 'fetch_order',
|
||||||
order_id='_', pair='TKN/BTC')
|
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={}))
|
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)
|
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):
|
def test_get_pair_detail_url(default_conf, mocker, caplog):
|
||||||
@ -1267,7 +1291,8 @@ def test_get_pair_detail_url(default_conf, mocker, caplog):
|
|||||||
assert log_has('Could not get exchange url for Poloniex', caplog.record_tuples)
|
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'
|
order_id = 'ABCD-ABCD'
|
||||||
since = datetime(2018, 5, 5)
|
since = datetime(2018, 5, 5)
|
||||||
default_conf["dry_run"] = False
|
default_conf["dry_run"] = False
|
||||||
@ -1294,13 +1319,13 @@ def test_get_trades_for_order(default_conf, mocker):
|
|||||||
'amount': 0.2340606,
|
'amount': 0.2340606,
|
||||||
'fee': {'cost': 0.06179, 'currency': 'BTC'}
|
'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)
|
orders = exchange.get_trades_for_order(order_id, 'LTC/BTC', since)
|
||||||
assert len(orders) == 1
|
assert len(orders) == 1
|
||||||
assert orders[0]['price'] == 165
|
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',
|
'get_trades_for_order', 'fetch_my_trades',
|
||||||
order_id=order_id, pair='LTC/BTC', since=since)
|
order_id=order_id, pair='LTC/BTC', since=since)
|
||||||
|
|
||||||
@ -1308,10 +1333,11 @@ def test_get_trades_for_order(default_conf, mocker):
|
|||||||
assert exchange.get_trades_for_order(order_id, 'LTC/BTC', since) == []
|
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 = MagicMock()
|
||||||
api_mock.fetch_markets = markets
|
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()
|
ret = exchange.get_markets()
|
||||||
assert isinstance(ret, list)
|
assert isinstance(ret, list)
|
||||||
assert len(ret) == 6
|
assert len(ret) == 6
|
||||||
@ -1319,11 +1345,12 @@ 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"
|
||||||
|
|
||||||
ccxt_exceptionhandlers(mocker, default_conf, api_mock,
|
ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name,
|
||||||
'get_markets', 'fetch_markets')
|
'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 = MagicMock()
|
||||||
api_mock.calculate_fee = MagicMock(return_value={
|
api_mock.calculate_fee = MagicMock(return_value={
|
||||||
'type': 'taker',
|
'type': 'taker',
|
||||||
@ -1331,11 +1358,11 @@ def test_get_fee(default_conf, mocker):
|
|||||||
'rate': 0.025,
|
'rate': 0.025,
|
||||||
'cost': 0.05
|
'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
|
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')
|
'get_fee', 'calculate_fee')
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user