Skip to content

Commit f371300

Browse files
Merge pull request #540 from davidastephens/quotes
BUG: Fix yahoo quotes API
2 parents 3c17058 + 6670cdf commit f371300

File tree

5 files changed

+62
-52
lines changed

5 files changed

+62
-52
lines changed

docs/source/remote_data.rst

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,14 @@
1717
Remote Data Access
1818
******************
1919

20-
.. warning::
21-
22-
Yahoo! Finance has been immediately deprecated. Yahoo! substantially
23-
altered their API in late 2017 and the csv endpoint was retired.
2420

2521
.. _remote_data.data_reader:
2622

2723
Functions from :mod:`pandas_datareader.data` and :mod:`pandas_datareader.wb`
2824
extract data from various Internet sources into a pandas DataFrame.
2925
Currently the following sources are supported:
3026

27+
- :ref:`Yahoo! Finance<remote_data.yahoo>`
3128
- :ref:`Google Finance<remote_data.google>`
3229
- :ref:`Tiingo<remote_data.tiingo>`
3330
- :ref:`Morningstar<remote_data.morningstar>`
@@ -48,8 +45,31 @@ Currently the following sources are supported:
4845

4946
It should be noted, that various sources support different kinds of data, so not all sources implement the same methods and the data elements returned might also differ.
5047

48+
.. _remote_data.yahoo:
49+
50+
Yahoo! Finance
51+
==============
52+
53+
.. ipython:: python
54+
55+
import pandas_datareader.data as web
56+
import datetime
57+
start = datetime.datetime(2010, 1, 1)
58+
end = datetime.datetime(2013, 1, 27)
59+
f = web.DataReader('F', 'yahoo', start, end)
60+
f.ix['2010-01-04']
61+
62+
63+
.. ipython:: python
64+
65+
import pandas_datareader.data as web
66+
aapl = web.get_quote_yahoo('AAPL')
67+
aapl.loc['AAPL']
68+
69+
5170
.. _remote_data.google:
5271

72+
5373
Google Finance
5474
==============
5575

docs/source/whatsnew/v0.7.0.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,4 @@ Bug Fixes
6262
- Added back support for Yahoo! price, dividends, and splits data for stocks
6363
and currency pairs (:issue:`487`).
6464
- Add `is_list_like` to compatibility layer to avoid failure on pandas >= 0.23 (:issue:`520`)
65-
65+
- Fix Yahoo! quote reader (:issue: `540`)

pandas_datareader/data.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ def get_data_yahoo_actions(*args, **kwargs):
8484

8585

8686
def get_quote_yahoo(*args, **kwargs):
87-
raise ImmediateDeprecationError(DEP_ERROR_MSG.format('Yahoo Quotes'))
8887
return YahooQuotesReader(*args, **kwargs).read()
8988

9089

pandas_datareader/tests/yahoo/test_yahoo.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
import pandas_datareader.data as web
1212
from pandas_datareader.data import YahooDailyReader
13-
from pandas_datareader.yahoo.quotes import _yahoo_codes
1413
from pandas_datareader._utils import RemoteDataError
1514
from pandas_datareader._testing import skip_on_exception
1615

@@ -39,41 +38,39 @@ def test_yahoo_fails(self):
3938
with pytest.raises(Exception):
4039
web.DataReader('NON EXISTENT TICKER', 'yahoo', start, end)
4140

42-
@pytest.mark.skip('Yahoo quotes deprecated')
4341
def test_get_quote_series(self):
42+
stringlist = ['GOOG', 'AAPL']
43+
fields = ['exchange', 'sharesOutstanding', 'epsForward']
4444
try:
45-
df = web.get_quote_yahoo(pd.Series(['GOOG', 'AAPL', 'GOOG']))
45+
AAPL = web.get_quote_yahoo('AAPL')
46+
df = web.get_quote_yahoo(pd.Series(stringlist))
4647
except ConnectionError:
4748
pytest.xfail(reason=XFAIL_REASON)
48-
tm.assert_series_equal(df.iloc[0], df.iloc[2])
49+
tm.assert_series_equal(AAPL.iloc[0][fields], df.loc['AAPL'][fields])
50+
assert sorted(stringlist) == sorted(list(df.index.values))
4951

50-
@pytest.mark.skip('Yahoo quotes deprecated')
5152
def test_get_quote_string(self):
52-
_yahoo_codes.update({'MarketCap': 'j1'})
5353
try:
5454
df = web.get_quote_yahoo('GOOG')
5555
except ConnectionError:
5656
pytest.xfail(reason=XFAIL_REASON)
5757

