Skip to content

Rgkimball iex #446

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

Merged
merged 10 commits into from
Jan 18, 2018
9 changes: 8 additions & 1 deletion pandas_datareader/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
from ._version import get_versions

from .data import (get_components_yahoo, get_data_famafrench, get_data_google,
get_data_yahoo, get_data_enigma, get_data_yahoo_actions,
get_quote_google, get_quote_yahoo, DataReader, Options)
get_quote_google, get_quote_yahoo, get_tops_iex,
get_last_iex, get_markets_iex, get_summary_iex,
get_records_iex, get_recent_iex, get_iex_symbols,
get_iex_book, DataReader, Options)

__version__ = get_versions()['version']
del get_versions

__all__ = ['__version__', 'get_components_yahoo', 'get_data_enigma',
'get_data_famafrench', 'get_data_google', 'get_data_yahoo',
'get_data_yahoo_actions', 'get_quote_google', 'get_quote_yahoo',
'get_iex_book', 'get_iex_symbols', 'get_last_iex',
'get_markets_iex', 'get_recent_iex', 'get_records_iex',
'get_summary_iex', 'get_tops_iex',
'DataReader', 'Options']
15 changes: 15 additions & 0 deletions pandas_datareader/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,14 +133,29 @@ def _get_response(self, url, params=None, headers=None):
# Get a new breadcrumb if necessary, in case ours is invalidated
if isinstance(params, list) and 'crumb' in params:
params['crumb'] = self._get_crumb(self.retry_count)

# If our output error function returns True, exit the loop.
if self._output_error(response):
break

if params is not None and len(params) > 0:
url = url + "?" + urlencode(params)

raise RemoteDataError('Unable to read URL: {0}'.format(url))

def _get_crumb(self, *args):
""" To be implemented by subclass """
raise NotImplementedError("Subclass has not implemented method.")

def _output_error(self, out):
"""If necessary, a service can implement an interpreter for any non-200
HTTP responses.

:param out: raw output from an HTTP request
:return: boolean
"""
return False

def _read_lines(self, out):
rs = read_csv(out, index_col=0, parse_dates=True,
na_values=('-', 'null'))[::-1]
Expand Down
153 changes: 153 additions & 0 deletions pandas_datareader/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
from pandas_datareader.google.daily import GoogleDailyReader
from pandas_datareader.google.options import Options as GoogleOptions
from pandas_datareader.google.quotes import GoogleQuotesReader
from pandas_datareader.iex.deep import Deep as IEXDeep
from pandas_datareader.iex.tops import LastReader as IEXLasts
from pandas_datareader.iex.tops import TopsReader as IEXTops
from pandas_datareader.moex import MoexReader
from pandas_datareader.nasdaq_trader import get_nasdaq_symbols
from pandas_datareader.oecd import OECDReader
Expand All @@ -29,6 +32,9 @@
'get_data_fred', 'get_data_google', 'get_data_moex',
'get_data_quandl', 'get_data_yahoo', 'get_data_yahoo_actions',
'get_nasdaq_symbols', 'get_quote_google', 'get_quote_yahoo',
'get_tops_iex', 'get_summary_iex', 'get_records_iex',
'get_recent_iex', 'get_markets_iex', 'get_last_iex',
'get_iex_symbols', 'get_iex_book', 'get_dailysummary_iex',
'get_data_stooq', 'DataReader']


Expand Down Expand Up @@ -76,6 +82,129 @@ def get_data_stooq(*args, **kwargs):
return StooqDailyReader(*args, **kwargs).read()


def get_tops_iex(*args, **kwargs):
return IEXTops(*args, **kwargs).read()


def get_last_iex(*args, **kwargs):
return IEXLasts(*args, **kwargs).read()


def get_markets_iex(*args, **kwargs):
"""
Returns near-real time volume data across markets segregated by tape
and including a percentage of overall volume during the session

This endpoint does not accept any parameters.

Reference: https://www.iextrading.com/developer/docs/#markets

:return: DataFrame
"""
from pandas_datareader.iex.market import MarketReader
return MarketReader(*args, **kwargs).read()


