We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Handle too many redirects thrown by http://exchangeratesapi.io/
## Problem Definition

The http://exchangeratesapi.io/ throws too many redirects error if you end up putting the EUR as the base symbol. Made an issue [there](https://github.com/exchangeratesapi/exchangeratesapi/issues/35) too.

## Task

We solve it in Rotkehlchen by handling the too many redirects error and falling back to a different API for querying fiat exchange rates.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `rotkehlchen/inquirer.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 import logging
4 import os
5 from json.decoder import JSONDecodeError
6 from typing import Dict, Iterable, Optional, cast
7 
8 import requests
9 from rotkehlchen.constants import (
10     CURRENCYCONVERTER_API_KEY,
11     FIAT_CURRENCIES,
12     S_BCHSV,
13     S_BQX,
14     S_DATACOIN,
15     S_IOTA,
16     S_NANO,
17     S_RAIBLOCKS,
18     S_RDN,
19     S_USD,
20 )
21 from rotkehlchen.errors import RemoteError
22 from rotkehlchen.fval import FVal
23 from rotkehlchen.logging import RotkehlchenLogsAdapter
24 from rotkehlchen.typing import (
25     Asset,
26     EthToken,
27     FiatAsset,
28     FilePath,
29     NonEthTokenBlockchainAsset,
30     Timestamp,
31 )
32 from rotkehlchen.utils import (
33     request_get_dict,
34     retry_calls,
35     rlk_jsondumps,
36     rlk_jsonloads_dict,
37     ts_now,
38     tsToDate,
39 )
40 
41 logger = logging.getLogger(__name__)
42 log = RotkehlchenLogsAdapter(logger)
43 
44 
45 def world_to_cryptocompare(asset: Asset, timestamp: Timestamp = None) -> Asset:
46     # Adjust some ETH tokens to how cryptocompare knows them
47     if asset == S_RDN:
48         # remove this if cryptocompare changes the symbol
49         asset = cast(EthToken, 'RDN*')
50     elif asset == S_DATACOIN:
51         asset = cast(NonEthTokenBlockchainAsset, 'DATA')
52     elif asset == S_IOTA:
53         asset = cast(NonEthTokenBlockchainAsset, 'IOT')
54     elif asset == S_BCHSV:
55         asset = cast(NonEthTokenBlockchainAsset, 'BSV')
56     elif asset == S_BQX:
57         asset = cast(EthToken, 'ETHOS')
58     elif asset == S_RAIBLOCKS:
59         return S_NANO
60 
61     return asset
62 
63 
64 def query_cryptocompare_for_fiat_price(asset: Asset) -> FVal:
65     log.debug('Get usd price from cryptocompare', asset=asset)
66     asset = world_to_cryptocompare(asset)
67     resp = retry_calls(
68         5,
69         'find_usd_price',
70         'requests.get',
71         requests.get,
72         u'https://min-api.cryptocompare.com/data/price?'
73         'fsym={}&tsyms=USD'.format(asset),
74     )
75 
76     if resp.status_code != 200:
77         raise RemoteError('Cant reach cryptocompare to get USD value of {}'.format(asset))
78 
79     resp = rlk_jsonloads_dict(resp.text)
80 
81     # If there is an error in the response skip this token
82     if 'USD' not in resp:
83         error_message = ''
84         if resp['Response'] == 'Error':
85             error_message = resp['Message']
86 
87         log.error(
88             'Cryptocompare usd price query failed',
89             asset=asset,
90             error=error_message,
91         )
92         return FVal(0)
93 
94     price = FVal(resp['USD'])
95     log.debug('Got usd price from cryptocompare', asset=asset, price=price)
96     return price
97 
98 
99 def _query_exchanges_rateapi(base: FiatAsset, quote: FiatAsset) -> Optional[FVal]:
100     log.debug(
101         'Querying api.exchangeratesapi.io fiat pair',
102         base_currency=base,
103         quote_currency=quote,
104     )
105     querystr = f'https://api.exchangeratesapi.io/latest?base={base}&symbols={quote}'
106     try:
107         resp = request_get_dict(querystr)
108         return FVal(resp['rates'][quote])
109     except (ValueError, RemoteError, KeyError):
110         log.error(
111             'Querying api.exchangeratesapi.io for fiat pair failed',
112             base_currency=base,
113             quote_currency=quote,
114         )
115         return None
116 
117 
118 def _query_currency_converterapi(base: FiatAsset, quote: FiatAsset) -> Optional[FVal]:
119     log.debug(
120         'Query free.currencyconverterapi.com fiat pair',
121         base_currency=base,
122         quote_currency=quote,
123     )
124     pair = f'{base}_{quote}'
125     querystr = (
126         f'https://free.currencyconverterapi.com/api/v6/convert?'
127         f'q={pair}&apiKey={CURRENCYCONVERTER_API_KEY}'
128     )
129     try:
130         resp = request_get_dict(querystr)
131         return FVal(resp['results'][pair]['val'])
132     except (ValueError, RemoteError, KeyError):
133         log.error(
134             'Querying free.currencyconverterapi.com fiat pair failed',
135             base_currency=base,
136             quote_currency=quote,
137         )
138         return None
139 
140 
141 class Inquirer(object):
142     def __init__(self, data_dir: FilePath, kraken=None):
143         self.kraken = kraken
144         self.session = requests.session()
145         self.data_directory = data_dir
146 
147         filename = os.path.join(self.data_directory, 'price_history_forex.json')
148         try:
149             with open(filename, 'r') as f:
150                 # we know price_history_forex contains a dict
151                 data = rlk_jsonloads_dict(f.read())
152                 self.cached_forex_data = data
153         except (OSError, IOError, JSONDecodeError):
154             self.cached_forex_data = dict()
155 
156     def query_kraken_for_price(
157             self,
158             asset: Asset,
159             asset_btc_price: FVal,
160     ) -> FVal:
161         if asset == 'BTC':
162             return self.kraken.usdprice['BTC']
163         return asset_btc_price * self.kraken.usdprice['BTC']
164 
165     def find_usd_price(
166             self,
167             asset: Asset,
168             asset_btc_price: Optional[FVal] = None,
169     ) -> FVal:
170         if self.kraken and self.kraken.first_connection_made and asset_btc_price is not None:
171             price = self.query_kraken_for_price(asset, asset_btc_price)
172             log.debug('Get usd price from kraken', asset=asset, price=price)
173             return price
174 
175         return query_cryptocompare_for_fiat_price(asset)
176 
177     def get_fiat_usd_exchange_rates(
178             self,
179             currencies: Optional[Iterable[FiatAsset]] = None,
180     ) -> Dict[FiatAsset, FVal]:
181         rates = {S_USD: FVal(1)}
182         if not currencies:
183             currencies = FIAT_CURRENCIES[1:]
184         for currency in currencies:
185             rates[currency] = self.query_fiat_pair(S_USD, currency)
186         return rates
187 
188     def query_historical_fiat_exchange_rates(
189             self,
190             from_currency: FiatAsset,
191             to_currency: FiatAsset,
192             timestamp: Timestamp,
193     ) -> Optional[FVal]:
194         date = tsToDate(timestamp, formatstr='%Y-%m-%d')
195         rate = self._get_cached_forex_data(date, from_currency, to_currency)
196         if rate:
197             return rate
198 
199         log.debug(
200             'Querying exchangeratesapi',
201             from_currency=from_currency,
202             to_currency=to_currency,
203             timestamp=timestamp,
204         )
205 
206         query_str = (
207             f'https://api.exchangeratesapi.io/{date}?'
208             f'base={from_currency}'
209         )
210         resp = retry_calls(
211             5,
212             'query_exchangeratesapi',
213             'requests.get',
214             requests.get,
215             query_str,
216         )
217 
218         if resp.status_code != 200:
219             return None
220 
221         try:
222             result = rlk_jsonloads_dict(resp.text)
223         except JSONDecodeError:
224             return None
225 
226         if 'rates' not in result or to_currency not in result['rates']:
227             return None
228 
229         if date not in self.cached_forex_data:
230             self.cached_forex_data[date] = {}
231 
232         if from_currency not in self.cached_forex_data[date]:
233             self.cached_forex_data[date][from_currency] = {}
234 
235         for key, value in result['rates'].items():
236             self.cached_forex_data[date][from_currency][key] = FVal(value)
237 
238         rate = FVal(result['rates'][to_currency])
239         log.debug('Exchangeratesapi query succesful', rate=rate)
240         return rate
241 
242     def _save_forex_rate(
243             self,
244             date: str,
245             from_currency: FiatAsset,
246             to_currency: FiatAsset,
247             price: FVal,
248     ):
249         if date not in self.cached_forex_data:
250             self.cached_forex_data[date] = {}
251 
252         if from_currency not in self.cached_forex_data[date]:
253             self.cached_forex_data[date][from_currency] = {}
254 
255         msg = 'Cached value should not already exist'
256         assert to_currency not in self.cached_forex_data[date][from_currency], msg
257         self.cached_forex_data[date][from_currency][to_currency] = price
258         self.save_historical_forex_data()
259 
260     def _get_cached_forex_data(
261             self,
262             date: str,
263             from_currency: FiatAsset,
264             to_currency: FiatAsset,
265     ) -> Optional[FVal]:
266         if date in self.cached_forex_data:
267             if from_currency in self.cached_forex_data[date]:
268                 rate = self.cached_forex_data[date][from_currency].get(to_currency)
269                 if rate:
270                     log.debug(
271                         'Got cached forex rate',
272                         from_currency=from_currency,
273                         to_currency=to_currency,
274                         rate=rate,
275                     )
276                 return rate
277         return None
278 
279     def save_historical_forex_data(self) -> None:
280         filename = os.path.join(self.data_directory, 'price_history_forex.json')
281         with open(filename, 'w') as outfile:
282             outfile.write(rlk_jsondumps(self.cached_forex_data))
283 
284     def query_fiat_pair(self, base: FiatAsset, quote: FiatAsset) -> FVal:
285         if base == quote:
286             return FVal(1.0)
287 
288         now = ts_now()
289         date = tsToDate(ts_now(), formatstr='%Y-%m-%d')
290         price = self._get_cached_forex_data(date, base, quote)
291         if price:
292             return price
293 
294         price = _query_exchanges_rateapi(base, quote)
295         if not price:
296             price = _query_currency_converterapi(base, quote)
297 
298         if not price:
299             # Search the cache for any price in the last month
300             for i in range(1, 31):
301                 now = Timestamp(now - Timestamp(86401))
302                 date = tsToDate(now, formatstr='%Y-%m-%d')
303                 price = self._get_cached_forex_data(date, base, quote)
304                 if price:
305                     log.debug(
306                         f'Could not query online apis for a fiat price. '
307                         f'Used cached value from {i} days ago.',
308                         base_currency=base,
309                         quote_currency=quote,
310                         price=price,
311                     )
312                     return price
313 
314             raise ValueError('Could not find a "{}" price for "{}"'.format(base, quote))
315 
316         self._save_forex_rate(date, base, quote, price)
317         return price
318
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/rotkehlchen/inquirer.py b/rotkehlchen/inquirer.py
--- a/rotkehlchen/inquirer.py
+++ b/rotkehlchen/inquirer.py
@@ -6,6 +6,7 @@
 from typing import Dict, Iterable, Optional, cast
 
 import requests
+
 from rotkehlchen.constants import (
     CURRENCYCONVERTER_API_KEY,
     FIAT_CURRENCIES,
@@ -106,7 +107,7 @@
     try:
         resp = request_get_dict(querystr)
         return FVal(resp['rates'][quote])
-    except (ValueError, RemoteError, KeyError):
+    except (ValueError, RemoteError, KeyError, requests.exceptions.TooManyRedirects):
         log.error(
             'Querying api.exchangeratesapi.io for fiat pair failed',
             base_currency=base,

{"golden_diff": "diff --git a/rotkehlchen/inquirer.py b/rotkehlchen/inquirer.py\n--- a/rotkehlchen/inquirer.py\n+++ b/rotkehlchen/inquirer.py\n@@ -6,6 +6,7 @@\n from typing import Dict, Iterable, Optional, cast\n \n import requests\n+\n from rotkehlchen.constants import (\n     CURRENCYCONVERTER_API_KEY,\n     FIAT_CURRENCIES,\n@@ -106,7 +107,7 @@\n     try:\n         resp = request_get_dict(querystr)\n         return FVal(resp['rates'][quote])\n-    except (ValueError, RemoteError, KeyError):\n+    except (ValueError, RemoteError, KeyError, requests.exceptions.TooManyRedirects):\n         log.error(\n             'Querying api.exchangeratesapi.io for fiat pair failed',\n             base_currency=base,\n", "issue": "Handle too many redirects thrown by http://exchangeratesapi.io/\n## Problem Definition\r\n\r\nThe http://exchangeratesapi.io/ throws too many redirects error if you end up putting the EUR as the base symbol. Made an issue [there](https://github.com/exchangeratesapi/exchangeratesapi/issues/35) too.\r\n\r\n## Task\r\n\r\nWe solve it in Rotkehlchen by handling the too many redirects error and falling back to a different API for querying fiat exchange rates.\n", "before_files": [{"content": "from __future__ import unicode_literals\n\nimport logging\nimport os\nfrom json.decoder import JSONDecodeError\nfrom typing import Dict, Iterable, Optional, cast\n\nimport requests\nfrom rotkehlchen.constants import (\n    CURRENCYCONVERTER_API_KEY,\n    FIAT_CURRENCIES,\n    S_BCHSV,\n    S_BQX,\n    S_DATACOIN,\n    S_IOTA,\n    S_NANO,\n    S_RAIBLOCKS,\n    S_RDN,\n    S_USD,\n)\nfrom rotkehlchen.errors import RemoteError\nfrom rotkehlchen.fval import FVal\nfrom rotkehlchen.logging import RotkehlchenLogsAdapter\nfrom rotkehlchen.typing import (\n    Asset,\n    EthToken,\n    FiatAsset,\n    FilePath,\n    NonEthTokenBlockchainAsset,\n    Timestamp,\n)\nfrom rotkehlchen.utils import (\n    request_get_dict,\n    retry_calls,\n    rlk_jsondumps,\n    rlk_jsonloads_dict,\n    ts_now,\n    tsToDate,\n)\n\nlogger = logging.getLogger(__name__)\nlog = RotkehlchenLogsAdapter(logger)\n\n\ndef world_to_cryptocompare(asset: Asset, timestamp: Timestamp = None) -> Asset:\n    # Adjust some ETH tokens to how cryptocompare knows them\n    if asset == S_RDN:\n        # remove this if cryptocompare changes the symbol\n        asset = cast(EthToken, 'RDN*')\n    elif asset == S_DATACOIN:\n        asset = cast(NonEthTokenBlockchainAsset, 'DATA')\n    elif asset == S_IOTA:\n        asset = cast(NonEthTokenBlockchainAsset, 'IOT')\n    elif asset == S_BCHSV:\n        asset = cast(NonEthTokenBlockchainAsset, 'BSV')\n    elif asset == S_BQX:\n        asset = cast(EthToken, 'ETHOS')\n    elif asset == S_RAIBLOCKS:\n        return S_NANO\n\n    return asset\n\n\ndef query_cryptocompare_for_fiat_price(asset: Asset) -> FVal:\n    log.debug('Get usd price from cryptocompare', asset=asset)\n    asset = world_to_cryptocompare(asset)\n    resp = retry_calls(\n        5,\n        'find_usd_price',\n        'requests.get',\n        requests.get,\n        u'https://min-api.cryptocompare.com/data/price?'\n        'fsym={}&tsyms=USD'.format(asset),\n    )\n\n    if resp.status_code != 200:\n        raise RemoteError('Cant reach cryptocompare to get USD value of {}'.format(asset))\n\n    resp = rlk_jsonloads_dict(resp.text)\n\n    # If there is an error in the response skip this token\n    if 'USD' not in resp:\n        error_message = ''\n        if resp['Response'] == 'Error':\n            error_message = resp['Message']\n\n        log.error(\n            'Cryptocompare usd price query failed',\n            asset=asset,\n            error=error_message,\n        )\n        return FVal(0)\n\n    price = FVal(resp['USD'])\n    log.debug('Got usd price from cryptocompare', asset=asset, price=price)\n    return price\n\n\ndef _query_exchanges_rateapi(base: FiatAsset, quote: FiatAsset) -> Optional[FVal]:\n    log.debug(\n        'Querying api.exchangeratesapi.io fiat pair',\n        base_currency=base,\n        quote_currency=quote,\n    )\n    querystr = f'https://api.exchangeratesapi.io/latest?base={base}&symbols={quote}'\n    try:\n        resp = request_get_dict(querystr)\n        return FVal(resp['rates'][quote])\n    except (ValueError, RemoteError, KeyError):\n        log.error(\n            'Querying api.exchangeratesapi.io for fiat pair failed',\n            base_currency=base,\n            quote_currency=quote,\n        )\n        return None\n\n\ndef _query_currency_converterapi(base: FiatAsset, quote: FiatAsset) -> Optional[FVal]:\n    log.debug(\n        'Query free.currencyconverterapi.com fiat pair',\n        base_currency=base,\n        quote_currency=quote,\n    )\n    pair = f'{base}_{quote}'\n    querystr = (\n        f'https://free.currencyconverterapi.com/api/v6/convert?'\n        f'q={pair}&apiKey={CURRENCYCONVERTER_API_KEY}'\n    )\n    try:\n        resp = request_get_dict(querystr)\n        return FVal(resp['results'][pair]['val'])\n    except (ValueError, RemoteError, KeyError):\n        log.error(\n            'Querying free.currencyconverterapi.com fiat pair failed',\n            base_currency=base,\n            quote_currency=quote,\n        )\n        return None\n\n\nclass Inquirer(object):\n    def __init__(self, data_dir: FilePath, kraken=None):\n        self.kraken = kraken\n        self.session = requests.session()\n        self.data_directory = data_dir\n\n        filename = os.path.join(self.data_directory, 'price_history_forex.json')\n        try:\n            with open(filename, 'r') as f:\n                # we know price_history_forex contains a dict\n                data = rlk_jsonloads_dict(f.read())\n                self.cached_forex_data = data\n        except (OSError, IOError, JSONDecodeError):\n            self.cached_forex_data = dict()\n\n    def query_kraken_for_price(\n            self,\n            asset: Asset,\n            asset_btc_price: FVal,\n    ) -> FVal:\n        if asset == 'BTC':\n            return self.kraken.usdprice['BTC']\n        return asset_btc_price * self.kraken.usdprice['BTC']\n\n    def find_usd_price(\n            self,\n            asset: Asset,\n            asset_btc_price: Optional[FVal] = None,\n    ) -> FVal:\n        if self.kraken and self.kraken.first_connection_made and asset_btc_price is not None:\n            price = self.query_kraken_for_price(asset, asset_btc_price)\n            log.debug('Get usd price from kraken', asset=asset, price=price)\n            return price\n\n        return query_cryptocompare_for_fiat_price(asset)\n\n    def get_fiat_usd_exchange_rates(\n            self,\n            currencies: Optional[Iterable[FiatAsset]] = None,\n    ) -> Dict[FiatAsset, FVal]:\n        rates = {S_USD: FVal(1)}\n        if not currencies:\n            currencies = FIAT_CURRENCIES[1:]\n        for currency in currencies:\n            rates[currency] = self.query_fiat_pair(S_USD, currency)\n        return rates\n\n    def query_historical_fiat_exchange_rates(\n            self,\n            from_currency: FiatAsset,\n            to_currency: FiatAsset,\n            timestamp: Timestamp,\n    ) -> Optional[FVal]:\n        date = tsToDate(timestamp, formatstr='%Y-%m-%d')\n        rate = self._get_cached_forex_data(date, from_currency, to_currency)\n        if rate:\n            return rate\n\n        log.debug(\n            'Querying exchangeratesapi',\n            from_currency=from_currency,\n            to_currency=to_currency,\n            timestamp=timestamp,\n        )\n\n        query_str = (\n            f'https://api.exchangeratesapi.io/{date}?'\n            f'base={from_currency}'\n        )\n        resp = retry_calls(\n            5,\n            'query_exchangeratesapi',\n            'requests.get',\n            requests.get,\n            query_str,\n        )\n\n        if resp.status_code != 200:\n            return None\n\n        try:\n            result = rlk_jsonloads_dict(resp.text)\n        except JSONDecodeError:\n            return None\n\n        if 'rates' not in result or to_currency not in result['rates']:\n            return None\n\n        if date not in self.cached_forex_data:\n            self.cached_forex_data[date] = {}\n\n        if from_currency not in self.cached_forex_data[date]:\n            self.cached_forex_data[date][from_currency] = {}\n\n        for key, value in result['rates'].items():\n            self.cached_forex_data[date][from_currency][key] = FVal(value)\n\n        rate = FVal(result['rates'][to_currency])\n        log.debug('Exchangeratesapi query succesful', rate=rate)\n        return rate\n\n    def _save_forex_rate(\n            self,\n            date: str,\n            from_currency: FiatAsset,\n            to_currency: FiatAsset,\n            price: FVal,\n    ):\n        if date not in self.cached_forex_data:\n            self.cached_forex_data[date] = {}\n\n        if from_currency not in self.cached_forex_data[date]:\n            self.cached_forex_data[date][from_currency] = {}\n\n        msg = 'Cached value should not already exist'\n        assert to_currency not in self.cached_forex_data[date][from_currency], msg\n        self.cached_forex_data[date][from_currency][to_currency] = price\n        self.save_historical_forex_data()\n\n    def _get_cached_forex_data(\n            self,\n            date: str,\n            from_currency: FiatAsset,\n            to_currency: FiatAsset,\n    ) -> Optional[FVal]:\n        if date in self.cached_forex_data:\n            if from_currency in self.cached_forex_data[date]:\n                rate = self.cached_forex_data[date][from_currency].get(to_currency)\n                if rate:\n                    log.debug(\n                        'Got cached forex rate',\n                        from_currency=from_currency,\n                        to_currency=to_currency,\n                        rate=rate,\n                    )\n                return rate\n        return None\n\n    def save_historical_forex_data(self) -> None:\n        filename = os.path.join(self.data_directory, 'price_history_forex.json')\n        with open(filename, 'w') as outfile:\n            outfile.write(rlk_jsondumps(self.cached_forex_data))\n\n    def query_fiat_pair(self, base: FiatAsset, quote: FiatAsset) -> FVal:\n        if base == quote:\n            return FVal(1.0)\n\n        now = ts_now()\n        date = tsToDate(ts_now(), formatstr='%Y-%m-%d')\n        price = self._get_cached_forex_data(date, base, quote)\n        if price:\n            return price\n\n        price = _query_exchanges_rateapi(base, quote)\n        if not price:\n            price = _query_currency_converterapi(base, quote)\n\n        if not price:\n            # Search the cache for any price in the last month\n            for i in range(1, 31):\n                now = Timestamp(now - Timestamp(86401))\n                date = tsToDate(now, formatstr='%Y-%m-%d')\n                price = self._get_cached_forex_data(date, base, quote)\n                if price:\n                    log.debug(\n                        f'Could not query online apis for a fiat price. '\n                        f'Used cached value from {i} days ago.',\n                        base_currency=base,\n                        quote_currency=quote,\n                        price=price,\n                    )\n                    return price\n\n            raise ValueError('Could not find a \"{}\" price for \"{}\"'.format(base, quote))\n\n        self._save_forex_rate(date, base, quote, price)\n        return price\n", "path": "rotkehlchen/inquirer.py"}], "after_files": [{"content": "from __future__ import unicode_literals\n\nimport logging\nimport os\nfrom json.decoder import JSONDecodeError\nfrom typing import Dict, Iterable, Optional, cast\n\nimport requests\n\nfrom rotkehlchen.constants import (\n    CURRENCYCONVERTER_API_KEY,\n    FIAT_CURRENCIES,\n    S_BCHSV,\n    S_BQX,\n    S_DATACOIN,\n    S_IOTA,\n    S_NANO,\n    S_RAIBLOCKS,\n    S_RDN,\n    S_USD,\n)\nfrom rotkehlchen.errors import RemoteError\nfrom rotkehlchen.fval import FVal\nfrom rotkehlchen.logging import RotkehlchenLogsAdapter\nfrom rotkehlchen.typing import (\n    Asset,\n    EthToken,\n    FiatAsset,\n    FilePath,\n    NonEthTokenBlockchainAsset,\n    Timestamp,\n)\nfrom rotkehlchen.utils import (\n    request_get_dict,\n    retry_calls,\n    rlk_jsondumps,\n    rlk_jsonloads_dict,\n    ts_now,\n    tsToDate,\n)\n\nlogger = logging.getLogger(__name__)\nlog = RotkehlchenLogsAdapter(logger)\n\n\ndef world_to_cryptocompare(asset: Asset, timestamp: Timestamp = None) -> Asset:\n    # Adjust some ETH tokens to how cryptocompare knows them\n    if asset == S_RDN:\n        # remove this if cryptocompare changes the symbol\n        asset = cast(EthToken, 'RDN*')\n    elif asset == S_DATACOIN:\n        asset = cast(NonEthTokenBlockchainAsset, 'DATA')\n    elif asset == S_IOTA:\n        asset = cast(NonEthTokenBlockchainAsset, 'IOT')\n    elif asset == S_BCHSV:\n        asset = cast(NonEthTokenBlockchainAsset, 'BSV')\n    elif asset == S_BQX:\n        asset = cast(EthToken, 'ETHOS')\n    elif asset == S_RAIBLOCKS:\n        return S_NANO\n\n    return asset\n\n\ndef query_cryptocompare_for_fiat_price(asset: Asset) -> FVal:\n    log.debug('Get usd price from cryptocompare', asset=asset)\n    asset = world_to_cryptocompare(asset)\n    resp = retry_calls(\n        5,\n        'find_usd_price',\n        'requests.get',\n        requests.get,\n        u'https://min-api.cryptocompare.com/data/price?'\n        'fsym={}&tsyms=USD'.format(asset),\n    )\n\n    if resp.status_code != 200:\n        raise RemoteError('Cant reach cryptocompare to get USD value of {}'.format(asset))\n\n    resp = rlk_jsonloads_dict(resp.text)\n\n    # If there is an error in the response skip this token\n    if 'USD' not in resp:\n        error_message = ''\n        if resp['Response'] == 'Error':\n            error_message = resp['Message']\n\n        log.error(\n            'Cryptocompare usd price query failed',\n            asset=asset,\n            error=error_message,\n        )\n        return FVal(0)\n\n    price = FVal(resp['USD'])\n    log.debug('Got usd price from cryptocompare', asset=asset, price=price)\n    return price\n\n\ndef _query_exchanges_rateapi(base: FiatAsset, quote: FiatAsset) -> Optional[FVal]:\n    log.debug(\n        'Querying api.exchangeratesapi.io fiat pair',\n        base_currency=base,\n        quote_currency=quote,\n    )\n    querystr = f'https://api.exchangeratesapi.io/latest?base={base}&symbols={quote}'\n    try:\n        resp = request_get_dict(querystr)\n        return FVal(resp['rates'][quote])\n    except (ValueError, RemoteError, KeyError, requests.exceptions.TooManyRedirects):\n        log.error(\n            'Querying api.exchangeratesapi.io for fiat pair failed',\n            base_currency=base,\n            quote_currency=quote,\n        )\n        return None\n\n\ndef _query_currency_converterapi(base: FiatAsset, quote: FiatAsset) -> Optional[FVal]:\n    log.debug(\n        'Query free.currencyconverterapi.com fiat pair',\n        base_currency=base,\n        quote_currency=quote,\n    )\n    pair = f'{base}_{quote}'\n    querystr = (\n        f'https://free.currencyconverterapi.com/api/v6/convert?'\n        f'q={pair}&apiKey={CURRENCYCONVERTER_API_KEY}'\n    )\n    try:\n        resp = request_get_dict(querystr)\n        return FVal(resp['results'][pair]['val'])\n    except (ValueError, RemoteError, KeyError):\n        log.error(\n            'Querying free.currencyconverterapi.com fiat pair failed',\n            base_currency=base,\n            quote_currency=quote,\n        )\n        return None\n\n\nclass Inquirer(object):\n    def __init__(self, data_dir: FilePath, kraken=None):\n        self.kraken = kraken\n        self.session = requests.session()\n        self.data_directory = data_dir\n\n        filename = os.path.join(self.data_directory, 'price_history_forex.json')\n        try:\n            with open(filename, 'r') as f:\n                # we know price_history_forex contains a dict\n                data = rlk_jsonloads_dict(f.read())\n                self.cached_forex_data = data\n        except (OSError, IOError, JSONDecodeError):\n            self.cached_forex_data = dict()\n\n    def query_kraken_for_price(\n            self,\n            asset: Asset,\n            asset_btc_price: FVal,\n    ) -> FVal:\n        if asset == 'BTC':\n            return self.kraken.usdprice['BTC']\n        return asset_btc_price * self.kraken.usdprice['BTC']\n\n    def find_usd_price(\n            self,\n            asset: Asset,\n            asset_btc_price: Optional[FVal] = None,\n    ) -> FVal:\n        if self.kraken and self.kraken.first_connection_made and asset_btc_price is not None:\n            price = self.query_kraken_for_price(asset, asset_btc_price)\n            log.debug('Get usd price from kraken', asset=asset, price=price)\n            return price\n\n        return query_cryptocompare_for_fiat_price(asset)\n\n    def get_fiat_usd_exchange_rates(\n            self,\n            currencies: Optional[Iterable[FiatAsset]] = None,\n    ) -> Dict[FiatAsset, FVal]:\n        rates = {S_USD: FVal(1)}\n        if not currencies:\n            currencies = FIAT_CURRENCIES[1:]\n        for currency in currencies:\n            rates[currency] = self.query_fiat_pair(S_USD, currency)\n        return rates\n\n    def query_historical_fiat_exchange_rates(\n            self,\n            from_currency: FiatAsset,\n            to_currency: FiatAsset,\n            timestamp: Timestamp,\n    ) -> Optional[FVal]:\n        date = tsToDate(timestamp, formatstr='%Y-%m-%d')\n        rate = self._get_cached_forex_data(date, from_currency, to_currency)\n        if rate:\n            return rate\n\n        log.debug(\n            'Querying exchangeratesapi',\n            from_currency=from_currency,\n            to_currency=to_currency,\n            timestamp=timestamp,\n        )\n\n        query_str = (\n            f'https://api.exchangeratesapi.io/{date}?'\n            f'base={from_currency}'\n        )\n        resp = retry_calls(\n            5,\n            'query_exchangeratesapi',\n            'requests.get',\n            requests.get,\n            query_str,\n        )\n\n        if resp.status_code != 200:\n            return None\n\n        try:\n            result = rlk_jsonloads_dict(resp.text)\n        except JSONDecodeError:\n            return None\n\n        if 'rates' not in result or to_currency not in result['rates']:\n            return None\n\n        if date not in self.cached_forex_data:\n            self.cached_forex_data[date] = {}\n\n        if from_currency not in self.cached_forex_data[date]:\n            self.cached_forex_data[date][from_currency] = {}\n\n        for key, value in result['rates'].items():\n            self.cached_forex_data[date][from_currency][key] = FVal(value)\n\n        rate = FVal(result['rates'][to_currency])\n        log.debug('Exchangeratesapi query succesful', rate=rate)\n        return rate\n\n    def _save_forex_rate(\n            self,\n            date: str,\n            from_currency: FiatAsset,\n            to_currency: FiatAsset,\n            price: FVal,\n    ):\n        if date not in self.cached_forex_data:\n            self.cached_forex_data[date] = {}\n\n        if from_currency not in self.cached_forex_data[date]:\n            self.cached_forex_data[date][from_currency] = {}\n\n        msg = 'Cached value should not already exist'\n        assert to_currency not in self.cached_forex_data[date][from_currency], msg\n        self.cached_forex_data[date][from_currency][to_currency] = price\n        self.save_historical_forex_data()\n\n    def _get_cached_forex_data(\n            self,\n            date: str,\n            from_currency: FiatAsset,\n            to_currency: FiatAsset,\n    ) -> Optional[FVal]:\n        if date in self.cached_forex_data:\n            if from_currency in self.cached_forex_data[date]:\n                rate = self.cached_forex_data[date][from_currency].get(to_currency)\n                if rate:\n                    log.debug(\n                        'Got cached forex rate',\n                        from_currency=from_currency,\n                        to_currency=to_currency,\n                        rate=rate,\n                    )\n                return rate\n        return None\n\n    def save_historical_forex_data(self) -> None:\n        filename = os.path.join(self.data_directory, 'price_history_forex.json')\n        with open(filename, 'w') as outfile:\n            outfile.write(rlk_jsondumps(self.cached_forex_data))\n\n    def query_fiat_pair(self, base: FiatAsset, quote: FiatAsset) -> FVal:\n        if base == quote:\n            return FVal(1.0)\n\n        now = ts_now()\n        date = tsToDate(ts_now(), formatstr='%Y-%m-%d')\n        price = self._get_cached_forex_data(date, base, quote)\n        if price:\n            return price\n\n        price = _query_exchanges_rateapi(base, quote)\n        if not price:\n            price = _query_currency_converterapi(base, quote)\n\n        if not price:\n            # Search the cache for any price in the last month\n            for i in range(1, 31):\n                now = Timestamp(now - Timestamp(86401))\n                date = tsToDate(now, formatstr='%Y-%m-%d')\n                price = self._get_cached_forex_data(date, base, quote)\n                if price:\n                    log.debug(\n                        f'Could not query online apis for a fiat price. '\n                        f'Used cached value from {i} days ago.',\n                        base_currency=base,\n                        quote_currency=quote,\n                        price=price,\n                    )\n                    return price\n\n            raise ValueError('Could not find a \"{}\" price for \"{}\"'.format(base, quote))\n\n        self._save_forex_rate(date, base, quote, price)\n        return price\n", "path": "rotkehlchen/inquirer.py"}]}