mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-02 15:32:04 +00:00
* Began the fax * Bumped ruff * Fixed a BUNCH of ruff errors * Fixed a lot of ruff errors * Simplify if statements * Fixed black * Updated some stuff * Updated some stuff * A lot more ruff fixes * Added a fix * Added a fix * Fixed mypy * Fixed some stuff * Reverted one thing * Small fix * Small fix * Small fix * Added fix * Added fix * Update requirements
955 lines
35 KiB
Python
955 lines
35 KiB
Python
# type: ignore
|
|
# pylint: disable=[W0621,R1714]
|
|
from typing import Any, Tuple
|
|
|
|
import pandas as pd
|
|
import streamlit as st
|
|
|
|
from openbb_terminal.core.plots.plotly_helper import OpenBBFigure
|
|
from openbb_terminal.core.session.current_system import set_system_variable
|
|
from openbb_terminal.dashboards.stream import streamlit_helpers as st_helpers
|
|
from openbb_terminal.stocks.options.cboe_model import INDEXES, SYMBOLS
|
|
from openbb_terminal.stocks.options.options_chains_model import (
|
|
get_nearest_call_strike,
|
|
get_nearest_otm_strike,
|
|
get_nearest_put_strike,
|
|
)
|
|
from openbb_terminal.stocks.options.options_sdk_helper import (
|
|
OptionsChains,
|
|
load_options_chains,
|
|
)
|
|
|
|
EXCEPTIONS = ["NDX", "RUT"]
|
|
analysis_type: str = ""
|
|
strike_price: float = 0
|
|
moneyness: float = 0
|
|
strangle_moneyness: float = 0
|
|
price_data = pd.DataFrame()
|
|
strategy_data = pd.DataFrame()
|
|
title: str = ""
|
|
_strikes: Any = None
|
|
chart_data_type: str = ""
|
|
target_column: dict[str, str] = {
|
|
"Last Price": "lastTradePrice",
|
|
"Bid Price": "bid",
|
|
"Ask Price": "ask",
|
|
"Theoretical Price": "theoretical",
|
|
"Breakeven Price": "Breakeven",
|
|
"Open Interest": "openInterest",
|
|
"Volume": "volume",
|
|
"Implied Volatility": "impliedVolatility",
|
|
"Gamma": "gamma",
|
|
"Theta": "theta",
|
|
"Vega": "vega",
|
|
"Delta Dollars": "DEX",
|
|
"GEX Per 1% Move": "GEX",
|
|
}
|
|
strategy_target_column_dict = {
|
|
"Cost of Position": "Cost",
|
|
"Cost as % of Underlying": "Cost Percent",
|
|
}
|
|
surface_choice_dict = {
|
|
"OTM Only": "otm",
|
|
"ITM Only": "itm",
|
|
"Calls Only": "calls",
|
|
"Puts Only": "puts",
|
|
}
|
|
chains_df = pd.DataFrame()
|
|
symbol_choices = SYMBOLS.sort_index().reset_index()
|
|
default_symbol = int(symbol_choices.query("`Symbol` == 'SPY'").index[0])
|
|
chains_strikes: dict[str, float] = {}
|
|
|
|
pd.options.plotting.backend = "plotly"
|
|
# Suppressing sdk logs
|
|
set_system_variable("LOGGING_SUPPRESS", True)
|
|
|
|
st.set_page_config(
|
|
layout="wide",
|
|
page_title="CBOE Options Dashboard",
|
|
initial_sidebar_state="expanded",
|
|
menu_items={
|
|
"Get help": "https://discord.com/invite/Y4HDyB6Ypu",
|
|
},
|
|
)
|
|
|
|
st_helpers.set_current_page("Options")
|
|
st_helpers.set_css()
|
|
|
|
|
|
def format_plotly(fig: OpenBBFigure):
|
|
fig.update_layout(
|
|
margin=dict(t=40),
|
|
autosize=False,
|
|
width=1050,
|
|
height=700,
|
|
title=dict(y=0.99, x=0.5, xanchor="center", yanchor="top", automargin=True),
|
|
legend=dict(bgcolor="rgba(0,0,0,0)"),
|
|
)
|
|
return fig
|
|
|
|
|
|
@st.cache_data(show_spinner=False, experimental_allow_widgets=True)
|
|
def load_data(ticker):
|
|
df = load_options_chains(ticker)
|
|
|
|
_calls = df.chains[df.chains["optionType"] == "call"].copy()
|
|
_puts = df.chains[df.chains["optionType"] == "put"].copy()
|
|
|
|
_calls.loc[:, ("$ To Spot")] = (
|
|
(_calls.loc[:, ("strike")]) + (_calls.loc[:, ("ask")]) - (df.last_price)
|
|
)
|
|
_calls.loc[:, ("% To Spot")] = (_calls.loc[:, ("$ To Spot")] / df.last_price) * 100
|
|
_calls.loc[:, ("Breakeven")] = _calls.loc[:, ("strike")] + _calls.loc[:, ("ask")]
|
|
_calls.loc[:, ("DEX")] = (
|
|
(_calls.loc[:, ("delta")] * 100)
|
|
* (_calls.loc[:, ("openInterest")])
|
|
* df.last_price
|
|
)
|
|
_calls["DEX"] = _calls["DEX"].convert_dtypes(convert_floating=True)
|
|
_calls.loc[:, ("GEX")] = (
|
|
_calls.loc[:, ("gamma")]
|
|
* 100
|
|
* _calls.loc[:, ("openInterest")]
|
|
* (df.last_price * df.last_price)
|
|
* 0.01
|
|
)
|
|
_calls.GEX = _calls.GEX.convert_dtypes(convert_floating=True)
|
|
_calls.set_index(keys=["expiration", "strike", "optionType"], inplace=True)
|
|
|
|
_puts.loc[:, ("$ To Spot")] = (
|
|
(_puts.loc[:, ("strike")]) - (_puts.loc[:, ("ask")]) - (df.last_price)
|
|
)
|
|
_puts.loc[:, ("% To Spot")] = (_puts.loc[:, ("$ To Spot")] / df.last_price) * 100
|
|
_puts.loc[:, ("Breakeven")] = _puts.loc[:, ("strike")] - _puts.loc[:, ("ask")]
|
|
_puts.loc[:, ("DEX")] = (
|
|
(_puts.loc[:, ("delta")] * 100)
|
|
* (_puts.loc[:, ("openInterest")])
|
|
* df.last_price
|
|
)
|
|
_puts.loc[:, ("GEX")] = (
|
|
_puts.loc[:, ("gamma")]
|
|
* 100
|
|
* _puts.loc[:, ("openInterest")]
|
|
* (df.last_price * df.last_price)
|
|
* 0.01
|
|
* (-1)
|
|
)
|
|
_puts.GEX = _puts.GEX.convert_dtypes(convert_floating=True)
|
|
_puts["DEX"] = _puts["DEX"].convert_dtypes(convert_floating=True)
|
|
_puts.set_index(keys=["expiration", "strike", "optionType"], inplace=True)
|
|
|
|
_calls.GEX = round(_calls.GEX, ndigits=2)
|
|
_calls["DEX"] = round(_calls["DEX"], ndigits=2)
|
|
_puts.GEX = round(_puts.GEX, ndigits=2)
|
|
_puts["DEX"] = round(_puts["DEX"], ndigits=2)
|
|
|
|
df.chains = pd.concat([_puts, _calls])
|
|
df.chains.sort_index(inplace=True)
|
|
df.chains.reset_index(inplace=True)
|
|
|
|
return df
|
|
|
|
|
|
def get_exposure(df: OptionsChains) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
|
by_strike = pd.DataFrame()
|
|
by_expiration = pd.DataFrame()
|
|
puts_by_strike = (
|
|
df.chains.query("`optionType` == 'put'").groupby("strike")[["DEX", "GEX"]].sum()
|
|
)
|
|
puts_by_strike.columns = ["Put DEX", "Put GEX"]
|
|
calls_by_strike = (
|
|
df.chains.query("`optionType` == 'call'")
|
|
.groupby("strike")[["DEX", "GEX"]]
|
|
.sum()
|
|
)
|
|
calls_by_strike.columns = ["Call DEX", "Call GEX"]
|
|
by_strike = pd.concat([calls_by_strike, puts_by_strike], axis=1)
|
|
puts_by_expiration = (
|
|
df.chains.query("`optionType` == 'put'")
|
|
.groupby("expiration")[["DEX", "GEX"]]
|
|
.sum()
|
|
)
|
|
puts_by_expiration.columns = ["Put DEX", "Put GEX"]
|
|
calls_by_expiration = (
|
|
df.chains.query("`optionType` == 'call'")
|
|
.groupby("expiration")[["DEX", "GEX"]]
|
|
.sum()
|
|
)
|
|
calls_by_expiration.columns = ["Call DEX", "Call GEX"]
|
|
by_expiration = pd.concat([calls_by_expiration, puts_by_expiration], axis=1)
|
|
return by_strike.astype("int64"), by_expiration.astype("int64")
|
|
|
|
|
|
def get_highest_oi(df: OptionsChains) -> pd.DataFrame:
|
|
highest_oi = df.chains[df.chains["openInterest"] == max(df.chains["openInterest"])][
|
|
[
|
|
"optionType",
|
|
"strike",
|
|
"dte",
|
|
"openInterest",
|
|
"volume",
|
|
"impliedVolatility",
|
|
"bid",
|
|
"ask",
|
|
"Breakeven",
|
|
]
|
|
]
|
|
highest_oi.columns = [
|
|
"Type",
|
|
"Strike",
|
|
"DTE",
|
|
"OI",
|
|
"Volume",
|
|
"IV",
|
|
"Bid",
|
|
"Ask",
|
|
"Breakeven",
|
|
]
|
|
return highest_oi
|
|
|
|
|
|
def get_highest_volume(df: OptionsChains) -> pd.DataFrame:
|
|
highest_volume = df.chains[df.chains["volume"] == max(df.chains["volume"])][
|
|
[
|
|
"optionType",
|
|
"strike",
|
|
"dte",
|
|
"openInterest",
|
|
"volume",
|
|
"impliedVolatility",
|
|
"bid",
|
|
"ask",
|
|
"Breakeven",
|
|
]
|
|
]
|
|
highest_volume.columns = [
|
|
"Type",
|
|
"Strike",
|
|
"DTE",
|
|
"OI",
|
|
"Volume",
|
|
"IV",
|
|
"Bid",
|
|
"Ask",
|
|
"Breakeven",
|
|
]
|
|
return highest_volume
|
|
|
|
|
|
with st.sidebar:
|
|
ticker = st.selectbox(
|
|
label="Ticker",
|
|
options=symbol_choices["Symbol"].tolist(),
|
|
index=default_symbol,
|
|
key="ticker",
|
|
)
|
|
df = load_data(ticker)
|
|
chains_df = df.chains
|
|
if ticker == "":
|
|
st.write("Please enter a symbol")
|
|
if hasattr(df, "underlying_name"):
|
|
ticker_good = True
|
|
stats = df.get_stats()
|
|
net_calls = stats.sum()["Calls OI"].astype(int)
|
|
net_puts = stats.sum()["Puts OI"].astype(int)
|
|
pcr_ratio = round((net_puts / net_calls), 4)
|
|
net_calls_vol = stats.sum()["Calls Volume"].astype(int)
|
|
net_puts_vol = stats.sum()["Puts Volume"].astype(int)
|
|
straddle_30d = df.get_straddle()
|
|
st.write(df.underlying_name)
|
|
st.write("Last Price: ", f"${df.last_price:0,.2f}")
|
|
_change = round(df.underlying_price["changePercent"], 2)
|
|
st.write("Change: ", f"{_change:0,.2f}%")
|
|
st.write(
|
|
f"Cost of {straddle_30d.loc['DTE']['Long Straddle']} DTE Straddle: ",
|
|
f"${round(straddle_30d.loc['Cost']['Long Straddle'], 2)}",
|
|
)
|
|
if df.underlying_price["ivThirty"] != 0:
|
|
st.write("IV 30: ", str(df.underlying_price["ivThirty"]))
|
|
if ticker.upper() not in INDEXES and ticker.upper() not in EXCEPTIONS:
|
|
st.write("Stock Volume :", format(df.underlying_price["volume"], ","))
|
|
st.write("Call Volume: ", format(net_calls_vol, ","))
|
|
st.write("Put Volume: ", format(net_puts_vol, ","))
|
|
st.write("Put/Call Ratio: ", str(pcr_ratio))
|
|
analysis_type = st.selectbox(
|
|
"Analysis Type", options=["Charts", "Tables"], key="analysis_type"
|
|
)
|
|
if analysis_type == "Tables":
|
|
table_choices = ["Chains", "Stats"]
|
|
table_choice = st.selectbox("Table Type", options=table_choices)
|
|
if table_choice == "Chains":
|
|
chains_selections = ["% OTM", "Delta", "None"]
|
|
chains_select = st.selectbox(
|
|
"Limit Range of Results",
|
|
options=chains_selections,
|
|
key="chains_select",
|
|
)
|
|
if chains_select == "% OTM":
|
|
chains_moneyness: float = st.number_input(
|
|
label="% OTM Range",
|
|
min_value=0.01,
|
|
max_value=99.99,
|
|
value=2.0,
|
|
step=0.25,
|
|
)
|
|
chains_strikes = get_nearest_otm_strike(df, chains_moneyness)
|
|
if chains_select == "Delta":
|
|
delta_choice: float = st.number_input(
|
|
label="Delta Value",
|
|
min_value=0.01,
|
|
max_value=1.0,
|
|
value=0.5,
|
|
step=0.05,
|
|
)
|
|
chains_choice = st.selectbox(
|
|
"Chain Type", options=["Calls", "Puts", "Both"], key="chains_choice"
|
|
)
|
|
expiration_choices = ["All"]
|
|
expiration_choices.extend(df.expirations)
|
|
expiration_choice = st.selectbox(
|
|
"Expiration Date",
|
|
options=expiration_choices,
|
|
key="expiration_choice",
|
|
)
|
|
if analysis_type == "Charts":
|
|
stats_type: str = ""
|
|
chart_data_type = st.selectbox(
|
|
"Chart Data Type",
|
|
options=["Stats", "Strikes", "Strategies", "Volatility"],
|
|
key="chart_data_type",
|
|
)
|
|
expiry = "All"
|
|
if chart_data_type == "Stats":
|
|
stats_type = st.selectbox(
|
|
"Stat Type",
|
|
options=["Open Interest", "Volume", "Ratios"],
|
|
key="stats_type",
|
|
)
|
|
if stats_type in ("Open Interest", "Volume"):
|
|
is_percent = st.checkbox(
|
|
"Percent of Total", value=False, key="is_percent"
|
|
)
|
|
if is_percent is False and stats_type == "Open Interest":
|
|
expiries = ["All"]
|
|
expiries.extend(df.expirations)
|
|
expiry = st.selectbox("Expiry", options=expiries, key="expiry")
|
|
if chart_data_type == "Strikes":
|
|
strike_choice = st.selectbox(
|
|
"Strike Selection Type",
|
|
options=(["Single Strike", "Separate Calls and Puts", "% OTM"]),
|
|
key="strike_choice",
|
|
)
|
|
if strike_choice == "Separate Calls and Puts":
|
|
_default_call = get_nearest_call_strike(
|
|
df, strike_price=df.last_price
|
|
)
|
|
_default_put = get_nearest_put_strike(
|
|
df, strike_price=df.last_price
|
|
)
|
|
_default_strikes = pd.Series(df.strikes)
|
|
default_call = int(
|
|
_default_strikes[_default_strikes == _default_call].index[0]
|
|
)
|
|
default_put = int(
|
|
_default_strikes[_default_strikes == _default_put].index[0]
|
|
)
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
_call = st.selectbox(
|
|
"Call Strike",
|
|
options=df.strikes,
|
|
index=default_call,
|
|
key="call_strike",
|
|
)
|
|
with col2:
|
|
_put = st.selectbox(
|
|
"Put Strike",
|
|
options=df.strikes,
|
|
index=default_put,
|
|
key="put_strike",
|
|
)
|
|
_strikes = {"call": _call, "put": _put}
|
|
if strike_choice == "% OTM":
|
|
moneyness: float = st.number_input(
|
|
label="% OTM Moneyness",
|
|
min_value=0.01,
|
|
max_value=99.99,
|
|
value=2.0,
|
|
step=0.25,
|
|
key="moneyness",
|
|
)
|
|
_strikes = get_nearest_otm_strike(df, moneyness)
|
|
if strike_choice == "Single Strike":
|
|
default_strike_price = get_nearest_call_strike(
|
|
df, strike_price=df.last_price
|
|
)
|
|
_strikes = pd.Series(df.strikes)
|
|
default_strike = int(
|
|
_strikes[_strikes == default_strike_price].index[0]
|
|
)
|
|
strike_price = st.selectbox(
|
|
"Strike Price",
|
|
options=df.strikes,
|
|
index=default_strike,
|
|
key="strike_price",
|
|
)
|
|
strike_data_point = st.selectbox(
|
|
"Data Point",
|
|
options=list(target_column.keys()),
|
|
key="strike_data_point",
|
|
)
|
|
|
|
if chart_data_type == "Strategies":
|
|
_strikes = pd.Series(df.strikes)
|
|
strategy_choice = st.selectbox(
|
|
"Strategy Type",
|
|
options=(
|
|
[
|
|
"Straddle",
|
|
"Strangle",
|
|
"Synthetic Long",
|
|
"Synthetic Short",
|
|
"Vertical Call",
|
|
"Vertical Put",
|
|
]
|
|
),
|
|
key="strategy_choice",
|
|
)
|
|
if strategy_choice == "Strangle":
|
|
strangle_moneyness: float = st.number_input(
|
|
label="% OTM Moneyness",
|
|
min_value=0.01,
|
|
max_value=99.99,
|
|
value=2.0,
|
|
step=0.25,
|
|
key="strangle_moneyness",
|
|
)
|
|
if strategy_choice == "Straddle":
|
|
default_strike_price = get_nearest_call_strike(
|
|
df, strike_price=df.last_price
|
|
)
|
|
default_strike = int(
|
|
_strikes[_strikes == default_strike_price].index[0]
|
|
)
|
|
straddle_strike: float = st.selectbox(
|
|
"Strike Price",
|
|
options=df.strikes,
|
|
index=default_strike,
|
|
key="straddle_strike",
|
|
)
|
|
if strategy_choice in ("Synthetic Long", "Synthetic Short"):
|
|
default_strike_price = get_nearest_call_strike(
|
|
df, strike_price=df.last_price
|
|
)
|
|
default_strike = int(
|
|
_strikes[_strikes == default_strike_price].index[0]
|
|
)
|
|
synthetic_strike = st.selectbox(
|
|
"Strike Price",
|
|
options=df.strikes,
|
|
index=default_strike,
|
|
key="synthetic_strike",
|
|
)
|
|
if strategy_choice in ("Vertical Call", "Vertical Put"):
|
|
default_strike_price1 = get_nearest_call_strike(
|
|
df,
|
|
strike_price=df.last_price + 5
|
|
if strategy_choice == "Vertical Call"
|
|
else df.last_price - 5,
|
|
)
|
|
default_strike_price2 = get_nearest_call_strike(
|
|
df, strike_price=df.last_price
|
|
)
|
|
default_strike1 = int(
|
|
_strikes[_strikes == default_strike_price1].index[0]
|
|
)
|
|
default_strike2 = int(
|
|
_strikes[_strikes == default_strike_price2].index[0]
|
|
)
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
strike1 = st.selectbox(
|
|
"Sold Strike Price",
|
|
options=df.strikes,
|
|
index=default_strike1,
|
|
key="strike1",
|
|
)
|
|
with col2:
|
|
strike2 = st.selectbox(
|
|
"Bought Strike Price",
|
|
options=df.strikes,
|
|
index=default_strike2,
|
|
key="strike2",
|
|
)
|
|
|
|
strategy_target_column = st.selectbox(
|
|
"Data Type",
|
|
options=list(strategy_target_column_dict.keys()),
|
|
key="strategy_target_column",
|
|
)
|
|
|
|
if chart_data_type == "Volatility":
|
|
vol_choices = ["Smile", "Skew", "Surface"]
|
|
vol_choice = st.selectbox(
|
|
"Volatility Type", options=vol_choices, index=0, key="vol_choice"
|
|
)
|
|
if vol_choice in ("Smile", "Skew"):
|
|
if vol_choice == "Smile":
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
with_volume = st.checkbox(
|
|
"With Volume", value=False, key="with_volume"
|
|
)
|
|
with col2:
|
|
with_oi = st.checkbox("With OI", value=False, key="with_oi")
|
|
if vol_choice == "Skew":
|
|
otm_only = st.checkbox("OTM Only", value=False, key="otm_only")
|
|
with st.expander("Select Expiration Dates"):
|
|
expiration_choice = st.multiselect(
|
|
"Expiration Date",
|
|
options=df.expirations,
|
|
default=df.expirations[2],
|
|
key="expiration_choice",
|
|
)
|
|
if vol_choice == "Surface":
|
|
surface_choices = [
|
|
"Calls Only",
|
|
"Puts Only",
|
|
"OTM Only",
|
|
"ITM Only",
|
|
]
|
|
surface_choice = st.selectbox(
|
|
"Surface Type",
|
|
options=surface_choices,
|
|
index=2,
|
|
key="surface_choice",
|
|
)
|
|
else:
|
|
st.write("Please enter a valid symbol.")
|
|
ticker_good = False
|
|
|
|
|
|
if analysis_type == "Tables" and table_choice == "Chains" and ticker_good is True:
|
|
if chains_select == "% OTM":
|
|
upper = chains_strikes["call"]
|
|
lower = chains_strikes["put"]
|
|
chains_df = df.chains.query("@lower <= `strike` <= @upper")
|
|
if chains_select == "Delta":
|
|
upper_call = delta_choice + 0.01
|
|
lower_call = delta_choice - 0.01
|
|
upper_put = (delta_choice + 0.01) * (-1)
|
|
lower_put = (delta_choice - 0.01) * (-1)
|
|
chains_df = df.chains.query(
|
|
"@lower_call <= `delta` <= @upper_call or @lower_put >= `delta` >= @upper_put"
|
|
)
|
|
if chains_select == "None":
|
|
chains_df = df.chains
|
|
if expiration_choice != "All":
|
|
chains_df = chains_df[chains_df["expiration"] == expiration_choice]
|
|
if expiration_choice == "All":
|
|
chains_df = chains_df[chains_df["dte"] >= 0]
|
|
chains_df = chains_df[
|
|
[
|
|
"strike",
|
|
"dte",
|
|
"optionType",
|
|
"theoretical",
|
|
"bidSize",
|
|
"bid",
|
|
"ask",
|
|
"askSize",
|
|
"lastTradePrice",
|
|
"openInterest",
|
|
"volume",
|
|
"impliedVolatility",
|
|
"delta",
|
|
"gamma",
|
|
"theta",
|
|
"rho",
|
|
"vega",
|
|
"open",
|
|
"high",
|
|
"low",
|
|
"previousClose",
|
|
"DEX",
|
|
"GEX",
|
|
"Breakeven",
|
|
"$ To Spot",
|
|
"% To Spot",
|
|
]
|
|
]
|
|
chains_df = chains_df.rename(
|
|
columns={
|
|
"strike": "Strike",
|
|
"dte": "DTE",
|
|
"optionType": "Type",
|
|
"theoretical": "Theoretical",
|
|
"bidSize": "Bid Size",
|
|
"bid": "Bid",
|
|
"ask": "Ask",
|
|
"askSize": "Ask Size",
|
|
"lastTradePrice": "Last",
|
|
"openInterest": "OI",
|
|
"volume": "Volume",
|
|
"impliedVolatility": "IV",
|
|
"delta": "Delta",
|
|
"gamma": "Gamma",
|
|
"theta": "Theta",
|
|
"rho": "Rho",
|
|
"vega": "Vega",
|
|
"open": "Open",
|
|
"high": "High",
|
|
"low": "Low",
|
|
"previousClose": "Prev Close",
|
|
}
|
|
)
|
|
chains_df.Theoretical = round(chains_df.Theoretical, 2)
|
|
chains_df = chains_df.set_index("Strike")
|
|
chains_df = chains_df[chains_df["Last"] > 0]
|
|
if chains_choice == "Calls":
|
|
chains_df = chains_df[chains_df["Type"] == "call"]
|
|
if chains_choice == "Puts":
|
|
chains_df = chains_df[chains_df["Type"] == "put"]
|
|
st.dataframe(chains_df, use_container_width=True, height=700)
|
|
|
|
if analysis_type == "Tables" and table_choice == "Stats" and ticker_good is True:
|
|
exposure_by_strike, exposure_by_expiration = get_exposure(df)
|
|
stats_df_expiration = df.get_stats()
|
|
stats_df_strike = df.get_stats("strike")
|
|
sc1, sc2, sc3, sc4, sc5 = st.columns(5)
|
|
with sc1:
|
|
st.write("Total OI: ", format(stats_df_expiration["Total OI"].sum(), ","))
|
|
with sc2:
|
|
st.write("Call OI: ", format(stats_df_expiration["Calls OI"].sum(), ","))
|
|
with sc3:
|
|
st.write("Put OI: ", format(stats_df_expiration["Puts OI"].sum(), ","))
|
|
with sc4:
|
|
st.write("Call DEX: $", format(exposure_by_expiration["Call DEX"].sum(), ","))
|
|
with sc5:
|
|
st.write(
|
|
"Put DEX: $", format(abs(exposure_by_expiration["Put DEX"]).sum(), ",")
|
|
)
|
|
|
|
with st.expander("Stats by Expiration", expanded=True):
|
|
st.dataframe(stats_df_expiration, use_container_width=True)
|
|
with st.expander("Stats by Strike", expanded=False):
|
|
st.dataframe(stats_df_strike, use_container_width=True)
|
|
with st.expander("Delta Dollars and GEX per 1% Move", expanded=False):
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
st.dataframe(exposure_by_expiration.abs(), use_container_width=True)
|
|
with col2:
|
|
st.dataframe(exposure_by_strike.abs(), use_container_width=True)
|
|
|
|
if analysis_type == "Charts" and ticker_good is True and chart_data_type == "Stats":
|
|
if stats_type == "Open Interest" and expiry != "All":
|
|
fig_oi_exp = df.chart_stats(
|
|
"strike", expiry=expiry, external_axes=True, oi=True, percent=False
|
|
)
|
|
fig_oi_exp = format_plotly(fig_oi_exp)
|
|
st.plotly_chart(
|
|
fig_oi_exp,
|
|
use_container_width=True,
|
|
)
|
|
|
|
elif stats_type in ("Open Interest", "Volume"):
|
|
tab1, tab2 = st.tabs(["By Expiration", "By Strike"])
|
|
with tab1:
|
|
if stats_type == "Open Interest":
|
|
fig_oi_exp = df.chart_stats(
|
|
percent=bool(is_percent), oi=True, external_axes=True
|
|
)
|
|
fig_oi_exp = format_plotly(fig_oi_exp)
|
|
st.plotly_chart(fig_oi_exp, use_container_width=True)
|
|
if stats_type == "Volume":
|
|
fig_vol_exp = df.chart_stats(
|
|
percent=bool(is_percent), oi=False, external_axes=True
|
|
)
|
|
fig_vol_exp = format_plotly(fig_vol_exp)
|
|
st.plotly_chart(fig_vol_exp, use_container_width=True)
|
|
with tab2:
|
|
if stats_type == "Open Interest":
|
|
fig_oi_strike = df.chart_stats(
|
|
"strike", percent=bool(is_percent), oi=True, external_axes=True
|
|
)
|
|
fig_oi_strike = format_plotly(fig_oi_strike)
|
|
st.plotly_chart(fig_oi_strike, use_container_width=True)
|
|
if stats_type == "Volume":
|
|
fig_vol_exp = df.chart_stats(
|
|
"strike", percent=bool(is_percent), oi=False, external_axes=True
|
|
)
|
|
fig_vol_exp = format_plotly(fig_vol_exp)
|
|
st.plotly_chart(fig_vol_exp, use_container_width=True)
|
|
|
|
elif stats_type == "Ratios":
|
|
ratios_fig = df.chart_stats(ratios=True, external_axes=True)
|
|
ratios_fig.update_layout(
|
|
legend=dict(
|
|
orientation="h",
|
|
yanchor="top",
|
|
y=1,
|
|
xanchor="left",
|
|
x=0.01,
|
|
)
|
|
)
|
|
ratios_fig = format_plotly(ratios_fig)
|
|
st.plotly_chart(
|
|
ratios_fig,
|
|
use_container_width=True,
|
|
config=dict(
|
|
scrollZoom=True,
|
|
displaylogo=False,
|
|
),
|
|
)
|
|
|
|
if analysis_type == "Charts" and chart_data_type == "Strikes" and ticker_good is True:
|
|
title = f"{strike_data_point} of {df.symbol} Options @ ${strike_price} Strike Price"
|
|
title = (
|
|
f"{strike_data_point} of {df.symbol} Options @ {moneyness}% OTM"
|
|
if strike_choice == "% OTM"
|
|
else title
|
|
)
|
|
title = (
|
|
f"{strike_data_point} of {df.symbol} Options"
|
|
if strike_choice == "Separate Calls and Puts"
|
|
else title
|
|
)
|
|
title = (
|
|
f"Gamma Exposure per 1% Move of {df.symbol}"
|
|
if strike_data_point == "GEX Per 1% Move"
|
|
else title
|
|
)
|
|
if strike_choice == "Single Strike":
|
|
_price_data = df.chains[df.chains["strike"] == strike_price]
|
|
call_price_data = _price_data[_price_data["optionType"] == "call"]
|
|
put_price_data = _price_data[_price_data["optionType"] == "put"]
|
|
call_price_data = call_price_data.drop_duplicates("expiration").set_index(
|
|
"expiration"
|
|
)
|
|
put_price_data = put_price_data.drop_duplicates("expiration").set_index(
|
|
"expiration"
|
|
)
|
|
price_data["Call"] = call_price_data[target_column[strike_data_point]]
|
|
price_data["Put"] = put_price_data[target_column[strike_data_point]]
|
|
if strike_choice in ("Separate Calls and Puts", "% OTM"):
|
|
call_price_data = (
|
|
df.chains[df.chains["strike"] == _strikes["call"]]
|
|
.query("`optionType` == 'call'")
|
|
.drop_duplicates("expiration")
|
|
.set_index("expiration")
|
|
)
|
|
put_price_data = (
|
|
df.chains[df.chains["strike"] == _strikes["put"]]
|
|
.query("`optionType` == 'put'")
|
|
.drop_duplicates("expiration")
|
|
.set_index("expiration")
|
|
)
|
|
price_data["Call"] = call_price_data[target_column[strike_data_point]]
|
|
price_data["Put"] = put_price_data[target_column[strike_data_point]]
|
|
fig_price_data = OpenBBFigure()
|
|
fig_price_data.add_scatter(
|
|
x=price_data.index,
|
|
y=price_data["Call"],
|
|
mode="lines+markers",
|
|
name="Calls"
|
|
if strike_choice == "Single Strike"
|
|
else f"Calls @ ${_strikes['call']}",
|
|
marker=dict(color="blue"),
|
|
)
|
|
fig_price_data.add_scatter(
|
|
x=price_data.index,
|
|
y=price_data["Put"],
|
|
mode="lines+markers",
|
|
name="Puts"
|
|
if strike_choice == "Single Strike"
|
|
else f"Puts @ {_strikes['put']}",
|
|
marker=dict(color="red"),
|
|
)
|
|
fig_price_data.update_xaxes(type="category")
|
|
fig_price_data.update_layout(
|
|
title=dict(text=title, x=0.5, y=0.97),
|
|
legend=dict(
|
|
orientation="h",
|
|
yanchor="top",
|
|
y=1.125,
|
|
xanchor="left",
|
|
x=0.01,
|
|
),
|
|
)
|
|
fig_price_data = format_plotly(fig_price_data)
|
|
st.plotly_chart(
|
|
fig_price_data,
|
|
use_container_width=True,
|
|
config=dict(
|
|
scrollZoom=True,
|
|
displaylogo=False,
|
|
),
|
|
)
|
|
|
|
if (
|
|
analysis_type == "Charts"
|
|
and chart_data_type == "Strategies"
|
|
and ticker_good is True
|
|
):
|
|
if strategy_choice == "Strangle":
|
|
title = f"Cost of {df.symbol} Strangle @ {strangle_moneyness}% OTM"
|
|
strategy_data = df.get_strategies(strangle_moneyness=strangle_moneyness)
|
|
|
|
if strategy_choice == "Straddle":
|
|
title = f"Cost of {df.symbol} Straddle @ ${straddle_strike} Strike"
|
|
strategy_data = df.get_strategies(straddle_strike=straddle_strike)
|
|
|
|
if strategy_choice == "Synthetic Long":
|
|
title = f"Cost of {df.symbol} Synthetic Long @ ${synthetic_strike} Strike"
|
|
strategy_data = df.get_strategies(synthetic_longs=synthetic_strike)
|
|
|
|
if strategy_choice == "Synthetic Short":
|
|
title = f"Cost of {df.symbol} Synthetic Short @ ${synthetic_strike} Strike"
|
|
strategy_data = df.get_strategies(synthetic_shorts=synthetic_strike)
|
|
|
|
if strategy_choice == "Vertical Call":
|
|
strategy_data = df.get_strategies(vertical_calls=[strike1, strike2])
|
|
strategy_name = strategy_data.iloc[0]["Strategy"]
|
|
title = f"Cost of {df.symbol} {strategy_name} @ ${strike1} & ${strike2} Strikes"
|
|
|
|
if strategy_choice == "Vertical Put":
|
|
strategy_data = df.get_strategies(vertical_puts=[strike1, strike2])
|
|
strategy_name = strategy_data.iloc[0]["Strategy"]
|
|
title = f"Cost of {df.symbol} {strategy_name} @ ${strike1} & ${strike2} Strikes"
|
|
|
|
title = (
|
|
title + " As % of Underlying Price"
|
|
if strategy_target_column == "Cost as % of Underlying"
|
|
else title
|
|
)
|
|
|
|
fig_strategy = OpenBBFigure()
|
|
fig_strategy.add_scatter(
|
|
x=strategy_data.Expiration,
|
|
y=strategy_data[strategy_target_column_dict[strategy_target_column]],
|
|
mode="lines+markers",
|
|
name="Cost of Long Strangle",
|
|
marker=dict(color="red"),
|
|
)
|
|
fig_strategy.update_xaxes(type="category")
|
|
fig_strategy.update_layout(title=dict(text=title, x=0.5, y=0.97))
|
|
fig_strategy = format_plotly(fig_strategy)
|
|
st.plotly_chart(
|
|
fig_strategy,
|
|
use_container_width=True,
|
|
config=dict(
|
|
scrollZoom=True,
|
|
displaylogo=False,
|
|
),
|
|
)
|
|
|
|
if (
|
|
analysis_type == "Charts"
|
|
and chart_data_type == "Volatility"
|
|
and ticker_good is True
|
|
):
|
|
if vol_choice == "Smile":
|
|
if len(expiration_choice) == 0:
|
|
expiration_choice = df.expirations[2]
|
|
if len(expiration_choice) > 5:
|
|
expiration_choice = expiration_choice[0:5]
|
|
smile_fig = df.chart_volatility(
|
|
expirations=expiration_choice,
|
|
oi=with_oi,
|
|
volume=with_volume,
|
|
external_axes=True,
|
|
)
|
|
smile_fig = format_plotly(smile_fig)
|
|
smile_fig.update_layout(
|
|
legend=dict(
|
|
orientation="h", yanchor="top", y=1.025, xanchor="center", x=0.5
|
|
)
|
|
)
|
|
st.plotly_chart(
|
|
smile_fig,
|
|
use_container_width=True,
|
|
config=dict(
|
|
scrollZoom=True,
|
|
displaylogo=False,
|
|
),
|
|
)
|
|
if vol_choice == "Skew":
|
|
if len(expiration_choice) == 0:
|
|
expiration_choice = df.expirations[2]
|
|
if otm_only is False and len(expiration_choice) > 5:
|
|
expiration_choice = expiration_choice[0:5]
|
|
if otm_only is True and len(expiration_choice) > 10:
|
|
expiration_choice = expiration_choice[0:10]
|
|
skew_fig = df.chart_skew(
|
|
expirations=expiration_choice, otm_only=otm_only, external_axes=True
|
|
)
|
|
skew_fig = format_plotly(skew_fig)
|
|
skew_fig.update_layout(
|
|
legend=dict(
|
|
orientation="h", yanchor="top", y=1.025, xanchor="center", x=0.5
|
|
)
|
|
)
|
|
st.plotly_chart(
|
|
skew_fig,
|
|
use_container_width=True,
|
|
config=dict(
|
|
scrollZoom=True,
|
|
displaylogo=False,
|
|
),
|
|
)
|
|
if vol_choice == "Surface":
|
|
dte_ranges = df.chains.dte.unique().tolist()
|
|
col1, col2, col3, col4 = st.columns(4)
|
|
with col1:
|
|
near_dte_limit = int(
|
|
st.select_slider(
|
|
"Near DTE Boundary",
|
|
options=dte_ranges,
|
|
value=dte_ranges[0],
|
|
key="near_dte_limit",
|
|
)
|
|
)
|
|
with col2:
|
|
far_dte_limit = int(
|
|
st.select_slider(
|
|
"Far DTE Boundary",
|
|
options=dte_ranges,
|
|
value=dte_ranges[-1],
|
|
key="far_dte_limit",
|
|
)
|
|
)
|
|
near = near_dte_limit if near_dte_limit < far_dte_limit else far_dte_limit
|
|
far = far_dte_limit if near_dte_limit < far_dte_limit else near_dte_limit
|
|
if near == far:
|
|
far += 1
|
|
dte_range = [far, near]
|
|
with col3:
|
|
lower_strike = float(
|
|
st.select_slider(
|
|
"Lower Strike",
|
|
options=df.strikes,
|
|
value=df.strikes[0],
|
|
key="lower_strike",
|
|
)
|
|
)
|
|
with col4:
|
|
upper_strike = float(
|
|
st.select_slider(
|
|
"Upper Strike",
|
|
options=df.strikes,
|
|
value=df.strikes[-1],
|
|
key="upper_strike",
|
|
)
|
|
)
|
|
lower = lower_strike if lower_strike < upper_strike else upper_strike
|
|
upper = upper_strike if lower_strike < upper_strike else lower_strike
|
|
strike_range = [lower, upper]
|
|
surface_fig = df.chart_surface(
|
|
surface_choice_dict[surface_choice],
|
|
dte_range,
|
|
strike_range,
|
|
external_axes=True,
|
|
)
|
|
surface_fig = format_plotly(surface_fig)
|
|
st.plotly_chart(surface_fig, use_container_width=True)
|