Repository: rotki/rotki

Issue:
Find a backup API for fiat exchange rate queries
## Problem Definition

We used to have https://free.currencyconverterapi.com/ as a backup API for current fiat exchange rates but it has shut down and we remove it here: https://github.com/rotki/rotki/pull/1035

Now we are only left with api.exchangeratesapi.io which is nice but if that fails too then we have a problem, so we should find a backup API and add it to rotki.


Discussion:

Resolving patch:
diff --git a/rotkehlchen/constants/misc.py b/rotkehlchen/constants/misc.py
--- a/rotkehlchen/constants/misc.py
+++ b/rotkehlchen/constants/misc.py
@@ -14,6 +14,8 @@
 EV_DEFI = EventType('defi_event')
 EV_LEDGER_ACTION = EventType('ledger_action')
 
+CURRENCYCONVERTER_API_KEY = '7ad371210f296db27c19'
+
 ZERO = FVal(0)
 ONE = FVal(1)
 
diff --git a/rotkehlchen/inquirer.py b/rotkehlchen/inquirer.py
--- a/rotkehlchen/inquirer.py
+++ b/rotkehlchen/inquirer.py
@@ -10,7 +10,7 @@
 
 from rotkehlchen.assets.asset import Asset
 from rotkehlchen.chain.ethereum.defi.price import handle_defi_price_query
-from rotkehlchen.constants import ZERO
+from rotkehlchen.constants import CURRENCYCONVERTER_API_KEY, ZERO
 from rotkehlchen.constants.assets import (
     A_ALINK,
     A_BTC,
@@ -180,6 +180,31 @@ def _query_exchanges_rateapi(base: Asset, quote: Asset) -> Optional[Price]:
         return None
 
 
+def _query_currency_converterapi(base: Asset, quote: Asset) -> Optional[Price]:
+    assert base.is_fiat(), 'fiat currency should have been provided'
+    assert quote.is_fiat(), 'fiat currency should have been provided'
+    log.debug(
+        'Query free.currencyconverterapi.com fiat pair',
+        base_currency=base.identifier,
+        quote_currency=quote.identifier,
+    )
+    pair = f'{base.identifier}_{quote.identifier}'
+    querystr = (
+        f'https://free.currconv.com/api/v7/convert?'
+        f'q={pair}&compact=ultra&apiKey={CURRENCYCONVERTER_API_KEY}'
+    )
+    try:
+        resp = request_get_dict(querystr)
+        return Price(FVal(resp[pair]))
+    except (ValueError, RemoteError, KeyError, UnableToDecryptRemoteData):
+        log.error(
+            'Querying free.currencyconverterapi.com fiat pair failed',
+            base_currency=base.identifier,
+            quote_currency=quote.identifier,
+        )
+        return None
+
+
 class CachedPriceEntry(NamedTuple):
     price: Price
     time: Timestamp
@@ -499,7 +524,8 @@ def _query_fiat_pair(base: Asset, quote: Asset) -> Price:
             return price
 
         price = _query_exchanges_rateapi(base, quote)
-        # TODO: Find another backup API for fiat exchange rates
+        if price is None:
+            price = _query_currency_converterapi(base, quote)
 
         if price is None:
             # Search the cache for any price in the last month


Test patch:
diff --git a/rotkehlchen/tests/unit/test_assets.py b/rotkehlchen/tests/unit/test_assets.py
--- a/rotkehlchen/tests/unit/test_assets.py
+++ b/rotkehlchen/tests/unit/test_assets.py
@@ -244,7 +244,7 @@ def test_coingecko_identifiers_are_reachable(data_dir):
 def test_assets_json_meta():
     """Test that all_assets.json md5 matches and that if md5 changes since last
     time then version is also bumped"""
-    last_meta = {'md5': '271b43b7fcb2ae995260a869adcf8e48', 'version': 71}
+    last_meta = {'md5': 'bd4e22bc1bd65aef857bdbf9137b37d9', 'version': 71}
     data_dir = Path(__file__).resolve().parent.parent.parent / 'data'
     data_md5 = file_md5(data_dir / 'all_assets.json')
 
diff --git a/rotkehlchen/tests/unit/test_inquirer.py b/rotkehlchen/tests/unit/test_inquirer.py
--- a/rotkehlchen/tests/unit/test_inquirer.py
+++ b/rotkehlchen/tests/unit/test_inquirer.py
@@ -15,6 +15,7 @@
     CURRENT_PRICE_CACHE_SECS,
     DEFAULT_CURRENT_PRICE_ORACLES_ORDER,
     CurrentPriceOracle,
+    _query_currency_converterapi,
     _query_exchanges_rateapi,
 )
 from rotkehlchen.tests.utils.constants import A_CNY, A_EUR, A_GBP, A_JPY
@@ -25,13 +26,14 @@
 
 @pytest.mark.parametrize('use_clean_caching_directory', [True])
 def test_query_realtime_price_apis(inquirer):
+    result = _query_currency_converterapi(A_USD, A_EUR)
+    assert result and isinstance(result, FVal)
     result = _query_exchanges_rateapi(A_USD, A_GBP)
     assert result and isinstance(result, FVal)
     result = inquirer.query_historical_fiat_exchange_rates(A_USD, A_CNY, 1411603200)
     assert result == FVal('6.1371932033')
 
 
-@pytest.mark.skip('The backup FIAT exchange rate API shut down. Unskip if new is found')
 @pytest.mark.parametrize('use_clean_caching_directory', [True])
 @pytest.mark.parametrize('should_mock_current_price_queries', [False])
 def test_switching_to_backup_api(inquirer):