def get_dailysummary_iex(*args, **kwargs):
"""
Returns a summary of daily market volume statistics. Without parameters,
this will return the most recent trading session by default.

:param start:
A datetime object - the beginning of the date range.
:param end:
A datetime object - the end of the date range.

Reference: https://www.iextrading.com/developer/docs/#historical-daily

:return: DataFrame
"""
from pandas_datareader.iex.stats import DailySummaryReader
return DailySummaryReader(*args, **kwargs).read()


def get_summary_iex(*args, **kwargs):
"""
Returns an aggregated monthly summary of market volume and a variety of
related metrics for trades by lot size, security market cap, and venue.
In the absence of parameters, this will return month-to-date statistics.
For ranges spanning multiple months, this will return one row per month.

:param start:
A datetime object - the beginning of the date range.
:param end:
A datetime object - the end of the date range.

:return: DataFrame
"""
from pandas_datareader.iex.stats import MonthlySummaryReader
return MonthlySummaryReader(*args, **kwargs).read()


def get_records_iex(*args, **kwargs):
"""
Returns the record value, record date, recent value, and 30-day average for
market volume, # of symbols traded, # of routed trades and notional value.
This function accepts no additional parameters.

Reference: https://www.iextrading.com/developer/docs/#records

:return: DataFrame
"""
from pandas_datareader.iex.stats import RecordsReader
return RecordsReader(*args, **kwargs).read()


def get_recent_iex(*args, **kwargs):
"""
Returns market volume and trade routing statistics for recent sessions.
Also reports IEX's relative market share, lit share volume and a boolean
halfday indicator.

Reference: https://www.iextrading.com/developer/docs/#recent

:return: DataFrame
"""
from pandas_datareader.iex.stats import RecentReader
return RecentReader(*args, **kwargs).read()


def get_iex_symbols(*args, **kwargs):
"""
Returns a list of all equity symbols available for trading on IEX. Accepts
no additional parameters.

Reference: https://www.iextrading.com/developer/docs/#symbols

:return: DataFrame
"""
from pandas_datareader.iex.ref import SymbolsReader
return SymbolsReader(*args, **kwargs).read()


def get_iex_book(*args, **kwargs):
"""
Returns an array of dictionaries with depth of book data from IEX for up to
10 securities at a time. Returns a dictionary of the bid and ask books.

:param symbols:
A string or list of strings of valid tickers
:param service:
'book': Live depth of book data
'op-halt-status': Checks to see if the exchange has instituted a halt
'security-event': Denotes individual security related event
'ssr-status': Short Sale Price Test restrictions, per reg 201 of SHO
'system-event': Relays current feed status (i.e. market open)
'trades': Retrieves recent executions, trade size/price and flags
'trade-breaks': Lists execution breaks for the current trading session
'trading-status': Returns status and cause codes for securities

:return: Object
"""
from pandas_datareader.iex.deep import Deep
return Deep(*args, **kwargs).read()