58-
assert not pd.isnull(df['MarketCap'][0])
58+
assert not pd.isnull(df['marketCap'][0])
5959

60-
@pytest.mark.skip('Yahoo quotes deprecated')
6160
def test_get_quote_stringlist(self):
61+
stringlist = ['GOOG', 'AAPL']
6262
try:
63-
df = web.get_quote_yahoo(['GOOG', 'AAPL', 'GOOG'])
63+
df = web.get_quote_yahoo(stringlist)
6464
except ConnectionError:
6565
pytest.xfail(reason=XFAIL_REASON)
66-
tm.assert_series_equal(df.iloc[0], df.iloc[2])
66+
assert sorted(stringlist) == sorted(list(df.index.values))
6767

68-
@pytest.mark.skip('Yahoo quotes deprecated')
6968
def test_get_quote_comma_name(self):
70-
_yahoo_codes.update({'name': 'n'})
7169
try:
7270
df = web.get_quote_yahoo(['RGLD'])
7371
except ConnectionError:
7472
pytest.xfail(reason=XFAIL_REASON)
75-
del _yahoo_codes['name']
76-
assert df['name'][0] == 'Royal Gold, Inc.'
73+
assert df['longName'][0] == 'Royal Gold, Inc.'
7774

7875
@pytest.mark.skip('Unreliable test, receive partial '
7976
'components back for dow_jones')

pandas_datareader/yahoo/quotes.py

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
1-
from collections import defaultdict
2-
import csv
1+
import json
2+
from collections import OrderedDict
33

44
import pandas.compat as compat
55
from pandas import DataFrame
66

7+
78
from pandas_datareader.base import _BaseReader
89

9-
_yahoo_codes = {'symbol': 's', 'last': 'l1', 'change_pct': 'p2', 'PE': 'r',
10-
'time': 't1', 'short_ratio': 's7'}
10+
11+
_DEFAULT_PARAMS = {
12+
'lang': 'en-US',
13+
'corsDomain': 'finance.yahoo.com',
14+
'.tsrc': 'finance',
15+
}
1116

1217

1318
class YahooQuotesReader(_BaseReader):
@@ -16,39 +21,28 @@ class YahooQuotesReader(_BaseReader):
1621

1722
@property
1823
def url(self):
19-
return 'http://finance.yahoo.com/d/quotes.csv'
24+
return 'https://query1.finance.yahoo.com/v7/finance/quote'
2025

21-
@property
22-
def params(self):
23-
"""Parameters to use in API calls"""
26+
def read(self):
2427
if isinstance(self.symbols, compat.string_types):
25-
sym_list = self.symbols
28+
return self._read_one_data(self.url, self.params(self.symbols))
2629
else:
27-
sym_list = '+'.join(self.symbols)
28-
29-
# For codes see: http://www.gummy-stuff.org/Yahoo-data.htm
30-
#
30+
data = OrderedDict()
31+
for symbol in self.symbols:
32+
data[symbol] = \
33+
self._read_one_data(
34+
self.url, self.params(symbol)).loc[symbol]
35+
return DataFrame.from_dict(data, orient='index')
36+
37+
def params(self, symbol):
38+
"""Parameters to use in API calls"""
3139
# Construct the code request string.
32-
request = ''.join(compat.itervalues(_yahoo_codes))
33-
params = {'s': sym_list, 'f': request}
40+
params = {'symbols': symbol}
41+
params.update(_DEFAULT_PARAMS)
3442
return params
3543

3644
def _read_lines(self, out):
37-
data = defaultdict(list)
38-
header = list(_yahoo_codes.keys())
39-
40-
for line in csv.reader(out.readlines()):
41-
for i, field in enumerate(line):
42-
if field[-2:] == '%"':
43-
v = float(field.strip('"%'))
44-
elif field[0] == '"':
45-
v = field.strip('"')
46-
else:
47-
try:
48-
v = float(field)
49-
except ValueError:
50-
v = field
51-
data[header[i]].append(v)
52-
45+
data = json.loads(out.read())['quoteResponse']['result'][0]
5346
idx = data.pop('symbol')
54-
return DataFrame(data, index=idx)
47+
data['price'] = data['regularMarketPrice']
48+
return DataFrame(data, index=[idx])

0 commit comments

Comments
 (0)