From 5cdd9dd44596d12c4d937306b38412f736f943f5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 28 Jan 2021 19:40:10 +0100 Subject: [PATCH] Cache markets in the exchange object --- freqtrade/exchange/exchange.py | 13 +++++++------ tests/conftest.py | 1 - tests/exchange/test_exchange.py | 29 ++++++++++++++--------------- tests/test_freqtradebot.py | 3 +++ 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 436c8e4e9..78e17359d 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -66,6 +66,7 @@ class Exchange: """ self._api: ccxt.Exchange = None self._api_async: ccxt_async.Exchange = None + self._markets: Dict = {} self._config.update(config) @@ -198,10 +199,10 @@ class Exchange: @property def markets(self) -> Dict: """exchange ccxt markets""" - if not self._api.markets: + if not self._markets: logger.info("Markets were not loaded. Loading them now..") self._load_markets() - return self._api.markets + return self._markets @property def precisionMode(self) -> str: @@ -291,7 +292,7 @@ class Exchange: def _load_markets(self) -> None: """ Initialize markets both sync and async """ try: - self._api.load_markets() + self._markets = self._api.load_markets() self._load_async_markets() self._last_markets_refresh = arrow.utcnow().int_timestamp except ccxt.BaseError as e: @@ -306,7 +307,7 @@ class Exchange: return None logger.debug("Performing scheduled market reload..") try: - self._api.load_markets(reload=True) + self._markets = self._api.load_markets(reload=True) # Also reload async markets to avoid issues with newly listed pairs self._load_async_markets(reload=True) self._last_markets_refresh = arrow.utcnow().int_timestamp @@ -660,8 +661,8 @@ class Exchange: @retrier def fetch_ticker(self, pair: str) -> dict: try: - if (pair not in self._api.markets or - self._api.markets[pair].get('active', False) is False): + if (pair not in self.markets or + self.markets[pair].get('active', False) is False): raise ExchangeError(f"Pair {pair} not available") data = self._api.fetch_ticker(pair) return data diff --git a/tests/conftest.py b/tests/conftest.py index 75a98dcc5..61899dd53 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -73,7 +73,6 @@ def patched_configuration_load_config_file(mocker, config) -> None: def patch_exchange(mocker, api_mock=None, id='bittrex', mock_markets=True) -> None: mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock(return_value={})) - mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock()) mocker.patch('freqtrade.exchange.Exchange.validate_ordertypes', MagicMock()) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 9d655997f..a35dc9da9 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -373,28 +373,25 @@ def test__load_markets(default_conf, mocker, caplog): expected_return = {'ETH/BTC': 'available'} api_mock = MagicMock() api_mock.load_markets = MagicMock(return_value=expected_return) - type(api_mock).markets = expected_return + mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) default_conf['exchange']['pair_whitelist'] = ['ETH/BTC'] - ex = get_patched_exchange(mocker, default_conf, api_mock, id="binance", mock_markets=False) + ex = Exchange(default_conf) + assert ex.markets == expected_return def test_reload_markets(default_conf, mocker, caplog): caplog.set_level(logging.DEBUG) initial_markets = {'ETH/BTC': {}} - - def load_markets(*args, **kwargs): - exchange._api.markets = updated_markets + updated_markets = {'ETH/BTC': {}, "LTC/BTC": {}} api_mock = MagicMock() - api_mock.load_markets = load_markets - type(api_mock).markets = initial_markets + api_mock.load_markets = MagicMock(return_value=initial_markets) default_conf['exchange']['markets_refresh_interval'] = 10 exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance", mock_markets=False) exchange._load_async_markets = MagicMock() exchange._last_markets_refresh = arrow.utcnow().int_timestamp - updated_markets = {'ETH/BTC': {}, "LTC/BTC": {}} assert exchange.markets == initial_markets @@ -403,6 +400,7 @@ def test_reload_markets(default_conf, mocker, caplog): assert exchange.markets == initial_markets assert exchange._load_async_markets.call_count == 0 + api_mock.load_markets = MagicMock(return_value=updated_markets) # more than 10 minutes have passed, reload is executed exchange._last_markets_refresh = arrow.utcnow().int_timestamp - 15 * 60 exchange.reload_markets() @@ -429,7 +427,7 @@ def test_reload_markets_exception(default_conf, mocker, caplog): def test_validate_stake_currency(default_conf, stake_currency, mocker, caplog): default_conf['stake_currency'] = stake_currency api_mock = MagicMock() - type(api_mock).markets = PropertyMock(return_value={ + type(api_mock).load_markets = MagicMock(return_value={ 'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'}, 'XRP/ETH': {'quote': 'ETH'}, 'NEO/USDT': {'quote': 'USDT'}, }) @@ -443,7 +441,7 @@ def test_validate_stake_currency(default_conf, stake_currency, mocker, caplog): def test_validate_stake_currency_error(default_conf, mocker, caplog): default_conf['stake_currency'] = 'XRP' api_mock = MagicMock() - type(api_mock).markets = PropertyMock(return_value={ + type(api_mock).load_markets = MagicMock(return_value={ 'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'}, 'XRP/ETH': {'quote': 'ETH'}, 'NEO/USDT': {'quote': 'USDT'}, }) @@ -489,7 +487,7 @@ def test_get_pair_base_currency(default_conf, mocker, pair, expected): def test_validate_pairs(default_conf, mocker): # test exchange.validate_pairs directly api_mock = MagicMock() - type(api_mock).markets = PropertyMock(return_value={ + type(api_mock).load_markets = MagicMock(return_value={ 'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'}, 'XRP/BTC': {'quote': 'BTC'}, @@ -540,7 +538,7 @@ def test_validate_pairs_exception(default_conf, mocker, caplog): def test_validate_pairs_restricted(default_conf, mocker, caplog): api_mock = MagicMock() - type(api_mock).markets = PropertyMock(return_value={ + type(api_mock).load_markets = MagicMock(return_value={ 'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'}, 'XRP/BTC': {'quote': 'BTC', 'info': {'IsRestricted': True}}, 'NEO/BTC': {'quote': 'BTC', 'info': 'TestString'}, # info can also be a string ... @@ -558,7 +556,7 @@ def test_validate_pairs_restricted(default_conf, mocker, caplog): def test_validate_pairs_stakecompatibility(default_conf, mocker, caplog): api_mock = MagicMock() - type(api_mock).markets = PropertyMock(return_value={ + type(api_mock).load_markets = MagicMock(return_value={ 'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'}, 'XRP/BTC': {'quote': 'BTC'}, 'NEO/BTC': {'quote': 'BTC'}, 'HELLO-WORLD': {'quote': 'BTC'}, @@ -574,7 +572,7 @@ def test_validate_pairs_stakecompatibility(default_conf, mocker, caplog): def test_validate_pairs_stakecompatibility_downloaddata(default_conf, mocker, caplog): api_mock = MagicMock() default_conf['stake_currency'] = '' - type(api_mock).markets = PropertyMock(return_value={ + type(api_mock).load_markets = MagicMock(return_value={ 'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'}, 'XRP/BTC': {'quote': 'BTC'}, 'NEO/BTC': {'quote': 'BTC'}, 'HELLO-WORLD': {'quote': 'BTC'}, @@ -585,12 +583,13 @@ def test_validate_pairs_stakecompatibility_downloaddata(default_conf, mocker, ca mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') Exchange(default_conf) + assert type(api_mock).load_markets.call_count == 1 def test_validate_pairs_stakecompatibility_fail(default_conf, mocker, caplog): default_conf['exchange']['pair_whitelist'].append('HELLO-WORLD') api_mock = MagicMock() - type(api_mock).markets = PropertyMock(return_value={ + type(api_mock).load_markets = MagicMock(return_value={ 'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'}, 'XRP/BTC': {'quote': 'BTC'}, 'NEO/BTC': {'quote': 'BTC'}, 'HELLO-WORLD': {'quote': 'USDT'}, diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 6257a7e0b..06c08075b 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2100,6 +2100,7 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_buy_order_open def test_bot_loop_start_called_once(mocker, default_conf, caplog): ftbot = get_patched_freqtradebot(mocker, default_conf) + mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trade') patch_get_signal(ftbot) ftbot.strategy.bot_loop_start = MagicMock(side_effect=ValueError) ftbot.strategy.analyze = MagicMock() @@ -3810,6 +3811,8 @@ def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee open_order_id="123456" ) freqtrade = get_patched_freqtradebot(mocker, default_conf) + # Ticker rate cannot be found for this to work. + mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', side_effect=ExchangeError) # Amount is reduced by "fee" assert freqtrade.get_real_amount(trade, limit_buy_order) == amount - 0.004