Files
OpenBB/openbb_platform/providers/cboe/openbb_cboe/utils/helpers.py
Henrique Joaquim 4400cd9a8f Merge provider and core into core (#5774)
* 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>
2023-11-22 13:48:01 +00:00

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))]