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

COMPAT: Migrate IEX Daily Reader to IEX Cloud #638

Merged
merged 2 commits into from
Jun 16, 2019
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
7 changes: 5 additions & 2 deletions docs/source/remote_data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,12 @@ writing).
IEX
===

.. warning:: Usage of all IEX readers now requries an API key. See
below for additional information.

The Investors Exchange (IEX) provides a wide range of data through an
`API <https://iextrading.com/developer/docs/>`__. Historical stock
prices are available for up to 5 years:
`API <https://iexcloud.io/api/docs/>`__. Historical stock
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.

.. ipython:: python

Expand Down
7 changes: 6 additions & 1 deletion docs/source/whatsnew/v0.8.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Highlights include:
- A new connector for Econdb was introduced. Econdb provides
aggregated economic data from 90+ official statistical agencies
(:issue:`615`)
- Migrated IEX readers to `IEX Cloud <https://iexcloud.io>`__. All
readers now require an API token (``IEX_API_TOKEN``)
- Removal of Google finance and Morningstar, which were deprecated in 0.7.0.
- Immediate deprecation of Robinhood for quotes and historical data. Robinhood
ended support for these endpoints in 1/2019
Expand All @@ -24,6 +26,7 @@ Enhancements
~~~~~~~~~~~~

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

.. _whatsnew_080.api_breaking:

Expand All @@ -33,7 +36,9 @@ Backwards incompatible API changes
- Immediate deprecation of Robinhood for quotes and historical data. Robinhood
ended support for these endpoints in 1/2019. The Robinhood quotes and daily
readers will raise an ``ImmediateDeprecationError`` when called.

- Usage of all IEX readers requires an IEX Cloud API token, which can
be passed as a parameter or stored in the environment variable
``IEX_API_TOKEN``

.. _whatsnew_080.bug_fixes:

Expand Down
2 changes: 1 addition & 1 deletion pandas_datareader/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ def DataReader(name, data_source=None, start=None, end=None,

elif data_source == "iex":
return IEXDailyReader(symbols=name, start=start, end=end,
chunksize=25,
chunksize=25, api_key=access_key,
retry_count=retry_count, pause=pause,
session=session).read()

Expand Down
34 changes: 28 additions & 6 deletions pandas_datareader/iex/daily.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import datetime
import json
import os

import pandas as pd

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

def __init__(self, symbols=None, start=None, end=None, retry_count=3,
pause=0.1, session=None, chunksize=25):
pause=0.1, session=None, chunksize=25, api_key=None):
if api_key is None:
api_key = os.getenv('IEX_API_KEY')
if not api_key or not isinstance(api_key, str):
raise ValueError('The IEX Cloud API key must be provided either '
'through the api_key variable or through the '
' environment variable IEX_API_KEY')
# Support for sandbox environment (testing purposes)
if os.getenv("IEX_SANDBOX") == "enable":
self.sandbox = True
else:
self.sandbox = False
self.api_key = api_key
super(IEXDailyReader, self).__init__(symbols=symbols, start=start,
end=end, retry_count=retry_count,
pause=pause, session=session,
Expand All @@ -52,7 +67,10 @@ def __init__(self, symbols=None, start=None, end=None, retry_count=3,
@property
def url(self):
"""API URL"""
return 'https://api.iextrading.com/1.0/stock/market/batch'
if self.sandbox is True:
return 'https://sandbox.iexapis.com/stable/stock/market/batch'
else:
return 'https://cloud.iexapis.com/stable/stock/market/batch'

@property
def endpoint(self):
Expand All @@ -69,20 +87,24 @@ def _get_params(self, symbol):
"symbols": symbolList,
"types": self.endpoint,
"range": chart_range,
"token": self.api_key
}
return params

def _range_string_from_date(self):
delta = relativedelta(self.start, datetime.datetime.now())
if 2 <= (delta.years * -1) <= 5:
years = (delta.years * -1)
if 5 <= years <= 15:
return "max"
if 2 <= years < 5:
return "5y"
elif 1 <= (delta.years * -1) <= 2:
elif 1 <= years < 2:
return "2y"
elif 0 <= (delta.years * -1) < 1:
elif 0 <= years < 1:
return "1y"
else:
raise ValueError(
"Invalid date specified. Must be within past 5 years.")
"Invalid date specified. Must be within past 15 years.")

def read(self):
"""Read data"""
Expand Down
14 changes: 4 additions & 10 deletions pandas_datareader/tests/test_iex_daily.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from datetime import datetime
import os

from pandas import DataFrame, MultiIndex

Expand All @@ -7,6 +8,8 @@
import pandas_datareader.data as web


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

@classmethod
Expand All @@ -31,7 +34,7 @@ def test_iex_bad_symbol_list(self):
self.start, self.end)

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

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

assert len(a) == 73
assert len(t) == 73

expected3 = t.loc["2017-02-09"]
assert expected3["close"] == 269.20
assert expected3["high"] == 271.18

expected4 = t.loc["2017-05-24"]
assert expected4["close"] == 310.22
assert expected4["high"] == 311.0