Skip to content

Commit b1b54d6

Browse files
authored
Merge pull request #638 from addisonlynch/iexcloud
COMPAT: Migrate IEX Daily Reader to IEX Cloud
2 parents b2ee60b + f160ca6 commit b1b54d6

File tree

5 files changed

+44
-20
lines changed

5 files changed

+44
-20
lines changed

docs/source/remote_data.rst

+5-2
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,12 @@ writing).
7070
IEX
7171
===
7272

73+
.. warning:: Usage of all IEX readers now requries an API key. See
74+
below for additional information.
75+
7376
The Investors Exchange (IEX) provides a wide range of data through an
74-
`API <https://iextrading.com/developer/docs/>`__. Historical stock
75-
prices are available for up to 5 years:
77+
`API <https://iexcloud.io/api/docs/>`__. Historical stock
78+
prices are available for up to 15 years. The usage of these readers requires an API key, which can be stored in the ``IEX_API_TOKEN`` environment variable.
7679

7780
.. ipython:: python
7881

docs/source/whatsnew/v0.8.0.txt

+6-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Highlights include:
88
- A new connector for Econdb was introduced. Econdb provides
99
aggregated economic data from 90+ official statistical agencies
1010
(:issue:`615`)
11+
- Migrated IEX readers to `IEX Cloud <https://iexcloud.io>`__. All
12+
readers now require an API token (``IEX_API_TOKEN``)
1113
- Removal of Google finance and Morningstar, which were deprecated in 0.7.0.
1214
- Immediate deprecation of Robinhood for quotes and historical data. Robinhood
1315
ended support for these endpoints in 1/2019
@@ -24,6 +26,7 @@ Enhancements
2426
~~~~~~~~~~~~
2527

2628
- Added Tiingo IEX Historical reader.
29+
- Up to 15 years of historical prices from IEX with new platform, IEX Cloud
2730

2831
.. _whatsnew_080.api_breaking:
2932

@@ -33,7 +36,9 @@ Backwards incompatible API changes
3336
- Immediate deprecation of Robinhood for quotes and historical data. Robinhood
3437
ended support for these endpoints in 1/2019. The Robinhood quotes and daily
3538
readers will raise an ``ImmediateDeprecationError`` when called.
36-
39+
- Usage of all IEX readers requires an IEX Cloud API token, which can
40+
be passed as a parameter or stored in the environment variable
41+
``IEX_API_TOKEN``
3742

3843
.. _whatsnew_080.bug_fixes:
3944

pandas_datareader/data.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ def DataReader(name, data_source=None, start=None, end=None,
312312

313313
elif data_source == "iex":
314314
return IEXDailyReader(symbols=name, start=start, end=end,
315-
chunksize=25,
315+
chunksize=25, api_key=access_key,
316316
retry_count=retry_count, pause=pause,
317317
session=session).read()
318318

pandas_datareader/iex/daily.py

+28-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import datetime
22
import json
3+
import os
34

45
import pandas as pd
56

@@ -40,10 +41,24 @@ class IEXDailyReader(_DailyBaseReader):
4041
Number of symbols to download consecutively before intiating pause.
4142
session : Session, default None
4243
requests.sessions.Session instance to be used
44+
api_key: str
45+
IEX Cloud Secret Token
4346
"""
4447

4548
def __init__(self, symbols=None, start=None, end=None, retry_count=3,
46-
pause=0.1, session=None, chunksize=25):
49+
pause=0.1, session=None, chunksize=25, api_key=None):
50+
if api_key is None:
51+
api_key = os.getenv('IEX_API_KEY')
52+
if not api_key or not isinstance(api_key, str):
53+
raise ValueError('The IEX Cloud API key must be provided either '
54+
'through the api_key variable or through the '
55+
' environment variable IEX_API_KEY')
56+
# Support for sandbox environment (testing purposes)
57+
if os.getenv("IEX_SANDBOX") == "enable":
58+
self.sandbox = True
59+
else:
60+
self.sandbox = False
61+
self.api_key = api_key
4762
super(IEXDailyReader, self).__init__(symbols=symbols, start=start,
4863
end=end, retry_count=retry_count,
4964
pause=pause, session=session,
@@ -52,7 +67,10 @@ def __init__(self, symbols=None, start=None, end=None, retry_count=3,
5267
@property
5368
def url(self):
5469
"""API URL"""
55-
return 'https://api.iextrading.com/1.0/stock/market/batch'
70+
if self.sandbox is True:
71+
return 'https://sandbox.iexapis.com/stable/stock/market/batch'
72+
else:
73+
return 'https://cloud.iexapis.com/stable/stock/market/batch'
5674

5775
@property
5876
def endpoint(self):
@@ -69,20 +87,24 @@ def _get_params(self, symbol):
6987
"symbols": symbolList,
7088
"types": self.endpoint,
7189
"range": chart_range,
90+
"token": self.api_key
7291
}
7392
return params
7493

7594
def _range_string_from_date(self):
7695
delta = relativedelta(self.start, datetime.datetime.now())
77-
if 2 <= (delta.years * -1) <= 5:
96+
years = (delta.years * -1)
97+
if 5 <= years <= 15:
98+
return "max"
99+
if 2 <= years < 5:
78100
return "5y"
79-
elif 1 <= (delta.years * -1) <= 2:
101+
elif 1 <= years < 2:
80102
return "2y"
81-
elif 0 <= (delta.years * -1) < 1:
103+
elif 0 <= years < 1:
82104
return "1y"
83105
else:
84106
raise ValueError(
85-
"Invalid date specified. Must be within past 5 years.")
107+
"Invalid date specified. Must be within past 15 years.")
86108

87109
def read(self):
88110
"""Read data"""

pandas_datareader/tests/test_iex_daily.py

+4-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from datetime import datetime
2+
import os
23

34
from pandas import DataFrame, MultiIndex
45

@@ -7,6 +8,8 @@
78
import pandas_datareader.data as web
89

910

11+
@pytest.mark.skipif(os.getenv("IEX_SANDBOX") != 'enable',
12+
reason='All tests must be run in sandbox mode')
1013
class TestIEXDaily(object):
1114

1215
@classmethod
@@ -31,7 +34,7 @@ def test_iex_bad_symbol_list(self):
3134
self.start, self.end)
3235

3336
def test_daily_invalid_date(self):
34-
start = datetime(2010, 1, 5)
37+
start = datetime(2000, 1, 5)
3538
end = datetime(2017, 5, 24)
3639
with pytest.raises(Exception):
3740
web.DataReader(["AAPL", "TSLA"], "iex", start, end)
@@ -40,7 +43,6 @@ def test_single_symbol(self):
4043
df = web.DataReader("AAPL", "iex", self.start, self.end)
4144
assert list(df) == ["open", "high", "low", "close", "volume"]
4245
assert len(df) == 578
43-
assert df["volume"][-1] == 19219154
4446

4547
def test_multiple_symbols(self):
4648
syms = ["AAPL", "MSFT", "TSLA"]
@@ -64,11 +66,3 @@ def test_multiple_symbols_2(self):
6466

6567
assert len(a) == 73
6668
assert len(t) == 73
67-
68-
expected3 = t.loc["2017-02-09"]
69-
assert expected3["close"] == 269.20
70-
assert expected3["high"] == 271.18
71-
72-
expected4 = t.loc["2017-05-24"]
73-
assert expected4["close"] == 310.22
74-
assert expected4["high"] == 311.0

0 commit comments

Comments
 (0)