Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better fix for Yahoo API error #31

Merged
merged 1 commit into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 34 additions & 15 deletions src/hdx/location/currency.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,39 @@ def _get_primary_rates_data(
except (DownloadError, KeyError):
return None

@classmethod
def _get_adjclose(cls, indicators: Dict) -> Optional[float]:
"""
Get the adjusted close fx rate from the indicators dictionary returned
from the Yahoo API.

Args:
indicators (Dict): Indicators dictionary from Yahoo API

Returns:
Optional[float]: Adjusted close fx rate or None
"""
adjclose = indicators["adjclose"][0].get("adjclose")
if adjclose is None:
return None
adjclose = adjclose[0]
# compare with high and low to reveal errors from Yahoo feed
quote = indicators["quote"][0]
high = quote.get("high")
low = quote.get("low")
if high and low:
high = high[0]
low = low[0]
if adjclose > high:
diff = adjclose / high
if diff > 1.1:
adjclose = low + (high - low) / 2
elif adjclose < low:
diff = low / adjclose
if diff > 1.1:
adjclose = low + (high - low) / 2
return adjclose

@classmethod
def _get_primary_rate(
cls, currency: str, timestamp: Optional[int] = None
Expand Down Expand Up @@ -201,21 +234,7 @@ def _get_primary_rate(
if not data:
return None
if get_close:
indicators = data["indicators"]
adjclose = indicators["adjclose"][0].get("adjclose")
if adjclose is None:
return None
adjclose = adjclose[0]
# compare with high and low to reveal errors from Yahoo feed
quote = indicators["quote"][0]
high = quote.get("high")
low = quote.get("low")
if high and low:
high = high[0]
low = low[0]
if adjclose > high or adjclose < low:
adjclose = low + (high - low) / 2
return adjclose
return cls._get_adjclose(data["indicators"])
return data["meta"]["regularMarketPrice"]

@classmethod
Expand Down
74 changes: 73 additions & 1 deletion tests/hdx/location/test_currency.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def test_get_historic_value_in_usd(
# 0.761817697025102 + (0.776276975624903 - 0.761817697025102) * (1582156800-1580428800) / (1582934400 - 1580428800)
assert Currency.get_historic_rate("gbp", date) == 0.7717896133008268

def test_broken_rate(self, retrievers, secondary_historic_url):
def test_broken_rates(self, retrievers, secondary_historic_url):
Currency._no_historic = False
Currency.setup(secondary_historic_url=secondary_historic_url)
# Without the checking against high and low returned by Yahoo API, this
Expand All @@ -257,3 +257,75 @@ def test_broken_rate(self, retrievers, secondary_historic_url):
Currency.get_historic_rate("NGN", parse_date("2017-02-15"))
== 314.5
)
# Without the checking against high and low returned by Yahoo API, this
# returned 0.10000000149011612
assert (
Currency.get_historic_rate("YER", parse_date("2016-09-15"))
== 249.7249984741211
)
# Since the adjclose is not too different from the low and high,
# despite being outside their range, we use adjclose
assert (
Currency.get_historic_rate("XAF", parse_date("2022-04-14"))
== 605.5509643554688
)
# Since the adjclose is not too different from the low and high,
# despite being outside their range, we use adjclose
assert (
Currency.get_historic_rate("XAF", parse_date("2022-04-15"))
== 601.632568359375
)

def test_get_adjclose(self):
indicators = {
"adjclose": [{"adjclose": [3.140000104904175]}],
"quote": [
{
"close": [3.140000104904175],
"high": [315.0],
"low": [314.0],
"open": [315.0],
"volume": [0],
}
],
}
assert Currency._get_adjclose(indicators) == 314.5
indicators = {
"adjclose": [{"adjclose": [605.5509643554688]}],
"quote": [
{
"close": [605.5509643554688],
"high": [602.6080932617188],
"low": [601.632568359375],
"open": [602.6080932617188],
"volume": [0],
}
],
}
assert Currency._get_adjclose(indicators) == 605.5509643554688
indicators = {
"adjclose": [{"adjclose": [601.632568359375]}],
"quote": [
{
"close": [601.632568359375],
"high": [606.8197631835938],
"low": [606.8197631835938],
"open": [606.8197631835938],
"volume": [0],
}
],
}
assert Currency._get_adjclose(indicators) == 601.632568359375
indicators = {
"adjclose": [{"adjclose": [314.0000104904175]}],
"quote": [
{
"close": [314.0000104904175],
"high": [3.150],
"low": [3.140],
"open": [3.150],
"volume": [0],
}
],
}
assert Currency._get_adjclose(indicators) == 3.145