From c2f35ce416cf8d6e6c5806dd1a3e624a124d3843 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 13 Apr 2021 20:09:22 +0200 Subject: [PATCH] /balance should use cached tickers when possible --- freqtrade/exchange/exchange.py | 18 ++++++++++++++++-- freqtrade/rpc/rpc.py | 2 +- tests/exchange/test_exchange.py | 8 ++++++++ tests/rpc/test_rpc.py | 2 ++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index e52e0e0d0..3627a07e4 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -14,6 +14,7 @@ from typing import Any, Dict, List, Optional, Tuple import arrow import ccxt import ccxt.async_support as ccxt_async +from cachetools import TTLCache from ccxt.base.decimal_to_precision import (ROUND_DOWN, ROUND_UP, TICK_SIZE, TRUNCATE, decimal_to_precision) from pandas import DataFrame @@ -84,6 +85,9 @@ class Exchange: # Timestamp of last markets refresh self._last_markets_refresh: int = 0 + # Cache for 10 minutes ... + self._fetch_tickers_cache = TTLCache(maxsize=1, ttl=60 * 10) + # Holds candles self._klines: Dict[Tuple[str, str], DataFrame] = {} @@ -693,9 +697,19 @@ class Exchange: raise OperationalException(e) from e @retrier - def get_tickers(self) -> Dict: + def get_tickers(self, cached: bool = False) -> Dict: + """ + :param cached: Allow cached result + :return: fetch_tickers result + """ + if cached: + tickers = self._fetch_tickers_cache.get('fetch_tickers') + if tickers: + return tickers try: - return self._api.fetch_tickers() + tickers = self._api.fetch_tickers() + self._fetch_tickers_cache['fetch_tickers'] = tickers + return tickers except ccxt.NotSupported as e: raise OperationalException( f'Exchange {self._api.name} does not support fetching tickers in batch. ' diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 59758a573..88f8b36db 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -442,7 +442,7 @@ class RPC: output = [] total = 0.0 try: - tickers = self._freqtrade.exchange.get_tickers() + tickers = self._freqtrade.exchange.get_tickers(cached=True) except (ExchangeError): raise RPCException('Error getting current tickers.') diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index db67d038c..76095be2d 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1319,6 +1319,14 @@ def test_get_tickers(default_conf, mocker, exchange_name): assert tickers['ETH/BTC']['ask'] == 1 assert tickers['BCH/BTC']['bid'] == 0.6 assert tickers['BCH/BTC']['ask'] == 0.5 + assert api_mock.fetch_tickers.call_count == 1 + + api_mock.fetch_tickers.reset_mock() + + # Cached ticker should not call api again + tickers2 = exchange.get_tickers(cached=True) + assert tickers2 == tickers + assert api_mock.fetch_tickers.call_count == 0 ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, "get_tickers", "fetch_tickers") diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index a97f6b65e..199845545 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -569,6 +569,8 @@ def test_rpc_balance_handle(default_conf, mocker, tickers): result = rpc._rpc_balance(default_conf['stake_currency'], default_conf['fiat_display_currency']) assert prec_satoshi(result['total'], 12.309096315) assert prec_satoshi(result['value'], 184636.44472997) + assert tickers.call_count == 1 + assert tickers.call_args.kwargs['cached'] is True assert 'USD' == result['symbol'] assert result['currencies'] == [ {'currency': 'BTC',