diff --git a/CHANGELOG.md b/CHANGELOG.md index 6201c98..4b54d3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,3 @@ # Changelog -See [here](https://addisonlynch.github.io/whatsnew.html). +See [here](https://addisonlynch.github.io/stable/whatsnew.html). diff --git a/README.rst b/README.rst index 21007de..647f92d 100644 --- a/README.rst +++ b/README.rst @@ -14,21 +14,26 @@ iexfinance :target: https://opensource.org/licenses/Apache-2.0 -Python module to retrieve stock data from the +Python wrapper around the `Investors Exchange (IEX) `__ -`Developer API `__ -platform. iexfinance provides real-time financial data from the various IEX -endpoints, including: +`Developer API `__. +An easy-to-use interface to obtain: +- Real-time quotes +- Historical data +- Fundamentals, +- Actions (dividends, splits), Sector Performance +- Trading analyses (gainers, losers, etc.) +- IEX Market Data & Stats -This data includes stock quotes, fundamentals, actions, and information. In -addition, support for IEX market data and statistics is provided. +iexfinance provides real-time financial data from the various IEX +endpoints, including: -- `Stocks `__ -- `Reference Data `__ -- `IEX Market Data `__ -- `IEX Stats `__ +- Stocks (`IEX Docs `__) +- Reference Data (`IEX Docs `__) +- IEX Market Data (`IEX Docs `__) +- IEX Stats (`IEX Docs `__) Documentation ------------- @@ -52,60 +57,126 @@ From development repository (dev version): $ cd iexfinance $ python3 setup.py install -Usage Examples --------------- +Common Usage Examples +--------------------- -The `iex-examples `__ repository provides a number of detailed examples of ``iexfinance`` usage. Basic examples are also provided below. +The `iex-examples `__ repository provides a number of detailed examples of iexfinance usage. Basic examples are also provided below. Using iexfinance to access data from IEX is quite easy. The most commonly-used endpoints are the `Stocks `__ endpoints, which allow access to various information regarding equities, including quotes, historical prices, dividends, and much more. -All top-level functions (such as ``Stock`` and ``get_historical_data``) +Real-time Quotes +^^^^^^^^^^^^^^^^ -Stock Endpoints -^^^^^^^^^^^^^^^ +To obtain real-time quotes for one or more symbols, use the ``get_price`` +method of the ``Stock`` object: .. code:: python - from iexfinance import Stock + from iexfinance.stocks import Stock tsla = Stock('TSLA') - tsla.get_open() tsla.get_price() -It's also possible to obtain historical data from the ``get_historical_data`` -top-level function. This will return a daily time-series of the ticker -requested over the desired date range (``start`` and ``end`` passed as -``datetime.datetime`` objects). +or for multiple symbols, use a list or list-like object (Tuple, Pandas Series, +etc.): -Pandas DataFrame and JSON (dict) output formatting are selected with the -``output_format`` parameter. +.. code:: python + + batch = Stock(["TSLA", "AAPL"]) + batch.get_price() + + +Historical Data +^^^^^^^^^^^^^^^ + +It's possible to obtain historical data the ``get_historical_data`` and +``get_historical_intraday``. -**Historical Data** +Daily +~~~~~ + +To obtain daily historical price data for one or more symbols, use the +``get_historical_data`` function. This will return a daily time-series of the ticker +requested over the desired date range (``start`` and ``end`` passed as +``datetime.datetime`` objects): .. code:: python - from iexfinance import get_historical_data - from datetime import datetime + from datetime import datetime + from iexfinance.stocks import get_historical_data + + start = datetime(2017, 1, 1) + end = datetime(2018, 1, 1) - start = datetime(2017, 2, 9) - end = datetime(2017, 5, 24) + df = get_historical_data("TSLA", start, end) - df = get_historical_data("AAPL", start=start, end=end, output_format='pandas') - df.head() -The resulting DataFrame will indexed by date, with a column for each OHLC -datapoint. +For Pandas DataFrame output formatting, pass ``output_format``: + +.. code:: python + + df = get_historical_data("TSLA", start, end, output_format='pandas') It's really simple to plot this data, using `matplotlib `__: .. code:: python - import matplotlib.pyplot as plt + import matplotlib.pyplot as plt + + df.plot() + plt.show() + + +Minutely (Intraday) +~~~~~~~~~~~~~~~~~~~ + +To obtain historical intraday data, use ``get_historical_intraday`` as follows. +Pass an optional ``date`` to specify a date within three months prior to the +current day (default is current date): + +.. code:: python + + from datetime import datetime + from iexfinance.stocks import get_historical_intraday + + date = datetime(2018, 11, 27) + + get_historical_intraday("AAPL", date) + +or for a Pandas Dataframe indexed by each minute: + +.. code:: python + + get_historical_intraday("AAPL", output_format='pandas') + + +Endpoints +--------- + +Stock Endpoints +^^^^^^^^^^^^^^^ + +The ``Stock`` function creates a ``StockReader`` instance which has a method to +retrieve each of the Stocks endpoints (``get_quote``, ``get_book``, +``get_volume_by_venue``, etc.): + +.. code:: python + + from iexfinance.stocks import Stock + tsla = Stock('TSLA') + tsla.get_open() + tsla.get_price() + +Pandas DataFrame and JSON (dict) output formatting are selected with the +``output_format`` parameter when calling ``Stock``. + +.. code:: python + + tsla = Stock("TSLA", output_format='pandas') + tsla.get_quote() - df.plot() - plt.show() IEX Reference Data ^^^^^^^^^^^^^^^^^^ @@ -140,7 +211,6 @@ and ``get_market_deep``. get_market_tops() - IEX Stats ^^^^^^^^^ @@ -165,7 +235,7 @@ for `Request Parameters `_ include ``retry_count``, ``pause``, and ``session``. These parameters are entirely optional. The first two deal with how unsuccessful requests are handled, and the third allows for the passing of a cached ``requests-cache`` -session (see `caching `__). +session (see `caching `__). Contact ------- diff --git a/docs/source/caching.rst b/docs/source/caching.rst index 216286c..3edc6f3 100644 --- a/docs/source/caching.rst +++ b/docs/source/caching.rst @@ -22,7 +22,7 @@ top-level function you are using: .. ipython:: python import datetime - from iexfinance import Stock + from iexfinance.stocks import Stock import requests_cache expiry = datetime.timedelta(days=3) diff --git a/docs/source/conf.py b/docs/source/conf.py index 6cf7c8f..ac426f1 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -58,9 +58,9 @@ # built documents. # # The short X.Y version. -version = '0.3.4' +version = '0.3.5' # The full version, including alpha/beta/rc tags. -release = '0.3.4' +release = '0.3.5' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/source/historical.rst b/docs/source/historical.rst index 780a95d..3b1c816 100644 --- a/docs/source/historical.rst +++ b/docs/source/historical.rst @@ -1,25 +1,35 @@ .. _historical: -.. currentmodule:: iexfinance +.. currentmodule:: iexfinance.stocks Historical Data =============== -Historical time series data is available through the top-level -``get_historical_data`` method, which sources the +Historical time series data is available through the +``get_historical_data`` and ``get_historical_intraday`` functions of +``stocks``, which +source the `chart `__ endpoint. -.. autofunction:: get_historical_data - -Data can be retrieved from up to 5 years before the current date. +Daily data can be retrieved from up to 5 years before the current date, and +historical data up to 3 months prior to the current date. Usage ----- +Daily +^^^^^ + +To obtain daily historical data, use ``get_historical_data``. + +.. autofunction:: get_historical_data + + If no date parameters are passed, the start date will default to 2015/1/1 and the end date will default to the current date. + .. ipython:: python :okwarning: @@ -32,6 +42,27 @@ and the end date will default to the current date. f = get_historical_data('AAPL', start, end, output_format='pandas') f.loc["2017-02-09"] + +Minutely +^^^^^^^^ + +To obtain one-minute intraday data for a given date, use +``get_historical_intraday``. **Note: this endpoint has a maximum of one symbol +and a single date.** + +.. autofunction:: get_historical_intraday + +.. ipython:: python + + from datetime import datetime + from iexfinance.stocks import get_historical_intraday + + date = datetime(2018, 11, 27) + + data = get_historical_intraday("AAPL", date, output_format='pandas') + data.head() + + Plotting -------- diff --git a/docs/source/stocks.rst b/docs/source/stocks.rst index cc40a42..5c75586 100644 --- a/docs/source/stocks.rst +++ b/docs/source/stocks.rst @@ -36,13 +36,11 @@ additional parameters (it uses the defaults) and does not allow Pandas DataFrame .. ipython:: python - from iexfinance import Stock + from iexfinance.stocks import Stock aapl = Stock("aapl") aapl.get_price() -.. autoclass:: iexfinance.stocks.base.StockReader - Formatting ---------- @@ -73,7 +71,7 @@ passing .. ipython:: python - from iexfinance import Stock + from iexfinance.stocks import Stock aapl = Stock("aapl", output_format='pandas') aapl.get_quote().head() @@ -387,7 +385,7 @@ A single symbol request will return data *exactly* as it appears in the IEX docs .. ipython:: python - from iexfinance import Stock + from iexfinance.stocks import Stock aapl = Stock("AAPL") aapl.get_price() @@ -402,7 +400,7 @@ Most endpoints can be formatted as a `pandas.DataFrame`. Multi-symbol requests w .. ipython:: python - from iexfinance import Stock as iex + from iexfinance.stocks import Stock as iex air_transport = Stock(['AAL', 'DAL', 'LUV'], output_format='pandas') air_transport.get_quote().head() @@ -420,7 +418,7 @@ reserved word in Python: .. ipython:: python - from iexfinance import Stock as iex + from iexfinance.stocks import Stock as iex aapl = Stock("AAPL", output_format='pandas') aapl.get_quote(filter_='ytdChange') diff --git a/docs/source/usage.rst b/docs/source/usage.rst index c6c2e6d..7257294 100644 --- a/docs/source/usage.rst +++ b/docs/source/usage.rst @@ -13,14 +13,101 @@ Usage .. _usage.common: -Common Tasks ------------- + +Common Usage Examples +--------------------- + +The `iex-examples `__ repository provides a number of detailed examples of iexfinance usage. Basic examples are also provided below. + +Using iexfinance to access data from IEX is quite easy. The most commonly-used +endpoints are the `Stocks `__ +endpoints, which allow access to various information regarding equities, +including quotes, historical prices, dividends, and much more. + +Real-time Quotes +^^^^^^^^^^^^^^^^ + +To obtain real-time quotes for one or more symbols, use the ``get_price`` +method of the ``Stock`` object: + +.. ipython:: python + + from iexfinance.stocks import Stock + tsla = Stock('TSLA') + tsla.get_price() + +or for multiple symbols, use a list or list-like object (Tuple, Pandas Series, +etc.): + +.. ipython:: python + + batch = Stock(["TSLA", "AAPL"]) + batch.get_price() + Historical Data -~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^ + +It's possible to obtain historical data the ``get_historical_data`` and +``get_historical_intraday``. Daily -^^^^^ +~~~~~ + +To obtain daily historical price data for one or more symbols, use the +``get_historical_data`` function. This will return a daily time-series of the ticker +requested over the desired date range (``start`` and ``end`` passed as +``datetime.datetime`` objects): + +.. ipython:: python + + from datetime import datetime + from iexfinance.stocks import get_historical_data + + start = datetime(2017, 1, 1) + end = datetime(2018, 1, 1) + + df = get_historical_data("TSLA", start, end) + + +For Pandas DataFrame output formatting, pass ``output_format``: + +.. ipython:: python + + df = get_historical_data("TSLA", start, end, output_format='pandas') + +It's really simple to plot this data, using `matplotlib `__: + +.. ipython:: python + + import matplotlib.pyplot as plt + + df.plot() + plt.show() + + +Minutely (Intraday) +~~~~~~~~~~~~~~~~~~~ + +To obtain historical intraday data, use ``get_historical_intraday`` as follows. +Pass an optional ``date`` to specify a date within three months prior to the +current day (default is current date): + +.. ipython:: python + + from datetime import datetime + from iexfinance.stocks import get_historical_intraday + + date = datetime(2018, 11, 27) + + get_historical_intraday("AAPL", date) + +or for a Pandas Dataframe indexed by each minute: + +.. ipython:: python + + get_historical_intraday("AAPL", output_format='pandas') + @@ -84,7 +171,7 @@ the endpoint requested. .. ipython:: python - from iexfinance import Stock + from iexfinance.stocks import Stock aapl = Stock("AAPL") aapl.get_previous() diff --git a/docs/source/whatsnew.rst b/docs/source/whatsnew.rst index 72fff83..7027fd7 100644 --- a/docs/source/whatsnew.rst +++ b/docs/source/whatsnew.rst @@ -19,6 +19,7 @@ New features, bug fixes, and improvements for each release. .. include:: whatsnew/v0.3.1.txt .. include:: whatsnew/v0.3.0.txt + Changelog --------- diff --git a/docs/source/whatsnew/v0.3.5.txt b/docs/source/whatsnew/v0.3.5.txt index a38ff52..2a542b0 100644 --- a/docs/source/whatsnew/v0.3.5.txt +++ b/docs/source/whatsnew/v0.3.5.txt @@ -1,28 +1,30 @@ .. _whatsnew_035: -v0.3.5 (11/12/2018) +v0.3.5 (11/28/2018) ----------------------- This is a minor release from 0.3.4. Highlights: -Adds and updates endpoints related to provider updates by IEX. +- Adds support for intraday historical prices +- Adds support for endpoint additions and updates from 8/8/2018 provider + updates (including expanded cryptocurrency support) +- Various bug fixes and enhancements -New Features + +Enhancements ~~~~~~~~~~~~ +- Adds support for intraday historical data through ``get_historical_intraday`` + of ``stocks``. - Adds support for the `Sector Performance `__ endpoint of Stocks (thank you kafana). - Adds support for the `List `__ infocus endpoint of Stocks (thank you kafana). - Adds support for the `Collections `__ endpoint of Stocks - Adds support for the `Crypto `__ endpoint of Stocks - Adds support for the `Earnings Today `__ endpoint of Stocks - Adds support for the `IPO Calendar `__ endpoint of Stocks - -Enhancements -~~~~~~~~~~~~ - - Adds pandas DataFrame output formatting for ``get_chart``, ``get_dividends``, ``get_earnings``, ``get_financials``. - Adds support for list-like data types for symbols (tuple, pandas.Series, @@ -38,3 +40,12 @@ Bug Fixes `GH34 `__ - ``get_time_series`` returns incorrect range when passed ``range_`` parameter `GH84 `__ +- Repaired issue where get_historical_data for an invalid symbol does not + raise an exception `GH82 `__ + +Backward Compatability +~~~~~~~~~~~~~~~~~~~~~~ + +- ``Stock`` and ``get_historical_data`` have been moved to + ``iexfinance.stocks``. The top-level functions remain with warnings but + will be deprecated in v0.4.0. diff --git a/iexfinance/__init__.py b/iexfinance/__init__.py index f0a4792..c2c0552 100644 --- a/iexfinance/__init__.py +++ b/iexfinance/__init__.py @@ -10,9 +10,10 @@ from iexfinance.utils.exceptions import IEXQueryError __author__ = 'Addison Lynch' -__version__ = '0.3.4' +__version__ = '0.3.5' -WNG_MSG = "%s will be moved to iexfinance.stocks in version 0.4.0" +WNG_MSG = "%s is moved to iexfinance.%s. This function will in be "\ + "deprecated in v0.4.0" # Data provided for free by IEX # Data is furnished in compliance with the guidelines promulgated in the IEX @@ -25,6 +26,8 @@ def Stock(symbols=None, **kwargs): """ Top-level function to to retrieve data from the IEX Stocks endpoints + WARNING: Moving to ``iexfinance.stocks``. Will be deprecated in v0.4.0. + Parameters ---------- symbols: str or list @@ -38,6 +41,8 @@ def Stock(symbols=None, **kwargs): stock.StockReader A StockReader instance """ + import warnings + warnings.warn(WNG_MSG % ("Stock", "stocks")) if isinstance(symbols, str) and symbols: return StockReader([symbols], **kwargs) elif isinstance(symbols, list) and 0 < len(symbols) <= 100: @@ -49,42 +54,27 @@ def Stock(symbols=None, **kwargs): # MOVED to iexfinance.stocks def get_historical_data(*args, **kwargs): import warnings - warnings.warn(WNG_MSG % "get_historical_data") + warnings.warn(WNG_MSG % ("get_historical_data", "stocks")) return stocks.get_historical_data(*args, **kwargs) -# MOVED to iexfinance.stocks def get_market_gainers(*args, **kwargs): - import warnings - warnings.warn(WNG_MSG % "get_market_gainers") return stocks.get_market_gainers(*args, **kwargs) -# MOVED to iexfinance.stocks def get_market_losers(*args, **kwargs): - import warnings - warnings.warn(WNG_MSG % "get_market_losers") return stocks.get_market_losers(*args, **kwargs) -# MOVED to iexfinance.stocks def get_market_most_active(*args, **kwargs): - import warnings - warnings.warn(WNG_MSG % "get_market_most_active") return stocks.get_market_most_active(*args, **kwargs) -# MOVED to iexfinance.stocks def get_market_iex_volume(*args, **kwargs): - import warnings - warnings.warn(WNG_MSG % "get_market_iex_volume") return stocks.get_market_iex_volume(*args, **kwargs) -# MOVED to iexfinance.stocks def get_market_iex_percent(*args, **kwargs): - import warnings - warnings.warn(WNG_MSG % "get_market_iex_percent") return stocks.get_market_iex_percent(*args, **kwargs) diff --git a/iexfinance/stocks/__init__.py b/iexfinance/stocks/__init__.py index 979c540..62f629f 100644 --- a/iexfinance/stocks/__init__.py +++ b/iexfinance/stocks/__init__.py @@ -1,6 +1,7 @@ +from iexfinance.stocks.base import StockReader from iexfinance.stocks.collections import CollectionsReader from iexfinance.stocks.crypto import CryptoReader -from iexfinance.stocks.historical import HistoricalReader +from iexfinance.stocks.historical import HistoricalReader, IntradayReader from iexfinance.stocks.ipocalendar import IPOReader from iexfinance.stocks.movers import MoversReader from iexfinance.stocks.sectorperformance import SectorPerformanceReader @@ -12,14 +13,39 @@ # and conditions of use -def get_historical_data(symbols=None, start=None, end=None, **kwargs): +def Stock(symbols=None, **kwargs): + """ + Function to to retrieve data from the IEX Stocks endpoints + + Parameters + ---------- + symbols: str or list + A string or list of strings that are valid symbols + output_format: str, default 'json', optional + Desired output format for requests + kwargs: + Additional Request Parameters (see base class) + Returns + ------- + stock.StockReader + A StockReader instance + """ + if isinstance(symbols, str) and symbols: + return StockReader([symbols], **kwargs) + elif isinstance(symbols, list) and 0 < len(symbols) <= 100: + return StockReader(symbols, **kwargs) + else: + raise ValueError("Please input a symbol or list of symbols") + + +def get_historical_data(symbols, start=None, end=None, **kwargs): """ Top-level function to obtain historical date for a symbol or list of symbols. Return an instance of HistoricalReader Parameters ---------- - symbols: str or list, default None + symbols: str or list A symbol or list of symbols start: datetime.datetime, default None Beginning of desired date range @@ -37,6 +63,28 @@ def get_historical_data(symbols=None, start=None, end=None, **kwargs): return HistoricalReader(symbols, start=start, end=end, **kwargs).fetch() +def get_historical_intraday(symbol, date=None, **kwargs): + """ + Top-level function to obtain intraday one-minute pricing data for one + symbol on a given date (defaults to current date) + + Parameters + ---------- + symbol: str + A single ticker + date: datetime.datetime, default current date + Desired date for intraday retrieval, defaults to today + kwargs: + Additional Request Parameters (see base class) + + Returns + ------- + list or DataFrame + Intraday pricing data for specified symbol on given date + """ + return IntradayReader(symbol, date, **kwargs).fetch() + + def get_sector_performance(**kwargs): """ This function returns an array of each sector and performance diff --git a/iexfinance/stocks/historical.py b/iexfinance/stocks/historical.py index 0e50da4..90b738a 100644 --- a/iexfinance/stocks/historical.py +++ b/iexfinance/stocks/historical.py @@ -1,6 +1,7 @@ import datetime import pandas as pd +from iexfinance.base import _IEXBase from iexfinance.stocks.base import StockReader from iexfinance.utils.exceptions import IEXSymbolError @@ -58,9 +59,8 @@ def params(self): def _output_format(self, out, fmt_j=None, fmt_p=None): result = {} for symbol in self.symbols: - if symbol not in out: - raise IEXSymbolError("Data for %s could not be found." % - symbol) + if symbol not in out or not out[symbol]["chart"]: + raise IEXSymbolError(symbol) d = out.pop(symbol)["chart"] df = pd.DataFrame(d) if self.output_format == 'pandas': @@ -79,3 +79,37 @@ def _output_format(self, out, fmt_j=None, fmt_p=None): for sym in list(result): result[sym] = result[sym].to_dict('index') return result[self.symbols[0]] if self.n_symbols == 1 else result + + +class IntradayReader(_IEXBase): + """ + Base class for intraday historical data + """ + def __init__(self, symbol, date, **kwargs): + if not date: + date = datetime.datetime.now() + + try: + self.date = date.strftime("%Y%m%d") + except AttributeError: + self.date = date + + if not isinstance(symbol, str): + raise ValueError("Please enter a valid single symbol.") + + self.symbol = symbol + super(IntradayReader, self).__init__(**kwargs) + + @property + def params(self): + return {} + + @property + def url(self): + return 'stock/%s/chart/date/%s' % (self.symbol, self.date) + + def _convert_output(self, out): + if out: + return pd.DataFrame(out).set_index("minute") + else: + return pd.DataFrame([]) diff --git a/tests/test_stocks.py b/tests/test_stocks.py index 2bd4292..9f6ce49 100644 --- a/tests/test_stocks.py +++ b/tests/test_stocks.py @@ -11,8 +11,8 @@ get_market_iex_volume, get_market_iex_percent, get_market_in_focus, get_sector_performance, get_collections, get_crypto_quotes, - get_todays_earnings, get_ipo_calendar) -from iexfinance import Stock + get_todays_earnings, get_ipo_calendar, + get_historical_intraday, Stock) from iexfinance.utils.exceptions import IEXSymbolError, IEXEndpointError import six @@ -1012,18 +1012,16 @@ def test_invalid_dates_batch(self): with pytest.raises(ValueError): get_historical_data(["AAPL", "TSLA"], start, end) - def test_invalid_symbol_single(self): - start = datetime(2017, 2, 9) - end = datetime(2017, 5, 24) - with pytest.raises(IEXSymbolError): - get_historical_data("BADSYMBOL", start, end) - def test_invalid_symbol_batch(self): start = datetime(2017, 2, 9) end = datetime(2017, 5, 24) with pytest.raises(IEXSymbolError): get_historical_data(["BADSYMBOL", "TSLA"], start, end) + def test_invalid_symbol_single(self): + with pytest.raises(IEXSymbolError): + get_historical_data("ZNWAA") + class TestMarketMovers(object): @@ -1145,3 +1143,32 @@ def test_get_ipo_calendar_today(self): def test_ipo_calendar_bad_period(self): with pytest.raises(ValueError): get_ipo_calendar("BADPERIOD") + + +class TestHistoricalIntraday(object): + + def test_intraday_fails_no_symbol(self): + with pytest.raises(TypeError): + get_historical_intraday() + + def test_intraday_default(self): + data = get_historical_intraday("AAPL") + + assert isinstance(data, list) + + def test_intraday_pandas(self): + data = get_historical_intraday("AAPL", output_format='pandas') + + assert isinstance(data, pd.DataFrame) + + def test_intraday_pass_date_str(self): + data = get_historical_intraday("AAPL", date="20181127") + + assert isinstance(data, list) + + def test_intraday_pass_datetime(self): + date = datetime(2018, 10, 27) + + data = get_historical_intraday("AAPL", date=date) + + assert isinstance(data, list)