def DataReader(name, data_source=None, start=None, end=None,
retry_count=3, pause=0.001, session=None, access_key=None):
"""
Expand Down Expand Up @@ -103,6 +232,8 @@ def DataReader(name, data_source=None, start=None, end=None,
single value given for symbol, represents the pause between retries.
session : Session, default None
requests.sessions.Session instance to be used
access_key : (str, None)
Optional parameter to specify an API key for certain data sources.

Examples
----------
Expand All @@ -117,6 +248,13 @@ def DataReader(name, data_source=None, start=None, end=None,
# Data from Google Finance
aapl = DataReader("AAPL", "google")

# Price and volume data from IEX
tops = DataReader(["GS", "AAPL"], "iex-tops")
# Top of book executions from IEX
gs = DataReader("GS", "iex-last")
# Real-time depth of book data from IEX
gs = DataReader("GS", "iex-book")

# Data from FRED
vix = DataReader("VIXCLS", "fred")

Expand All @@ -140,6 +278,7 @@ def DataReader(name, data_source=None, start=None, end=None,
return YahooActionReader(symbols=name, start=start, end=end,
retry_count=retry_count, pause=pause,
session=session).read()

elif data_source == "yahoo-dividends":
return YahooDivReader(symbols=name, start=start, end=end,
adjust_price=False, chunksize=25,
Expand All @@ -151,6 +290,15 @@ def DataReader(name, data_source=None, start=None, end=None,
chunksize=25,
retry_count=retry_count, pause=pause,
session=session).read()
elif data_source == "iex-tops":
return IEXTops(symbols=name, start=start, end=end,
retry_count=retry_count, pause=pause,
session=session).read()

elif data_source == "iex-last":
return IEXLasts(symbols=name, start=start, end=end,
retry_count=retry_count, pause=pause,
session=session).read()

elif data_source == "bankofcanada":
return BankOfCanadaReader(symbols=name, start=start, end=end,
Expand All @@ -162,6 +310,11 @@ def DataReader(name, data_source=None, start=None, end=None,
retry_count=retry_count, pause=pause,
session=session).read()

elif data_source == "iex-book":
return IEXDeep(symbols=name, service="book", start=start, end=end,
retry_count=retry_count, pause=pause,
session=session).read()

elif data_source == "enigma":
return EnigmaReader(dataset_id=name, api_key=access_key).read()

Expand Down
5 changes: 5 additions & 0 deletions pandas_datareader/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""Custom warnings and exceptions"""


class UnstableAPIWarning(Warning):
pass
83 changes: 83 additions & 0 deletions pandas_datareader/iex/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import json

import pandas as pd
from pandas.io.common import urlencode
from pandas_datareader.base import _BaseReader


# Data provided for free by IEX
# Data is furnished in compliance with the guidelines promulgated in the IEX
# API terms of service and manual
# See https://iextrading.com/api-exhibit-a/ for additional information
# and conditions of use


class IEX(_BaseReader):
"""
Serves as the base class for all IEX API services.
"""

_format = 'json'

def __init__(self, symbols=None, start=None, end=None, retry_count=3,
pause=0.001, session=None):
super(IEX, self).__init__(symbols=symbols,
start=start, end=end,
retry_count=retry_count,
pause=pause, session=session)

@property
def service(self):
# This property will be overridden by the subclass
raise NotImplementedError("IEX API service not specified.")

@property
def url(self):
qstring = urlencode(self._get_params(self.symbols))
return "https://api.iextrading.com/1.0/{}?{}".format(self.service,
qstring)

def read(self):
df = super(IEX, self).read()
if isinstance(df, pd.DataFrame):
df = df.squeeze()
if not isinstance(df, pd.DataFrame):
df = pd.DataFrame(df)
return df

def _get_params(self, symbols):
p = {}
if isinstance(symbols, list):
p['symbols'] = ','.join(symbols)
elif isinstance(symbols, str):
p['symbols'] = symbols
return p

def _output_error(self, out):
"""If IEX returns a non-200 status code, we need to notify the user of
the error returned.

:param out: Raw HTTP Output
"""
try:
content = json.loads(out.text)
except Exception:
raise TypeError("Failed to interpret response as JSON.")

for key, string in content.items():
e = "IEX Output error encountered: {}".format(string)
if key == 'error':
raise Exception(e)

def _read_lines(self, out):
"""IEX's output does not need anything complex, so we're overriding to
use Pandas' default interpreter

:param out: Raw HTTP Output
:return: DataFrame
"""

# IEX will return a blank line for invalid tickers:
if isinstance(out, list):
out = [x for x in out if x is not None]
return pd.DataFrame(out) if len(out) > 0 else pd.DataFrame()
Loading