diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 3c67db71f..5e768518b 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -18,6 +18,8 @@ _API: ccxt.Exchange = None _CONF: Dict = {} API_RETRY_COUNT = 4 +_CACHED_TICKER: Dict[str, Any] = {} + # Holds all open sell orders for dry_run _DRY_RUN_OPEN_ORDERS: Dict[str, Any] = {} @@ -264,17 +266,29 @@ def get_tickers() -> Dict: raise OperationalException(e) -# TODO: remove refresh argument, keeping it to keep track of where it was intended to be used @retrier def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict: - try: - return _API.fetch_ticker(pair) - except (ccxt.NetworkError, ccxt.ExchangeError) as e: - raise TemporaryError( - 'Could not load ticker history due to {}. Message: {}'.format( - e.__class__.__name__, e)) - except ccxt.BaseError as e: - raise OperationalException(e) + global _CACHED_TICKER + if refresh or pair not in _CACHED_TICKER.keys(): + try: + data = _API.fetch_ticker(pair) + try: + _CACHED_TICKER[pair] = { + 'bid': float(data['bid']), + 'ask': float(data['ask']), + } + except KeyError as e: + logger.debug("Could not cache ticker data for %s", pair) + return data + except (ccxt.NetworkError, ccxt.ExchangeError) as e: + raise TemporaryError( + 'Could not load ticker history due to {}. Message: {}'.format( + e.__class__.__name__, e)) + except ccxt.BaseError as e: + raise OperationalException(e) + else: + logger.info("returning cached ticker-data for %s", pair) + return _CACHED_TICKER[pair] @retrier diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index 56812c75e..97a723929 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -310,9 +310,19 @@ def test_get_ticker(default_conf, mocker): # if not fetching a new result we should get the cached ticker ticker = get_ticker(pair='ETH/BTC') + assert api_mock.fetch_ticker.call_count == 1 assert ticker['bid'] == 0.5 assert ticker['ask'] == 1 + assert 'ETH/BTC' in exchange._CACHED_TICKER + assert exchange._CACHED_TICKER['ETH/BTC']['bid'] == 0.5 + assert exchange._CACHED_TICKER['ETH/BTC']['ask'] == 1 + + # Test caching + api_mock.fetch_ticker = MagicMock() + get_ticker(pair='ETH/BTC', refresh=False) + assert api_mock.fetch_ticker.call_count == 0 + with pytest.raises(TemporaryError): # test retrier api_mock.fetch_ticker = MagicMock(side_effect=ccxt.NetworkError) mocker.patch('freqtrade.exchange._API', api_mock) @@ -323,6 +333,10 @@ def test_get_ticker(default_conf, mocker): mocker.patch('freqtrade.exchange._API', api_mock) get_ticker(pair='ETH/BTC', refresh=True) + api_mock.fetch_ticker = MagicMock(return_value={}) + mocker.patch('freqtrade.exchange._API', api_mock) + get_ticker(pair='ETH/BTC', refresh=True) + def make_fetch_ohlcv_mock(data): def fetch_ohlcv_mock(pair, timeframe, since):