Source code for pyEX.stocks.prices

# *****************************************************************************
#
# Copyright (c) 2020, the pyEX authors.
#
# This file is part of the pyEX library, distributed under the terms of
# the Apache License 2.0.  The full license can be found in the LICENSE file.
#
from functools import wraps

import pandas as pd

from ..common import (
    _EST,
    _TIMEFRAME_CHART,
    PyEXception,
    _expire,
    _get,
    _quoteSymbols,
    _raiseIfNotStr,
    _reindex,
    _strOrDate,
    _toDatetime,
    json_normalize,
)


[docs]def book(symbol, token="", version="stable", filter="", format="json"): """Book data https://iextrading.com/developer/docs/#book realtime during Investors Exchange market hours Args: symbol (str): Ticker to request token (str): Access token version (str): API version filter (str): filters: https://iexcloud.io/docs/api/#filter-results format (str): return format, defaults to json Returns: dict or DataFrame: result """ _raiseIfNotStr(symbol) symbol = _quoteSymbols(symbol) return _get( "stock/{symbol}/book".format(symbol=_quoteSymbols(symbol)), token=token, version=version, filter=filter, format=format, )
def _bookToDF(b): """internal""" quote = b.get("quote", []) asks = b.get("asks", []) bids = b.get("bids", []) trades = b.get("trades", []) df1 = json_normalize(quote) df1["type"] = "quote" df2 = json_normalize(asks) df2["symbol"] = quote["symbol"] df2["type"] = "ask" df3 = json_normalize(bids) df3["symbol"] = quote["symbol"] df3["type"] = "bid" df4 = json_normalize(trades) df4["symbol"] = quote["symbol"] df3["type"] = "trade" df = pd.concat([df1, df2, df3, df4], sort=True) _toDatetime(df) return df
[docs]@wraps(book) def bookDF(*args, **kwargs): return _bookToDF(book(*args, **kwargs))
[docs]@_expire(hour=4, tz=_EST) def chart( symbol, timeframe="1m", date=None, exactDate=None, last=-1, closeOnly=False, byDay=False, simplify=False, interval=-1, changeFromClose=False, displayPercent=False, sort="desc", includeToday=False, token="", version="stable", filter="", format="json", ): """Historical price/volume data, daily and intraday https://iexcloud.io/docs/api/#historical-prices Data Schedule 1d: -9:30-4pm ET Mon-Fri on regular market trading days -9:30-1pm ET on early close trading days All others: -Prior trading day available after 4am ET Tue-Sat Args: symbol (str): Ticker to request timeframe (str): Timeframe to request e.g. 1m date (datetime): date, if requesting intraday exactDate (str): Same as `date`, takes precedence last (int): If passed, chart data will return the last N elements from the time period defined by the range parameter closeOnly (bool): Will return adjusted data only with keys date, close, and volume. byDay (bool): Used only when range is date to return OHLCV data instead of minute bar data. simplify (bool) If true, runs a polyline simplification using the Douglas-Peucker algorithm. This is useful if plotting sparkline charts. interval (int) If passed, chart data will return every Nth element as defined by chartInterval changeFromClose (bool): If true, changeOverTime and marketChangeOverTime will be relative to previous day close instead of the first value. displayPercent (bool): If set to true, all percentage values will be multiplied by a factor of 100 (Ex: /stock/twtr/chart?displayPercent=true) range (str): Same format as the path parameter. This can be used for batch calls. sort (str): Can be "asc" or "desc" to sort results by date. Defaults to "desc" includeToday (bool): If true, current trading day data is appended token (str): Access token version (str): API version filter (str): filters: https://iexcloud.io/docs/api/#filter-results format (str): return format, defaults to json Returns: dict or DataFrame: result """ _raiseIfNotStr(symbol) base_url = "stock/{}/chart/{}?".format(_quoteSymbols(symbol), timeframe) # exactDate takes precedence date = exactDate or date if date: date = _strOrDate(date) if timeframe is not None and timeframe != "1d": if timeframe not in _TIMEFRAME_CHART: raise PyEXception("Range must be in {}".format(_TIMEFRAME_CHART)) # Assemble params params = {} # TODO need these? # if date: # params["exactDate"] = date # if range: # params["range"] = range if last > 0: params["chartLast"] = last if closeOnly: params["chartCloseOnly"] = closeOnly if byDay: params["chartByDay"] = byDay if simplify: params["chartSimplify"] = simplify if interval > 0: params["chartInterval"] = interval if changeFromClose: params["changeFromClose"] = changeFromClose if displayPercent: params["displayPercent"] = displayPercent if exactDate: params["exactDate"] = exactDate if sort: if sort.lower() not in ( "asc", "desc", ): raise PyEXception("Sort must be in (asc, desc), got: {}".format(sort)) params["sort"] = sort.lower() if includeToday: params["includeToday"] = includeToday if date: base_url = "stock/{}/chart/date/{}?".format(_quoteSymbols(symbol), date) if params: base_url += "&".join("{}={}".format(k, v) for k, v in params.items()) return _get( base_url, token=token, version=version, filter=filter, format=format ) if params: base_url += "&".join("{}={}".format(k, v) for k, v in params.items()) return _get(base_url, token=token, version=version, filter=filter, format=format)
def _chartToDF(c): """internal""" return _reindex(_toDatetime(pd.DataFrame(c)), "date")
[docs]@wraps(chart) def chartDF( symbol, timeframe="1m", date=None, exactDate=None, last=-1, closeOnly=False, byDay=False, simplify=False, interval=-1, changeFromClose=False, displayPercent=False, sort="desc", includeToday=False, token="", version="stable", filter="", format="json", ): c = chart( symbol=symbol, timeframe=timeframe, date=date, exactDate=exactDate, last=last, closeOnly=closeOnly, byDay=byDay, simplify=simplify, interval=interval, changeFromClose=changeFromClose, displayPercent=displayPercent, sort=sort, includeToday=includeToday, token=token, version=version, filter=filter, format=format, ) df = _toDatetime(pd.DataFrame(c)) if timeframe is not None and timeframe != "1d": _reindex(df, "date") else: if not df.empty and "date" in df.columns and "minute" in df.columns: df.set_index(["date", "minute"], inplace=True) elif not df.empty and "date" in df.columns: _reindex(df, "date") elif not df.empty: # Nothing to do ... else: df = pd.DataFrame() return df
[docs]@_expire(second=0) def delayedQuote(symbol, token="", version="stable", filter="", format="json"): """This returns the 15 minute delayed market quote. https://iexcloud.io/docs/api/#delayed-quote 15min delayed 4:30am - 8pm ET M-F when market is open Args: symbol (str): Ticker to request token (str): Access token version (str): API version filter (str): filters: https://iexcloud.io/docs/api/#filter-results format (str): return format, defaults to json Returns: dict or DataFrame: result """ _raiseIfNotStr(symbol) return _get( "stock/{symbol}/delayed-quote".format(symbol=_quoteSymbols(symbol)), token=token, version=version, filter=filter, format=format, )
[docs]@wraps(delayedQuote) def delayedQuoteDF(*args, **kwargs): return _reindex( _toDatetime(json_normalize(delayedQuote(*args, **kwargs))), "symbol" )
[docs]def intraday( symbol, date="", exactDate="", last=-1, IEXOnly=False, reset=False, simplify=False, interval=-1, changeFromClose=False, IEXWhenNull=False, token="", version="stable", filter="", format="json", ): """This endpoint will return aggregated intraday prices in one minute buckets https://iexcloud.io/docs/api/#intraday-prices 9:30-4pm ET Mon-Fri on regular market trading days 9:30-1pm ET on early close trading days Args: symbol (str): Ticker to request date (str): Formatted as YYYYMMDD. This can be used for batch calls when range is 1d or date. Currently supporting trailing 30 calendar days of minute bar data. exactDate (str): Same as `date`, takes precedence last (number): If passed, chart data will return the last N elements IEXOnly (bool): Limits the return of intraday prices to IEX only data. reset (bool): If true, chart will reset at midnight instead of the default behavior of 9:30am ET. simplify (bool): If true, runs a polyline simplification using the Douglas-Peucker algorithm. This is useful if plotting sparkline charts. interval (number): If passed, chart data will return every Nth element as defined by chartInterval changeFromClose (bool): If true, changeOverTime and marketChangeOverTime will be relative to previous day close instead of the first value. IEXWhenNull (bool): By default, all market prefixed fields are 15 minute delayed, meaning the most recent 15 objects will be null. If this parameter is passed as true, all market prefixed fields that are null will be populated with IEX data if available. token (str): Access token version (str): API version filter (str): filters: https://iexcloud.io/docs/api/#filter-results format (str): return format, defaults to json Returns: dict or DataFrame: result """ _raiseIfNotStr(symbol) symbol = _quoteSymbols(symbol) # exactDate takes precedence date = exactDate or date if date: date = _strOrDate(date) # Assemble params params = {} if date: params["exactDate"] = date if last > 0: params["chartLast"] = last if IEXOnly: params["chartIEXOnly"] = IEXOnly if reset: params["chartReset"] = reset if simplify: params["chartSimplify"] = simplify if interval > 0: params["chartInterval"] = interval if changeFromClose: params["changeFromClose"] = changeFromClose if IEXWhenNull: params["chartIEXWhenNull"] = IEXWhenNull base_url = "stock/{}/intraday-prices?".format(symbol) if params: base_url += "&".join("{}={}".format(k, v) for k, v in params.items()) return _get(base_url, token=token, version=version, filter=filter, format=format)
[docs]@wraps(intraday) def intradayDF(*args, **kwargs): val = intraday(*args, **kwargs) df = _toDatetime(pd.DataFrame(val)) if not df.empty and "date" in df.columns and "minute" in df.columns: df.set_index(["date", "minute"], inplace=True) elif not df.empty and "date" in df.columns: _reindex(df, "date") else: df = pd.DataFrame() return df
[docs]def largestTrades(symbol, token="", version="stable", filter="", format="json"): """This returns 15 minute delayed, last sale eligible trades. https://iexcloud.io/docs/api/#largest-trades 9:30-4pm ET M-F during regular market hours Args: symbol (str): Ticker to request token (str): Access token version (str): API version filter (str): filters: https://iexcloud.io/docs/api/#filter-results format (str): return format, defaults to json Returns: dict or DataFrame: result """ _raiseIfNotStr(symbol) return _get( "stock/{symbol}/largest-trades".format(symbol=_quoteSymbols(symbol)), token=token, version=version, filter=filter, format=format, )
[docs]@wraps(largestTrades) def largestTradesDF(*args, **kwargs): return _reindex(_toDatetime(pd.DataFrame(largestTrades(*args, **kwargs))), "time")
[docs]def ohlc(symbol, token="", version="stable", filter="", format="json"): """Returns the official open and close for a give symbol. https://iexcloud.io/docs/api/#ohlc 9:30am-5pm ET Mon-Fri Args: symbol (str): Ticker to request token (str): Access token version (str): API version filter (str): filters: https://iexcloud.io/docs/api/#filter-results format (str): return format, defaults to json Returns: dict or DataFrame: result """ _raiseIfNotStr(symbol) return _get( "stock/{symbol}/ohlc".format(symbol=_quoteSymbols(symbol)) + symbol + "/ohlc", token=token, version=version, filter=filter, format=format, )
[docs]@wraps(ohlc) def ohlcDF(*args, **kwargs): o = ohlc(*args, **kwargs) if o: df = json_normalize(o) _toDatetime(df) else: df = pd.DataFrame() return df
[docs]@_expire(hour=4, tz=_EST) def yesterday(symbol, token="", version="stable", filter="", format="json"): """This returns previous day adjusted price data for one or more stocks https://iexcloud.io/docs/api/#previous-day-prices Available after 4am ET Tue-Sat Args: symbol (str): Ticker to request token (str): Access token version (str): API version filter (str): filters: https://iexcloud.io/docs/api/#filter-results format (str): return format, defaults to json Returns: dict or DataFrame: result """ _raiseIfNotStr(symbol) return _get( "stock/{symbol}/previous".format(symbol=_quoteSymbols(symbol)), token=token, version=version, filter=filter, format=format, )
previous = yesterday
[docs]@wraps(yesterday) def yesterdayDF(*args, **kwargs): y = yesterday(*args, **kwargs) if y: df = _reindex(_toDatetime(json_normalize(y)), "symbol") else: df = pd.DataFrame() return df
previousDF = yesterdayDF
[docs]def price(symbol, token="", version="stable", filter="", format="json"): """Price of ticker https://iexcloud.io/docs/api/#price 4:30am-8pm ET Mon-Fri Args: symbol (str): Ticker to request token (str): Access token version (str): API version filter (str): filters: https://iexcloud.io/docs/api/#filter-results format (str): return format, defaults to json Returns: dict or DataFrame: result """ _raiseIfNotStr(symbol) return _get( "stock/{symbol}/price".format(symbol=_quoteSymbols(symbol)), token=token, version=version, filter=filter, format=format, )
[docs]@wraps(price) def priceDF(*args, **kwargs): return _toDatetime(json_normalize({"price": price(*args, **kwargs)}))
[docs]def quote(symbol, token="", version="stable", filter="", format="json"): """Get quote for ticker https://iexcloud.io/docs/api/#quote 4:30am-8pm ET Mon-Fri Args: symbol (str): Ticker to request token (str): Access token version (str): API version filter (str): filters: https://iexcloud.io/docs/api/#filter-results format (str): return format, defaults to json Returns: dict or DataFrame: result """ _raiseIfNotStr(symbol) return _get( "stock/{symbol}/quote".format(symbol=_quoteSymbols(symbol)), token=token, version=version, filter=filter, format=format, )
[docs]@wraps(quote) def quoteDF(*args, **kwargs): q = quote(*args, **kwargs) if q: df = _reindex(_toDatetime(json_normalize(q)), "symbol") else: df = pd.DataFrame() return df
[docs]@_expire(hour=8, tz=_EST) def spread(symbol, token="", version="stable", filter="", format="json"): """This returns an array of effective spread, eligible volume, and price improvement of a stock, by market. Unlike volume-by-venue, this will only return a venue if effective spread is not ‘N/A’. Values are sorted in descending order by effectiveSpread. Lower effectiveSpread and higher priceImprovement values are generally considered optimal. Effective spread is designed to measure marketable orders executed in relation to the market center’s quoted spread and takes into account hidden and midpoint liquidity available at each market center. Effective Spread is calculated by using eligible trade prices recorded to the consolidated tape and comparing those trade prices to the National Best Bid and Offer (“NBBO”) at the time of the execution. View the data disclaimer at the bottom of the stocks app for more information about how these values are calculated. 8am ET M-F Args: symbol (str): Ticker to request token (str): Access token version (str): API version filter (str): filters: https://iexcloud.io/docs/api/#filter-results format (str): return format, defaults to json Returns: dict or DataFrame: result """ _raiseIfNotStr(symbol) return _get( "stock/{symbol}/effective-spread".format(symbol=_quoteSymbols(symbol)), token=token, version=version, filter=filter, format=format, )
[docs]@wraps(spread) def spreadDF(*args, **kwargs): return _reindex(_toDatetime(pd.DataFrame(spread(*args, **kwargs))), "venue")
[docs]def volumeByVenue(symbol, token="", version="stable", filter="", format="json"): """This returns 15 minute delayed and 30 day average consolidated volume percentage of a stock, by market. This call will always return 13 values, and will be sorted in ascending order by current day trading volume percentage. https://iexcloud.io/docs/api/#volume-by-venue Updated during regular market hours 9:30am-4pm ET Args: symbol (str): Ticker to request token (str): Access token version (str): API version filter (str): filters: https://iexcloud.io/docs/api/#filter-results format (str): return format, defaults to json Returns: dict or DataFrame: result """ _raiseIfNotStr(symbol) return _get( "stock/{symbol}/volume-by-venue".format(symbol=_quoteSymbols(symbol)), token=token, version=version, filter=filter, format=format, )
[docs]@wraps(volumeByVenue) def volumeByVenueDF(*args, **kwargs): return _reindex(_toDatetime(pd.DataFrame(volumeByVenue(*args, **kwargs))), "venue")