mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-31 22:41:19 +00:00
* small changes - moving provider inside the core * uptade readme * lint * remove old provider readme * leftovers * refactor website examples * ruff * fix nox * relock * fix fails_to_deliver headers * fix for tests that have renamed endpoints * fix discovery * removed test for renamed endpoint * fix broken syntax - test_search_financial_attributes * fix cash/income/balance * fix derivatives tests * fix etf tests * fix news tests * fix regulators * black * wrong test param * sort equity gainers/losers by percent * platform markdown generation import * Hotfix/fix int tests core merge (#5779) * removing tests on old endpoint * removing visualization on old endpoint * fixing equity tests * fixing examples * fix news tests * fix index test * example * add comments * example --------- Co-authored-by: Diogo Sousa <montezdesousa@gmail.com> --------- Co-authored-by: montezdesousa <79287829+montezdesousa@users.noreply.github.com> Co-authored-by: Diogo Sousa <montezdesousa@gmail.com> Co-authored-by: Danglewood <85772166+deeleeramone@users.noreply.github.com> Co-authored-by: Pratyush Shukla <ps4534@nyu.edu>
398 lines
11 KiB
Python
398 lines
11 KiB
Python
"""CBOE Helpers Module."""
|
|
|
|
from datetime import date, timedelta
|
|
from io import BytesIO, StringIO
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
import pandas as pd
|
|
import requests
|
|
import requests_cache
|
|
from openbb_core.app.utils import get_user_cache_directory
|
|
from openbb_core.provider.utils.helpers import to_snake_case
|
|
|
|
cache_dir = get_user_cache_directory()
|
|
cboe_session = requests_cache.CachedSession(
|
|
f"{cache_dir}/http/cboe_directories", expire_after=timedelta(days=7)
|
|
)
|
|
|
|
TICKER_EXCEPTIONS = ["NDX", "RUT"]
|
|
|
|
US_INDEX_COLUMNS = {
|
|
"symbol": "symbol",
|
|
"name": "name",
|
|
"current_price": "price",
|
|
"open": "open",
|
|
"high": "high",
|
|
"low": "low",
|
|
"close": "close",
|
|
"prev_day_close": "prev_close",
|
|
"price_change": "change",
|
|
"price_change_percent": "change_percent",
|
|
"last_trade_time": "last_trade_timestamp",
|
|
}
|
|
|
|
EUR_INDEX_COLUMNS = {
|
|
"name": "name",
|
|
"isin": "isin",
|
|
"currency": "currency",
|
|
"current_price": "price",
|
|
"open": "open",
|
|
"high": "high",
|
|
"low": "low",
|
|
"close": "close",
|
|
"prev_day_close": "prev_close",
|
|
"price_change": "change",
|
|
"price_change_percent": "change_percent",
|
|
"last_trade_time": "last_trade_timestamp",
|
|
}
|
|
|
|
EUR_INDEX_CONSTITUENTS_COLUMNS = {
|
|
"symbol": "symbol",
|
|
"current_price": "price",
|
|
"open": "open",
|
|
"high": "high",
|
|
"low": "low",
|
|
"close": "close",
|
|
"volume": "volume",
|
|
"seqno": "seqno",
|
|
"prev_day_close": "prev_close",
|
|
"price_change": "change",
|
|
"price_change_percent": "change_percent",
|
|
"last_trade_time": "last_trade_timestamp",
|
|
"exchange_id": "exchange_id",
|
|
"tick": "tick",
|
|
"security_type": "type",
|
|
}
|
|
|
|
|
|
def get_cboe_directory(**kwargs) -> pd.DataFrame:
|
|
"""Get the US Listings Directory for the CBOE.
|
|
|
|
Returns
|
|
-------
|
|
pd.DataFrame: CBOE_DIRECTORY
|
|
DataFrame of the CBOE listings directory
|
|
"""
|
|
|
|
r = cboe_session.get(
|
|
"https://www.cboe.com/us/options/symboldir/equity_index_options/?download=csv",
|
|
timeout=10,
|
|
)
|
|
|
|
if r.status_code != 200:
|
|
raise RuntimeError(r.status_code)
|
|
|
|
r_json = StringIO(r.text)
|
|
|
|
CBOE_DIRECTORY = pd.read_csv(r_json)
|
|
|
|
CBOE_DIRECTORY = CBOE_DIRECTORY.rename(
|
|
columns={
|
|
" Stock Symbol": "symbol",
|
|
" DPM Name": "dpm_name",
|
|
" Post/Station": "post_station",
|
|
"Company Name": "name",
|
|
}
|
|
).set_index("symbol")
|
|
|
|
return CBOE_DIRECTORY.astype(str)
|
|
|
|
|
|
def get_cboe_index_directory(**kwargs) -> pd.DataFrame:
|
|
"""Get the US Listings Directory for the CBOE
|
|
|
|
Returns
|
|
-------
|
|
pd.DataFrame: CBOE_INDEXES
|
|
"""
|
|
|
|
r = cboe_session.get(
|
|
"https://cdn.cboe.com/api/global/us_indices/definitions/all_indices.json",
|
|
timeout=10,
|
|
)
|
|
|
|
if r.status_code != 200:
|
|
raise requests.HTTPError(r.status_code)
|
|
|
|
CBOE_INDEXES = pd.DataFrame(r.json())
|
|
|
|
CBOE_INDEXES = CBOE_INDEXES.rename(
|
|
columns={
|
|
"calc_end_time": "close_time",
|
|
"calc_start_time": "open_time",
|
|
"index_symbol": "symbol",
|
|
"mkt_data_delay": "data_delay",
|
|
},
|
|
)
|
|
|
|
indices_order: List[str] = [
|
|
"symbol",
|
|
"name",
|
|
"description",
|
|
"currency",
|
|
"open_time",
|
|
"close_time",
|
|
"tick_days",
|
|
"tick_frequency",
|
|
"tick_period",
|
|
"time_zone",
|
|
"data_delay",
|
|
]
|
|
|
|
CBOE_INDEXES = pd.DataFrame(CBOE_INDEXES, columns=indices_order).set_index("symbol")
|
|
|
|
idx = (
|
|
(CBOE_INDEXES.time_zone.str.contains("America/Chicago"))
|
|
& (CBOE_INDEXES.currency.str.contains("USD"))
|
|
& (CBOE_INDEXES.description.str.contains("Daily Risk Control") == 0)
|
|
& (CBOE_INDEXES.name.str.contains("OEX%-SML% INDEX") == 0)
|
|
& (CBOE_INDEXES.name.str.contains("Defined Risk Volatility Income") == 0)
|
|
& (CBOE_INDEXES.description.str.contains("is designed to track") == 0)
|
|
)
|
|
|
|
return CBOE_INDEXES[idx]
|
|
|
|
|
|
def get_ticker_info(symbol: str, **kwargs) -> Dict[str, Any]:
|
|
symbol = symbol.upper()
|
|
SYMBOLS = get_cboe_directory()
|
|
INDEXES = get_cboe_index_directory()
|
|
data: Dict[str, Any] = {}
|
|
|
|
if symbol not in SYMBOLS.index and symbol not in INDEXES.index:
|
|
raise RuntimeError(f"Data not found for: {symbol}")
|
|
|
|
url = (
|
|
f"https://cdn.cboe.com/api/global/delayed_quotes/quotes/_{symbol}.json"
|
|
if symbol in INDEXES.index or symbol in TICKER_EXCEPTIONS
|
|
else f"https://cdn.cboe.com/api/global/delayed_quotes/quotes/{symbol}.json"
|
|
)
|
|
|
|
r = requests.get(url, timeout=10)
|
|
|
|
if r.status_code not in set([200, 403]):
|
|
raise requests.HTTPError(r.status_code)
|
|
|
|
if r.status_code == 403:
|
|
raise RuntimeError(f"Data was not found for, {symbol}.")
|
|
|
|
data = r.json()["data"]
|
|
|
|
if symbol in SYMBOLS.index.to_list():
|
|
data.update({"name": SYMBOLS.at[symbol, "name"]})
|
|
if symbol in INDEXES.index.to_list():
|
|
data.update({"name": INDEXES.at[symbol, "name"]})
|
|
|
|
_data = pd.DataFrame.from_dict(data, orient="index").transpose()
|
|
|
|
_data = _data.rename(
|
|
columns={
|
|
"current_price": "price",
|
|
"prev_day_close": "prev_close",
|
|
"price_change": "change",
|
|
"price_change_percent": "change_percent",
|
|
"last_trade_time": "last_trade_timestamp",
|
|
"security_type": "type",
|
|
}
|
|
)
|
|
|
|
return _data.transpose()[0].to_dict()
|
|
|
|
|
|
def get_ticker_iv(symbol: str, **kwargs) -> Dict[str, float]:
|
|
"""Get annualized high/low historical and implied volatility over 30/60/90 day windows.
|
|
|
|
Parameters
|
|
----------
|
|
symbol: str
|
|
The loaded ticker
|
|
|
|
Returns
|
|
-------
|
|
pd.DataFrame: ticker_iv
|
|
"""
|
|
|
|
symbol = symbol.upper()
|
|
|
|
INDEXES = get_cboe_index_directory().index.to_list()
|
|
|
|
quotes_iv_url = (
|
|
"https://cdn.cboe.com/api/global/delayed_quotes/historical_data/_"
|
|
f"{symbol}"
|
|
".json"
|
|
if symbol in TICKER_EXCEPTIONS or symbol in INDEXES
|
|
else f"https://cdn.cboe.com/api/global/delayed_quotes/historical_data/{symbol}.json"
|
|
)
|
|
|
|
h_iv = requests.get(quotes_iv_url, timeout=10)
|
|
|
|
if h_iv.status_code not in set([200, 403]):
|
|
raise requests.HTTPError(h_iv.status_code)
|
|
|
|
if h_iv.status_code == 403:
|
|
raise RuntimeError(f"Data was not found for, {symbol}.")
|
|
|
|
data = pd.DataFrame(h_iv.json())[2:-1]["data"].rename(f"{symbol}")
|
|
|
|
return data.to_dict()
|
|
|
|
|
|
def list_futures(**kwargs) -> List[dict]:
|
|
"""List of CBOE futures and their underlying symbols.
|
|
|
|
Returns
|
|
--------
|
|
pd.DataFrame
|
|
Pandas DataFrame with results.
|
|
"""
|
|
|
|
r = requests.get(
|
|
"https://cdn.cboe.com/api/global/delayed_quotes/symbol_book/futures-roots.json",
|
|
timeout=10,
|
|
)
|
|
|
|
if r.status_code != 200:
|
|
raise requests.HTTPError(r.status_code)
|
|
|
|
return r.json()["data"]
|
|
|
|
|
|
def get_settlement_prices(
|
|
settlement_date: Optional[date] = None,
|
|
options: bool = False,
|
|
archives: bool = False,
|
|
final_settlement: bool = False,
|
|
**kwargs,
|
|
) -> pd.DataFrame:
|
|
"""Gets the settlement prices of CBOE futures.
|
|
|
|
Parameters
|
|
-----------
|
|
settlement_date: Optional[date]
|
|
The settlement date. Only valid for active contracts. [YYYY-MM-DD]
|
|
options: bool
|
|
If true, returns options on futures.
|
|
archives: bool
|
|
Settlement price archives for select years and products. Overridden by other parameters.
|
|
final_settlement: bool
|
|
Final settlement prices for expired contracts. Overrides archives.
|
|
|
|
Returns
|
|
-------
|
|
pd.DataFrame
|
|
Pandas DataFrame with results.
|
|
"""
|
|
url = ""
|
|
if archives is True:
|
|
url = "https://cdn.cboe.com/resources/futures/archive/volume-and-price/CFE_FinalSettlement_Archive.csv"
|
|
|
|
if settlement_date is not None:
|
|
url = f"https://www.cboe.com/us/futures/market_statistics/settlement/csv?dt={settlement_date}"
|
|
if options is True:
|
|
url = f"https://www.cboe.com/us/futures/market_statistics/settlement/csv?options=t&dt={settlement_date}"
|
|
|
|
if settlement_date is None:
|
|
url = "https://www.cboe.com/us/futures/market_statistics/settlement/csv"
|
|
if options is True:
|
|
url = "https://www.cboe.com/us/futures/market_statistics/settlement/csv?options=t"
|
|
|
|
if final_settlement is True:
|
|
url = "https://www.cboe.com/us/futures/market_statistics/final_settlement_prices/csv/"
|
|
|
|
r = requests.get(url, timeout=10)
|
|
|
|
if r.status_code != 200:
|
|
raise RuntimeError(r.status_code)
|
|
|
|
data = pd.read_csv(BytesIO(r.content), index_col=None, parse_dates=True)
|
|
|
|
if data.empty:
|
|
error_string = (
|
|
f"No results found for, {settlement_date}."
|
|
if settlement_date is not None
|
|
else "No results found."
|
|
)
|
|
raise RuntimeError(error_string)
|
|
|
|
data.columns = [to_snake_case(c) for c in data.columns]
|
|
data = data.rename(columns={"expiration_date": "expiration"})
|
|
|
|
if len(data) > 0:
|
|
return data
|
|
|
|
return pd.DataFrame()
|
|
|
|
|
|
class Europe:
|
|
"""Class for European CBOE data."""
|
|
|
|
@staticmethod
|
|
def get_all_index_definitions(**kwargs) -> Dict[Any, Any]:
|
|
"""Get the full list of European index definitions.
|
|
|
|
Returns
|
|
-------
|
|
Dict[Any, Any]
|
|
Dictionary with results.
|
|
"""
|
|
|
|
r = cboe_session.get(
|
|
"https://cdn.cboe.com/api/global/european_indices/definitions/all-definitions.json",
|
|
timeout=10,
|
|
)
|
|
|
|
if r.status_code != 200:
|
|
raise requests.HTTPError(r.status_code)
|
|
return r.json()["data"]
|
|
|
|
@staticmethod
|
|
def list_indices(**kwargs) -> List[Dict]:
|
|
"""Gets names, currencies, ISINs, regions, and symbols for European indices.
|
|
|
|
Returns
|
|
-------
|
|
Dict[str, str]
|
|
List of dictionaries with the results.
|
|
"""
|
|
|
|
data = Europe.get_all_index_definitions()
|
|
data = (
|
|
pd.DataFrame.from_records(pd.DataFrame(data)["index"])
|
|
.drop(columns=["short_name"])
|
|
.rename(columns={"long_name": "name"})
|
|
)
|
|
return data.to_dict("records")
|
|
|
|
@staticmethod
|
|
def list_index_constituents(symbol: str, **kwargs) -> List[str]:
|
|
"""List symbols for constituents of a European index.
|
|
|
|
Parameters
|
|
----------
|
|
symbol: str
|
|
The symbol of the index.
|
|
|
|
Returns
|
|
-------
|
|
List[str]
|
|
List of constituents as ticker symbols.
|
|
"""
|
|
|
|
SYMBOLS = pd.DataFrame(Europe.list_indices())["symbol"].to_list()
|
|
symbol = symbol.upper()
|
|
|
|
if symbol not in SYMBOLS:
|
|
raise RuntimeError(
|
|
f"The symbol, {symbol}, was not found in the CBOE European Index directory.",
|
|
)
|
|
|
|
url = f"https://cdn.cboe.com/api/global/european_indices/definitions/{symbol}.json"
|
|
r = requests.get(url, timeout=10)
|
|
|
|
if r.status_code != 200:
|
|
raise requests.HTTPError(r.status_code)
|
|
|
|
r_json = r.json()["constituents"]
|
|
|
|
return [r_json[i]["constituent_symbol"] for i in range(0, len(r_json))]
|