Spaces:
Running
Running
| import streamlit as st | |
| import yfinance as yf | |
| import pandas as pd | |
| import numpy as np | |
| import plotly.express as px | |
| import plotly.graph_objects as go | |
| from plotly.subplots import make_subplots | |
| from datetime import datetime | |
| from py_vollib.black_scholes.greeks import analytical | |
| st.set_page_config(layout="wide") | |
| st.sidebar.title("Input Parameters") | |
| with st.sidebar.expander("How to Use", expanded=False): | |
| st.markdown(""" | |
| - 1. **Input Ticker Symbol**: Enter the stock ticker symbol in the sidebar (e.g., `AAPL` for Apple Inc.). | |
| - 2. **Analyze**: Click the "Analyze" button to fetch and process the options data. | |
| - 3. **Visualize**: View the visualizations and interpretations of the options data to understand market sentiment. | |
| """) | |
| st.title("Options Sentiment Analysis Tool") | |
| st.markdown(""" | |
| This tool analyzes options data for a given stock ticker symbol to gauge market sentiment using implied volatility and infer the market's expectations of future price movements. IV represents the market's forecast of a likely movement in a security's price. High implied volatility often signals uncertainty or expected volatility, while comparing call and put options can provide insights into bullish or bearish sentiments. | |
| """) | |
| with st.expander("Understanding Implied Volatility:", expanded=False): | |
| st.markdown("""Here’s a simplified representation of how implied volatility is calculated. """) | |
| st.latex(r''' | |
| IV = \frac{Market \, Price \, of \, Option - Intrinsic \, Value}{Time \, Value \, of \, Money \, + \, Volatility \, Expectation} | |
| ''') | |
| st.markdown(""" | |
| In practice, implied volatility is derived using the Black-Scholes formula, based on current option prices. The formula calculates the theoretical value of options, and by inputting the current market prices, we can solve for the implied volatility. | |
| """) | |
| with st.expander("Visualizations:", expanded=False): | |
| st.markdown(""" | |
| ### Visualizations: | |
| - **Volatility Smile**: Displays the implied volatility of call and put options across different strike prices and expiration dates, providing insights into expected price volatility. | |
| - **Open Interest**: Shows the open interest of call and put options across different strike prices, indicating the level of trading activity and interest. | |
| - **Volume Analysis**: Presents the trading volume of call and put options across different strike prices, reflecting the market's trading activity and interest. | |
| - **3D Scatter Plot**: | |
| - **Puts**: Visualizes the implied volatility of put options in a 3D view, categorized by expiration date, to identify patterns and sentiment shifts. | |
| - **Calls**: Visualizes the implied volatility of call options in a 3D view, categorized by expiration date, to identify patterns and sentiment shifts. | |
| - **Historical Implied Volatility (IV)**: Tracks the historical implied volatility over a specified period, helping to understand the volatility trends and current volatility level compared to the past. | |
| - **Put/Call Ratio**: Calculates the ratio of the trading volume of put options to call options, indicating overall market sentiment (bullish or bearish). | |
| - **Options Greeks Analysis**: Assesses the Greeks (Delta, Gamma, Theta, Vega) of call and put options, providing insights into the sensitivity of options prices to various factors. | |
| - **Sentiment Score**: Combines implied volatility and volume data to generate a sentiment score, indicating whether the market sentiment is bullish or bearish. | |
| """) | |
| def get_options_data(ticker): | |
| asset = yf.Ticker(ticker) | |
| exp_dates = asset.options | |
| if not exp_dates: | |
| st.error(f"No options data available for ticker {ticker}.") | |
| return None, None | |
| recent_price = asset.history(period="1d")["Close"].iloc[-1] | |
| options_data = [] | |
| for date in exp_dates: | |
| calls = asset.option_chain(date).calls | |
| puts = asset.option_chain(date).puts | |
| calls["expiration"] = date | |
| puts["expiration"] = date | |
| calls["type"] = "call" | |
| puts["type"] = "put" | |
| data = pd.concat([calls, puts]) | |
| options_data.append(data) | |
| if not options_data: | |
| st.error(f"No valid options data available for ticker {ticker}.") | |
| return None, None | |
| options_data = pd.concat(options_data) | |
| options_data = options_data[options_data["strike"].between(recent_price * 0.9, recent_price * 1.1)] | |
| options_data["implied_volatility"] = options_data["impliedVolatility"] * 100 | |
| return options_data, recent_price | |
| def plot_volatility_smile(options_data, recent_price, ticker): | |
| calls_data = options_data[options_data["type"] == "call"] | |
| puts_data = options_data[options_data["type"] == "put"] | |
| expirations = options_data['expiration'].unique() | |
| color_map_2d = px.colors.qualitative.Prism | |
| fig = make_subplots(rows=1, cols=2, subplot_titles=["Calls", "Puts"], shared_yaxes=True) | |
| for exp, color in zip(expirations, color_map_2d): | |
| exp_calls = calls_data[calls_data["expiration"] == exp] | |
| exp_puts = puts_data[puts_data["expiration"] == exp] | |
| fig.add_trace(go.Scatter(x=exp_calls["strike"], y=exp_calls["implied_volatility"], mode='markers', | |
| marker=dict(color=color), name=exp), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=exp_puts["strike"], y=exp_puts["implied_volatility"], mode='markers', | |
| marker=dict(color=color), name=exp, showlegend=False), row=1, col=2) | |
| avg_iv_by_strike_calls = calls_data.groupby("strike")["implied_volatility"].mean() | |
| avg_iv_by_strike_puts = puts_data.groupby("strike")["implied_volatility"].mean() | |
| fig.add_trace(go.Scatter(x=avg_iv_by_strike_calls.index, y=avg_iv_by_strike_calls.values, mode='lines', | |
| line=dict(color='white', dash='dash'), name='Avg. IV by Strike (Calls)'), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=avg_iv_by_strike_puts.index, y=avg_iv_by_strike_puts.values, mode='lines', | |
| line=dict(color='white', dash='dash'), name='Avg. IV by Strike (Puts)', showlegend=False), row=1, col=2) | |
| overall_avg_iv_calls = calls_data["implied_volatility"].mean() | |
| overall_avg_iv_puts = puts_data["implied_volatility"].mean() | |
| fig.add_hline(y=overall_avg_iv_calls, line=dict(color='gray', dash='dash'), | |
| annotation_text=f"Overall Avg. IV (Calls): {overall_avg_iv_calls:.2f}%", | |
| row=1, col=1) | |
| fig.add_hline(y=overall_avg_iv_puts, line=dict(color='gray', dash='dash'), | |
| annotation_text=f"Overall Avg. IV (Puts): {overall_avg_iv_puts:.2f}%", | |
| row=1, col=2) | |
| fig.update_layout(title=f"{ticker} Volatility Smile - Current Price: {recent_price:.2f}", showlegend=True, legend_title_text='Expiration Date') | |
| fig.update_xaxes(title_text="Strike Price") | |
| fig.update_yaxes(title_text="Implied Volatility (%)") | |
| st.plotly_chart(fig) | |
| return overall_avg_iv_calls, overall_avg_iv_puts, avg_iv_by_strike_calls, avg_iv_by_strike_puts, expirations | |
| def interpret_volatility_smile(ticker, overall_avg_iv_calls, overall_avg_iv_puts, avg_iv_by_strike_calls, avg_iv_by_strike_puts): | |
| interpretation = f"**Interpretation of {ticker} Volatility Smile:**\n" | |
| interpretation += f"- The average implied volatility for call options is {overall_avg_iv_calls:.2f}%.\n" | |
| interpretation += f"- The average implied volatility for put options is {overall_avg_iv_puts:.2f}%.\n" | |
| if avg_iv_by_strike_calls.var() > 0.1: | |
| interpretation += "- The call options exhibit a noticeable 'volatility smile,' indicating varying implied volatility across different strike prices.\n" | |
| else: | |
| interpretation += "- The call options do not show a significant 'volatility smile,' suggesting more stable implied volatility across strike prices.\n" | |
| if avg_iv_by_strike_puts.var() > 0.1: | |
| interpretation += "- The put options exhibit a noticeable 'volatility smile,' indicating varying implied volatility across different strike prices.\n" | |
| else: | |
| interpretation += "- The put options do not show a significant 'volatility smile,' suggesting more stable implied volatility across strike prices.\n" | |
| market_sentiment = "bullish" if overall_avg_iv_calls > overall_avg_iv_puts else "bearish" | |
| interpretation += f"- The overall market sentiment is {market_sentiment}, inferred from the average implied volatility of calls and puts.\n" | |
| if overall_avg_iv_calls > overall_avg_iv_puts: | |
| interpretation += "- The higher implied volatility in call options suggests that traders expect upward price movements or higher uncertainty in the stock price.\n" | |
| else: | |
| interpretation += "- The higher implied volatility in put options suggests that traders expect downward price movements or higher uncertainty in the stock price.\n" | |
| st.markdown(interpretation) | |
| def plot_open_interest(options_data, recent_price, ticker): | |
| calls_data = options_data[options_data["type"] == "call"] | |
| puts_data = options_data[options_data["type"] == "put"] | |
| expirations = options_data['expiration'].unique() | |
| color_map_2d = px.colors.qualitative.Prism | |
| fig = make_subplots(rows=1, cols=2, subplot_titles=["Calls Open Interest", "Puts Open Interest"], shared_yaxes=True) | |
| for exp, color in zip(expirations, color_map_2d): | |
| exp_calls = calls_data[calls_data["expiration"] == exp] | |
| exp_puts = puts_data[puts_data["expiration"] == exp] | |
| fig.add_trace(go.Scatter(x=exp_calls["strike"], y=exp_calls["openInterest"], mode='markers', | |
| marker=dict(color=color), name=exp), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=exp_puts["strike"], y=exp_puts["openInterest"], mode='markers', | |
| marker=dict(color=color), name=exp, showlegend=False), row=1, col=2) | |
| overall_avg_oi_calls = calls_data["openInterest"].mean() | |
| overall_avg_oi_puts = puts_data["openInterest"].mean() | |
| fig.update_layout(title=f"{ticker} Open Interest by Strike - Current Price: {recent_price:.2f}", showlegend=True, legend_title_text='Expiration Date') | |
| fig.update_xaxes(title_text="Strike Price") | |
| fig.update_yaxes(title_text="Open Interest") | |
| st.plotly_chart(fig) | |
| return overall_avg_oi_calls, overall_avg_oi_puts, calls_data, puts_data | |
| def interpret_open_interest(ticker, overall_avg_oi_calls, overall_avg_oi_puts, calls_data, puts_data): | |
| interpretation = f"**Interpretation of {ticker} Open Interest:**\n" | |
| interpretation += f"- The average open interest for call options is {overall_avg_oi_calls:.2f} contracts.\n" | |
| interpretation += f"- The average open interest for put options is {overall_avg_oi_puts:.2f} contracts.\n" | |
| if overall_avg_oi_calls > overall_avg_oi_puts: | |
| interpretation += "- Call options have higher average open interest, indicating higher trading activity and interest in calls.\n" | |
| else: | |
| interpretation += "- Put options have higher average open interest, indicating higher trading activity and interest in puts.\n" | |
| highest_oi_call = calls_data.loc[calls_data['openInterest'].idxmax()] | |
| highest_oi_put = puts_data.loc[puts_data['openInterest'].idxmax()] | |
| interpretation += f"- The strike price with the highest open interest for calls is {highest_oi_call['strike']} with {highest_oi_call['openInterest']} contracts.\n" | |
| interpretation += f"- The strike price with the highest open interest for puts is {highest_oi_put['strike']} with {highest_oi_put['openInterest']} contracts.\n" | |
| if overall_avg_oi_calls > overall_avg_oi_puts: | |
| interpretation += "- The higher open interest in call options suggests that traders might be anticipating upward price movements.\n" | |
| else: | |
| interpretation += "- The higher open interest in put options suggests that traders might be anticipating downward price movements.\n" | |
| st.markdown(interpretation) | |
| def plot_volume(options_data, recent_price, ticker): | |
| calls_data = options_data[options_data["type"] == "call"] | |
| puts_data = options_data[options_data["type"] == "put"] | |
| expirations = options_data['expiration'].unique() | |
| color_map_2d = px.colors.qualitative.Prism | |
| fig = make_subplots(rows=1, cols=2, subplot_titles=["Calls Volume", "Puts Volume"], shared_yaxes=True) | |
| for exp, color in zip(expirations, color_map_2d): | |
| exp_calls = calls_data[calls_data["expiration"] == exp] | |
| exp_puts = puts_data[puts_data["expiration"] == exp] | |
| fig.add_trace(go.Scatter(x=exp_calls["strike"], y=exp_calls["volume"], mode='markers', | |
| marker=dict(color=color), name=exp), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=exp_puts["strike"], y=exp_puts["volume"], mode='markers', | |
| marker=dict(color=color), name=exp, showlegend=False), row=1, col=2) | |
| overall_avg_vol_calls = calls_data["volume"].mean() | |
| overall_avg_vol_puts = puts_data["volume"].mean() | |
| fig.update_layout(title=f"{ticker} Volume by Strike - Current Price: {recent_price:.2f}", showlegend=True, legend_title_text='Expiration Date') | |
| fig.update_xaxes(title_text="Strike Price") | |
| fig.update_yaxes(title_text="Volume") | |
| st.plotly_chart(fig) | |
| return overall_avg_vol_calls, overall_avg_vol_puts, calls_data, puts_data | |
| def interpret_volume(ticker, overall_avg_vol_calls, overall_avg_vol_puts, calls_data, puts_data): | |
| interpretation = f"**Interpretation of {ticker} Volume Analysis:**\n" | |
| interpretation += f"- The average volume for call options is {overall_avg_vol_calls:.2f} contracts.\n" | |
| interpretation += f"- The average volume for put options is {overall_avg_vol_puts:.2f} contracts.\n" | |
| if overall_avg_vol_calls > overall_avg_vol_puts: | |
| interpretation += "- Call options have higher average volume, indicating higher trading activity and interest in calls.\n" | |
| else: | |
| interpretation += "- Put options have higher average volume, indicating higher trading activity and interest in puts.\n" | |
| highest_vol_call = calls_data.loc[calls_data['volume'].idxmax()] | |
| highest_vol_put = puts_data.loc[puts_data['volume'].idxmax()] | |
| interpretation += f"- The strike price with the highest volume for calls is {highest_vol_call['strike']} with {highest_vol_call['volume']} contracts.\n" | |
| interpretation += f"- The strike price with the highest volume for puts is {highest_vol_put['strike']} with {highest_vol_put['volume']} contracts.\n" | |
| if overall_avg_vol_calls > overall_avg_vol_puts: | |
| interpretation += "- The higher volume in call options suggests that traders are more actively trading calls, possibly anticipating upward price movements.\n" | |
| else: | |
| interpretation += "- The higher volume in put options suggests that traders are more actively trading puts, possibly anticipating downward price movements.\n" | |
| st.markdown(interpretation) | |
| def plot_3d_puts_implied_volatility(options_data, ticker): | |
| puts_data = options_data[options_data["type"] == "put"] | |
| expirations = options_data['expiration'].unique() | |
| color_map_3d = {exp: color for exp, color in zip(expirations, px.colors.qualitative.Prism)} | |
| fig1 = px.scatter_3d(puts_data, x='strike', y='expiration', z='implied_volatility', | |
| color='expiration', color_discrete_map=color_map_3d, | |
| title=f'{ticker} Put Options Implied Volatility', | |
| labels={'strike': 'Strike Price', 'expiration': 'Expiration Date', 'implied_volatility': 'Implied Volatility (%)'}, | |
| hover_name='expiration') | |
| st.plotly_chart(fig1) | |
| return puts_data | |
| def interpret_3d_puts_implied_volatility(ticker, puts_data): | |
| interpretation = f"**Interpretation of {ticker} Put Options Implied Volatility (3D Scatter Plot):**\n" | |
| overall_avg_iv_puts = puts_data["implied_volatility"].mean() | |
| interpretation += f"- The average implied volatility for put options is {overall_avg_iv_puts:.2f}%.\n" | |
| highest_iv_put_idx = puts_data['implied_volatility'].idxmax() | |
| highest_iv_put = puts_data.loc[highest_iv_put_idx] | |
| strike_price = highest_iv_put['strike'] | |
| implied_volatility = highest_iv_put['implied_volatility'] | |
| expiration_date = highest_iv_put['expiration'] | |
| if isinstance(strike_price, pd.Series): | |
| strike_price = strike_price.iloc[0] | |
| if isinstance(implied_volatility, pd.Series): | |
| implied_volatility = implied_volatility.iloc[0] | |
| if isinstance(expiration_date, pd.Series): | |
| expiration_date = expiration_date.iloc[0] | |
| interpretation += f"- The strike price with the highest implied volatility for puts is {strike_price} with {implied_volatility:.2f}% implied volatility, expiring on {expiration_date}.\n" | |
| interpretation += "\n**Implied Volatility by Expiration Dates:**\n" | |
| for exp in puts_data['expiration'].unique(): | |
| exp_data = puts_data[puts_data['expiration'] == exp] | |
| avg_iv_exp = exp_data['implied_volatility'].mean() | |
| interpretation += f"- Average IV for puts expiring on {exp}: {avg_iv_exp:.2f}%.\n" | |
| interpretation += "\n**Implied Volatility by Strike Prices:**\n" | |
| strike_price_bins = pd.cut(puts_data['strike'], bins=5) | |
| grouped_strike_data = puts_data.groupby(strike_price_bins)['implied_volatility'].mean() | |
| for interval, avg_iv_strike in grouped_strike_data.items(): | |
| interpretation += f"- Average IV for puts with strike prices in range {interval}: {avg_iv_strike:.2f}%.\n" | |
| iv_variability = puts_data['implied_volatility'].std() | |
| if iv_variability > 10: | |
| interpretation += "\n- There is significant variability in implied volatility across different strike prices and expiration dates, indicating diverse market expectations and uncertainty.\n" | |
| else: | |
| interpretation += "\n- The implied volatility is relatively stable across different strike prices and expiration dates, indicating consistent market expectations.\n" | |
| interpretation += "\n**Overall Analysis:**\n" | |
| interpretation += f"- The average IV of {overall_avg_iv_puts:.2f}% suggests a certain level of expected volatility in the underlying asset. " | |
| interpretation += f"The highest IV of {implied_volatility:.2f}% at the strike price of {strike_price} and expiration date of {expiration_date} indicates significant uncertainty or expected price movement around that particular strike and time.\n" | |
| interpretation += "- Expiration dates and strike prices both show variations in IV, suggesting that traders' expectations of volatility differ based on the specific terms of the options. " | |
| interpretation += "Higher IV in shorter expirations might indicate expected near-term volatility, while longer expirations with lower IV can suggest stability over time.\n" | |
| st.markdown(interpretation) | |
| def plot_3d_calls_implied_volatility(options_data, ticker): | |
| calls_data = options_data[options_data["type"] == "call"] | |
| expirations = options_data['expiration'].unique() | |
| color_map_3d = {exp: color for exp, color in zip(expirations, px.colors.qualitative.Prism)} | |
| fig2 = px.scatter_3d(calls_data, x='strike', y='expiration', z='implied_volatility', | |
| color='expiration', color_discrete_map=color_map_3d, | |
| title=f'{ticker} Call Options Implied Volatility', | |
| labels={'strike': 'Strike Price', 'expiration': 'Expiration Date', 'implied_volatility': 'Implied Volatility (%)'}, | |
| hover_name='expiration') | |
| st.plotly_chart(fig2) | |
| return calls_data | |
| def interpret_3d_calls_implied_volatility(ticker, calls_data): | |
| interpretation = f"**Interpretation of {ticker} Call Options Implied Volatility (3D Scatter Plot):**\n" | |
| overall_avg_iv_calls = calls_data["implied_volatility"].mean() | |
| interpretation += f"- The average implied volatility for call options is {overall_avg_iv_calls:.2f}%.\n" | |
| highest_iv_call_idx = calls_data['implied_volatility'].idxmax() | |
| highest_iv_call = calls_data.loc[highest_iv_call_idx] | |
| strike_price = highest_iv_call['strike'] | |
| implied_volatility = highest_iv_call['implied_volatility'] | |
| expiration_date = highest_iv_call['expiration'] | |
| if isinstance(strike_price, pd.Series): | |
| strike_price = strike_price.iloc[0] | |
| if isinstance(implied_volatility, pd.Series): | |
| implied_volatility = implied_volatility.iloc[0] | |
| if isinstance(expiration_date, pd.Series): | |
| expiration_date = expiration_date.iloc[0] | |
| interpretation += f"- The strike price with the highest implied volatility for calls is {strike_price} with {implied_volatility:.2f}% implied volatility, expiring on {expiration_date}.\n" | |
| interpretation += "\n**Implied Volatility by Expiration Dates:**\n" | |
| for exp in calls_data['expiration'].unique(): | |
| exp_data = calls_data[calls_data['expiration'] == exp] | |
| avg_iv_exp = exp_data['implied_volatility'].mean() | |
| interpretation += f"- Average IV for calls expiring on {exp}: {avg_iv_exp:.2f}%.\n" | |
| interpretation += "\n**Implied Volatility by Strike Prices:**\n" | |
| strike_price_bins = pd.cut(calls_data['strike'], bins=5) | |
| grouped_strike_data = calls_data.groupby(strike_price_bins)['implied_volatility'].mean() | |
| for interval, avg_iv_strike in grouped_strike_data.items(): | |
| interpretation += f"- Average IV for calls with strike prices in range {interval}: {avg_iv_strike:.2f}%.\n" | |
| iv_variability = calls_data['implied_volatility'].std() | |
| if iv_variability > 10: | |
| interpretation += "\n- There is significant variability in implied volatility across different strike prices and expiration dates, indicating diverse market expectations and uncertainty.\n" | |
| else: | |
| interpretation += "\n- The implied volatility is relatively stable across different strike prices and expiration dates, indicating consistent market expectations.\n" | |
| interpretation += "\n**Overall Analysis:**\n" | |
| interpretation += f"- The average IV of {overall_avg_iv_calls:.2f}% suggests a certain level of expected volatility in the underlying asset. " | |
| interpretation += f"The highest IV of {implied_volatility:.2f}% at the strike price of {strike_price} and expiration date of {expiration_date} indicates significant uncertainty or expected price movement around that particular strike and time.\n" | |
| interpretation += "- Expiration dates and strike prices both show variations in IV, suggesting that traders' expectations of volatility differ based on the specific terms of the options. " | |
| interpretation += "Higher IV in shorter expirations might indicate expected near-term volatility, while longer expirations with lower IV can suggest stability over time.\n" | |
| st.markdown(interpretation) | |
| def plot_historical_iv(ticker, start_date): | |
| end_date = datetime.today().strftime('%Y-%m-%d') | |
| hist_data = yf.download(ticker, start=start_date, end=end_date) | |
| hist_data['IV'] = (hist_data['High'] - hist_data['Low']) / hist_data['Low'] * 100 | |
| fig = go.Figure() | |
| fig.add_trace(go.Scatter(x=hist_data.index, y=hist_data['IV'], mode='lines', name='Historical IV')) | |
| current_iv = hist_data['IV'].iloc[-1] | |
| fig.add_trace(go.Scatter(x=[hist_data.index[-1]], y=[current_iv], mode='markers', name='Current IV', | |
| marker=dict(color='red', size=10))) | |
| fig.update_layout( | |
| title=f"{ticker} Historical Implied Volatility", | |
| xaxis_title="Date", | |
| yaxis_title="Implied Volatility (%)", | |
| legend_title="Legend" | |
| ) | |
| st.plotly_chart(fig) | |
| return hist_data, current_iv | |
| def interpret_historical_iv(ticker, historical_iv, current_iv): | |
| interpretation = f"**Interpretation of {ticker} Historical Implied Volatility:**\n" | |
| avg_iv = historical_iv["IV"].mean() | |
| max_iv = historical_iv["IV"].max() | |
| min_iv = historical_iv["IV"].min() | |
| interpretation += f"- The average implied volatility over the period is {avg_iv:.2f}%.\n" | |
| interpretation += f"- The maximum implied volatility recorded was {max_iv:.2f}%.\n" | |
| interpretation += f"- The minimum implied volatility recorded was {min_iv:.2f}%.\n" | |
| interpretation += f"- The current implied volatility is {current_iv:.2f}%.\n" | |
| historical_iv['Year'] = historical_iv.index.year | |
| avg_iv_by_year = historical_iv.groupby('Year')['IV'].mean() | |
| interpretation += "\n**Average Implied Volatility by Year:**\n" | |
| for year, avg_iv in avg_iv_by_year.items(): | |
| interpretation += f"- {year}: {avg_iv:.2f}%.\n" | |
| iv_variability = historical_iv['IV'].std() | |
| interpretation += f"\n- The standard deviation of implied volatility over the period is {iv_variability:.2f}, indicating {'high' if iv_variability > 10 else 'low'} variability in implied volatility.\n" | |
| recent_trend = "increased" if historical_iv['IV'].iloc[-1] > historical_iv['IV'].iloc[-30] else "decreased" | |
| interpretation += f"\n- In the last 30 days, the implied volatility has {recent_trend} compared to the previous period.\n" | |
| st.markdown(interpretation) | |
| def calculate_put_call_ratio(options_data): | |
| calls_data = options_data[options_data["type"] == "call"] | |
| puts_data = options_data[options_data["type"] == "put"] | |
| total_puts = puts_data['volume'].sum() | |
| total_calls = calls_data['volume'].sum() | |
| put_call_ratio = total_puts / total_calls | |
| st.write(f"Put/Call Ratio: {put_call_ratio:.2f}") | |
| return total_puts, total_calls, put_call_ratio | |
| def interpret_put_call_ratio(ticker, total_puts, total_calls, put_call_ratio): | |
| interpretation = f"**Interpretation of {ticker} Put/Call Ratio:**\n" | |
| interpretation += f"- The Put/Call Ratio is {put_call_ratio:.2f}.\n" | |
| interpretation += f"- Total volume of puts: {total_puts}\n" | |
| interpretation += f"- Total volume of calls: {total_calls}\n" | |
| if put_call_ratio > 1: | |
| interpretation += "- A Put/Call Ratio greater than 1 indicates a bearish sentiment in the market, as more puts are being traded relative to calls.\n" | |
| elif put_call_ratio < 1: | |
| interpretation += "- A Put/Call Ratio less than 1 indicates a bullish sentiment in the market, as more calls are being traded relative to puts.\n" | |
| else: | |
| interpretation += "- A Put/Call Ratio of 1 indicates a neutral sentiment in the market, with equal volumes of puts and calls being traded.\n" | |
| st.markdown(interpretation) | |
| def calculate_greeks(option_type, S, K, T, r, sigma): | |
| greeks = {} | |
| try: | |
| if option_type == "call": | |
| flag = "c" | |
| else: | |
| flag = "p" | |
| greeks['delta'] = analytical.delta(flag, S, K, T, r, sigma) | |
| greeks['gamma'] = analytical.gamma(flag, S, K, T, r, sigma) | |
| greeks['theta'] = analytical.theta(flag, S, K, T, r, sigma) | |
| greeks['vega'] = analytical.vega(flag, S, K, T, r, sigma) | |
| except Exception as e: | |
| greeks = {'delta': np.nan, 'gamma': np.nan, 'theta': np.nan, 'vega': np.nan} | |
| return greeks | |
| def plot_greeks(options_data, recent_price, ticker): | |
| r = 0.01 | |
| calls_data = options_data[options_data["type"] == "call"].copy() | |
| puts_data = options_data[options_data["type"] == "put"].copy() | |
| for index, row in calls_data.iterrows(): | |
| T = (pd.to_datetime(row['expiration']) - pd.Timestamp.now()).days / 365.25 | |
| if T > 0 and row['impliedVolatility'] > 0: | |
| greeks = calculate_greeks("call", recent_price, row['strike'], T, r, row['impliedVolatility']) | |
| else: | |
| greeks = {'delta': np.nan, 'gamma': np.nan, 'theta': np.nan, 'vega': np.nan} | |
| for greek, value in greeks.items(): | |
| calls_data.at[index, greek] = value | |
| for index, row in puts_data.iterrows(): | |
| T = (pd.to_datetime(row['expiration']) - pd.Timestamp.now()).days / 365.25 | |
| if T > 0 and row['impliedVolatility'] > 0: | |
| greeks = calculate_greeks("put", recent_price, row['strike'], T, r, row['impliedVolatility']) | |
| else: | |
| greeks = {'delta': np.nan, 'gamma': np.nan, 'theta': np.nan, 'vega': np.nan} | |
| for greek, value in greeks.items(): | |
| puts_data.at[index, greek] = value | |
| fig = make_subplots(rows=2, cols=2, subplot_titles=["Delta", "Gamma", "Theta", "Vega"], shared_yaxes=True) | |
| fig.add_trace(go.Scatter(x=calls_data["strike"], y=calls_data["delta"], mode='markers', marker=dict(color='blue'), name="Delta (Calls)"), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=puts_data["strike"], y=puts_data["delta"], mode='markers', marker=dict(color='red'), name="Delta (Puts)"), row=1, col=1) | |
| fig.add_trace(go.Scatter(x=calls_data["strike"], y=calls_data["gamma"], mode='markers', marker=dict(color='blue'), name="Gamma (Calls)"), row=1, col=2) | |
| fig.add_trace(go.Scatter(x=puts_data["strike"], y=puts_data["gamma"], mode='markers', marker=dict(color='red'), name="Gamma (Puts)"), row=1, col=2) | |
| fig.add_trace(go.Scatter(x=calls_data["strike"], y=calls_data["theta"], mode='markers', marker=dict(color='blue'), name="Theta (Calls)"), row=2, col=1) | |
| fig.add_trace(go.Scatter(x=puts_data["strike"], y=puts_data["theta"], mode='markers', marker=dict(color='red'), name="Theta (Puts)"), row=2, col=1) | |
| fig.add_trace(go.Scatter(x=calls_data["strike"], y=calls_data["vega"], mode='markers', marker=dict(color='blue'), name="Vega (Calls)"), row=2, col=2) | |
| fig.add_trace(go.Scatter(x=puts_data["strike"], y=puts_data["vega"], mode='markers', marker=dict(color='red'), name="Vega (Puts)"), row=2, col=2) | |
| fig.update_layout(title=f"{ticker} Options Greeks by Strike - Current Price: {recent_price:.2f}", showlegend=True, legend_title_text='Options Type') | |
| fig.update_xaxes(title_text="Strike Price") | |
| fig.update_yaxes(title_text="Greeks Value") | |
| st.plotly_chart(fig) | |
| return calls_data, puts_data | |
| def interpret_greeks(ticker, calls_data, puts_data): | |
| interpretation = f"**Interpretation of {ticker} Options Greeks:**\n" | |
| avg_delta_calls = calls_data['delta'].mean() | |
| avg_gamma_calls = calls_data['gamma'].mean() | |
| avg_theta_calls = calls_data['theta'].mean() | |
| avg_vega_calls = calls_data['vega'].mean() | |
| avg_delta_puts = puts_data['delta'].mean() | |
| avg_gamma_puts = puts_data['gamma'].mean() | |
| avg_theta_puts = puts_data['theta'].mean() | |
| avg_vega_puts = puts_data['vega'].mean() | |
| interpretation += f"- **Delta Interpretation:**\n" | |
| interpretation += f" - Calls: The average delta for calls is {avg_delta_calls:.2f}. This indicates that on average, the call options' price changes by {avg_delta_calls:.2f} for a $1 change in the underlying stock price.\n" | |
| interpretation += f" - Puts: The average delta for puts is {avg_delta_puts:.2f}. This indicates that on average, the put options' price changes by {avg_delta_puts:.2f} for a $1 change in the underlying stock price.\n\n" | |
| interpretation += f"- **Gamma Interpretation:**\n" | |
| interpretation += f" - Calls: The average gamma for calls is {avg_gamma_calls:.2f}. This suggests that the delta of call options changes by {avg_gamma_calls:.2f} for a $1 change in the underlying stock price.\n" | |
| interpretation += f" - Puts: The average gamma for puts is {avg_gamma_puts:.2f}. This suggests that the delta of put options changes by {avg_gamma_puts:.2f} for a $1 change in the underlying stock price.\n\n" | |
| interpretation += f"- **Theta Interpretation:**\n" | |
| interpretation += f" - Calls: The average theta for calls is {avg_theta_calls:.2f}. This indicates that on average, the price of call options decreases by {avg_theta_calls:.2f} per day as time passes.\n" | |
| interpretation += f" - Puts: The average theta for puts is {avg_theta_puts:.2f}. This indicates that on average, the price of put options decreases by {avg_theta_puts:.2f} per day as time passes.\n\n" | |
| interpretation += f"- **Vega Interpretation:**\n" | |
| interpretation += f" - Calls: The average vega for calls is {avg_vega_calls:.2f}. This suggests that the price of call options changes by {avg_vega_calls:.2f} for a 1% change in the volatility of the underlying stock.\n" | |
| interpretation += f" - Puts: The average vega for puts is {avg_vega_puts:.2f}. This suggests that the price of put options changes by {avg_vega_puts:.2f} for a 1% change in the volatility of the underlying stock.\n\n" | |
| interpretation += "\n**Summary and Market Sentiment:**\n" | |
| if avg_delta_calls > avg_delta_puts: | |
| interpretation += "- The higher average delta for calls compared to puts indicates a bullish sentiment, as call options are more sensitive to upward movements in the stock price.\n" | |
| else: | |
| interpretation += "- The higher average delta for puts compared to calls indicates a bearish sentiment, as put options are more sensitive to downward movements in the stock price.\n" | |
| if avg_gamma_calls > avg_gamma_puts: | |
| interpretation += "- The higher average gamma for calls suggests that the sensitivity of call options to the underlying stock price is higher, indicating potential for larger price swings.\n" | |
| else: | |
| interpretation += "- The higher average gamma for puts suggests that the sensitivity of put options to the underlying stock price is higher, indicating potential for larger price swings.\n" | |
| if avg_theta_calls < avg_theta_puts: | |
| interpretation += "- The more negative average theta for calls indicates that call options lose value faster over time compared to puts, which may reflect higher time decay for bullish positions.\n" | |
| else: | |
| interpretation += "- The more negative average theta for puts indicates that put options lose value faster over time compared to calls, which may reflect higher time decay for bearish positions.\n" | |
| if avg_vega_calls > avg_vega_puts: | |
| interpretation += "- The higher average vega for calls suggests that call options are more sensitive to changes in volatility, which can be beneficial in volatile markets for bullish strategies.\n" | |
| else: | |
| interpretation += "- The higher average vega for puts suggests that put options are more sensitive to changes in volatility, which can be beneficial in volatile markets for bearish strategies.\n" | |
| st.markdown(interpretation) | |
| def calculate_sentiment_score(options_data, high_iv_calls, low_iv_puts, total_calls, total_puts): | |
| sentiment_score = (high_iv_calls - low_iv_puts) + (total_calls - total_puts) / (total_calls + total_puts) | |
| sentiment_description = "Bullish" if sentiment_score > 0 else "Bearish" | |
| st.write(f"Sentiment Score: {sentiment_score:.2f} ({sentiment_description})") | |
| return sentiment_score, sentiment_description | |
| def interpret_sentiment_score(ticker, sentiment_score, sentiment_description, high_iv_calls, low_iv_puts, total_calls, total_puts): | |
| interpretation = f"**Interpretation of {ticker} Sentiment Score:**\n" | |
| interpretation += f"- The sentiment score is {sentiment_score:.2f}, indicating a {sentiment_description} market sentiment.\n" | |
| iv_diff = high_iv_calls - low_iv_puts | |
| volume_diff = (total_calls - total_puts) / (total_calls + total_puts) | |
| interpretation += f"- Contribution from Implied Volatility difference (high IV of calls - low IV of puts): {iv_diff:.2f}\n" | |
| interpretation += f"- Contribution from Volume difference (total calls - total puts) / (total calls + total puts): {volume_diff:.2f}\n" | |
| if sentiment_description == "Bullish": | |
| interpretation += "- The bullish sentiment is driven by higher implied volatility in calls compared to puts, indicating expectations of upward price movements. Additionally, a higher volume of calls compared to puts suggests more traders are positioning for a rise in the stock price.\n" | |
| else: | |
| interpretation += "- The bearish sentiment is driven by higher implied volatility in puts compared to calls, indicating expectations of downward price movements. Additionally, a higher volume of puts compared to calls suggests more traders are positioning for a decline in the stock price.\n" | |
| st.markdown(interpretation) | |
| def overall_interpretation(ticker, | |
| overall_avg_iv_calls, overall_avg_iv_puts, avg_iv_by_strike_calls, avg_iv_by_strike_puts, | |
| overall_avg_vol_calls, overall_avg_vol_puts, calls_data, puts_data, | |
| historical_iv, current_iv, | |
| put_call_ratio, | |
| sentiment_score, sentiment_description, high_iv_calls, low_iv_puts, total_calls, total_puts, | |
| avg_delta_calls, avg_delta_puts, avg_gamma_calls, avg_gamma_puts, avg_theta_calls, avg_theta_puts, avg_vega_calls, avg_vega_puts): | |
| interpretation = f"**Overall Interpretation of {ticker} Market Sentiment and Conditions:**\n\n" | |
| # Volatility Smile Interpretation | |
| interpretation += f"**Volatility Smile Analysis:**\n" | |
| interpretation += f"- The average implied volatility for call options is {overall_avg_iv_calls:.2f}%.\n" | |
| interpretation += f"- The average implied volatility for put options is {overall_avg_iv_puts:.2f}%.\n" | |
| interpretation += f"- The market shows {'a significant' if avg_iv_by_strike_calls.var() > 0.1 else 'a stable'} volatility smile for call options.\n" | |
| interpretation += f"- The market shows {'a significant' if avg_iv_by_strike_puts.var() > 0.1 else 'a stable'} volatility smile for put options.\n" | |
| interpretation += f"- The overall market sentiment from volatility is {'bullish' if overall_avg_iv_calls > overall_avg_iv_puts else 'bearish'}.\n\n" | |
| # Volume Analysis Interpretation | |
| interpretation += f"**Volume Analysis:**\n" | |
| interpretation += f"- The average volume for call options is {overall_avg_vol_calls:.2f} contracts.\n" | |
| interpretation += f"- The average volume for put options is {overall_avg_vol_puts:.2f} contracts.\n" | |
| interpretation += f"- The higher volume in {'call' if overall_avg_vol_calls > overall_avg_vol_puts else 'put'} options indicates higher trading activity and interest.\n" | |
| highest_vol_call = calls_data.loc[calls_data['volume'].idxmax()] | |
| highest_vol_put = puts_data.loc[puts_data['volume'].idxmax()] | |
| interpretation += f"- The highest volume for calls is at strike {highest_vol_call['strike']} with {highest_vol_call['volume']} contracts.\n" | |
| interpretation += f"- The highest volume for puts is at strike {highest_vol_put['strike']} with {highest_vol_put['volume']} contracts.\n\n" | |
| # Historical IV Interpretation | |
| avg_iv = historical_iv["IV"].mean() | |
| max_iv = historical_iv["IV"].max() | |
| min_iv = historical_iv["IV"].min() | |
| interpretation += f"**Historical Implied Volatility Analysis:**\n" | |
| interpretation += f"- The average implied volatility over the period is {avg_iv:.2f}%.\n" | |
| interpretation += f"- The maximum implied volatility recorded was {max_iv:.2f}%.\n" | |
| interpretation += f"- The minimum implied volatility recorded was {min_iv:.2f}%.\n" | |
| interpretation += f"- The current implied volatility is {current_iv:.2f}%.\n" | |
| recent_trend = "increased" if historical_iv['IV'].iloc[-1] > historical_iv['IV'].iloc[-30] else "decreased" | |
| interpretation += f"- In the last 30 days, the implied volatility has {recent_trend} compared to the previous period.\n\n" | |
| # Put/Call Ratio Interpretation | |
| interpretation += f"**Put/Call Ratio Analysis:**\n" | |
| interpretation += f"- The Put/Call Ratio is {put_call_ratio:.2f}.\n" | |
| interpretation += f"- Total volume of puts: {total_puts}\n" | |
| interpretation += f"- Total volume of calls: {total_calls}\n" | |
| if put_call_ratio > 1: | |
| interpretation += "- A Put/Call Ratio greater than 1 indicates a bearish sentiment, with more puts being traded relative to calls.\n\n" | |
| elif put_call_ratio < 1: | |
| interpretation += "- A Put/Call Ratio less than 1 indicates a bullish sentiment, with more calls being traded relative to puts.\n\n" | |
| else: | |
| interpretation += "- A Put/Call Ratio of 1 indicates a neutral sentiment, with equal volumes of puts and calls being traded.\n\n" | |
| # Greeks Analysis Interpretation | |
| interpretation += f"**Options Greeks Analysis:**\n" | |
| interpretation += f"- **Delta:**\n" | |
| interpretation += f" - Calls: The average delta for calls is {avg_delta_calls:.2f}.\n" | |
| interpretation += f" - Puts: The average delta for puts is {avg_delta_puts:.2f}.\n" | |
| interpretation += f"- **Gamma:**\n" | |
| interpretation += f" - Calls: The average gamma for calls is {avg_gamma_calls:.2f}.\n" | |
| interpretation += f" - Puts: The average gamma for puts is {avg_gamma_puts:.2f}.\n" | |
| interpretation += f"- **Theta:**\n" | |
| interpretation += f" - Calls: The average theta for calls is {avg_theta_calls:.2f}.\n" | |
| interpretation += f" - Puts: The average theta for puts is {avg_theta_puts:.2f}.\n" | |
| interpretation += f"- **Vega:**\n" | |
| interpretation += f" - Calls: The average vega for calls is {avg_vega_calls:.2f}.\n" | |
| interpretation += f" - Puts: The average vega for puts is {avg_vega_puts:.2f}.\n" | |
| if avg_delta_calls > avg_delta_puts: | |
| interpretation += "- The higher average delta for calls suggests a bullish sentiment as calls are more sensitive to upward movements.\n" | |
| else: | |
| interpretation += "- The higher average delta for puts suggests a bearish sentiment as puts are more sensitive to downward movements.\n" | |
| if avg_gamma_calls > avg_gamma_puts: | |
| interpretation += "- The higher average gamma for calls indicates higher sensitivity of delta to underlying price changes, suggesting larger price swings for calls.\n" | |
| else: | |
| interpretation += "- The higher average gamma for puts indicates higher sensitivity of delta to underlying price changes, suggesting larger price swings for puts.\n" | |
| if avg_theta_calls < avg_theta_puts: | |
| interpretation += "- The more negative average theta for calls indicates higher time decay for bullish positions.\n" | |
| else: | |
| interpretation += "- The more negative average theta for puts indicates higher time decay for bearish positions.\n" | |
| if avg_vega_calls > avg_vega_puts: | |
| interpretation += "- The higher average vega for calls suggests calls are more sensitive to volatility changes, beneficial in volatile markets for bullish strategies.\n" | |
| else: | |
| interpretation += "- The higher average vega for puts suggests puts are more sensitive to volatility changes, beneficial in volatile markets for bearish strategies.\n\n" | |
| # Sentiment Score Interpretation | |
| interpretation += f"**Sentiment Score Analysis:**\n" | |
| interpretation += f"- The sentiment score is {sentiment_score:.2f}, indicating a {sentiment_description} market sentiment.\n" | |
| interpretation += f"- Contribution from Implied Volatility difference (high IV of calls - low IV of puts): {high_iv_calls - low_iv_puts:.2f}\n" | |
| interpretation += f"- Contribution from Volume difference (total calls - total puts) / (total calls + total puts): {(total_calls - total_puts) / (total_calls + total_puts):.2f}\n" | |
| if sentiment_description == "Bullish": | |
| interpretation += "- The bullish sentiment is driven by higher implied volatility in calls and higher call volumes, indicating expectations of upward price movements.\n" | |
| else: | |
| interpretation += "- The bearish sentiment is driven by higher implied volatility in puts and higher put volumes, indicating expectations of downward price movements.\n" | |
| st.markdown(interpretation) | |
| with st.sidebar.expander("Input Parameters", expanded=True): | |
| ticker = st.sidebar.text_input("Enter Ticker Symbol:", "AAPL", help="Enter the stock ticker symbol (e.g., AAPL for Apple Inc.).") | |
| start_date = st.sidebar.date_input("Select Start Date", datetime(2020, 1, 1), help="Select the start date for the analysis.") | |
| if st.sidebar.button("Run Analysis"): | |
| options_data, recent_price = get_options_data(ticker) | |
| if options_data is not None: | |
| st.subheader("Volatility Smile Plot") | |
| overall_avg_iv_calls, overall_avg_iv_puts, avg_iv_by_strike_calls, avg_iv_by_strike_puts, expirations = plot_volatility_smile(options_data, recent_price, ticker) | |
| with st.expander("Volatility Smile Interpretation", expanded=False): | |
| interpret_volatility_smile(ticker, overall_avg_iv_calls, overall_avg_iv_puts, avg_iv_by_strike_calls, avg_iv_by_strike_puts) | |
| st.subheader("Open Interest") | |
| overall_avg_oi_calls, overall_avg_oi_puts, calls_data, puts_data = plot_open_interest(options_data, recent_price, ticker) | |
| with st.expander("Open Interest Interpretation", expanded=False): | |
| interpret_open_interest(ticker, overall_avg_oi_calls, overall_avg_oi_puts, calls_data, puts_data) | |
| st.subheader("Volume Analysis") | |
| overall_avg_vol_calls, overall_avg_vol_puts, calls_data, puts_data = plot_volume(options_data, recent_price, ticker) | |
| with st.expander("Volume Analysis Interpretation", expanded=False): | |
| interpret_volume(ticker, overall_avg_vol_calls, overall_avg_vol_puts, calls_data, puts_data) | |
| st.subheader("Volatility Surface Plot") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.markdown("### Puts") | |
| puts_data = plot_3d_puts_implied_volatility(options_data, ticker) | |
| with st.expander("3D Puts Implied Volatility Interpretation", expanded=False): | |
| interpret_3d_puts_implied_volatility(ticker, puts_data) | |
| with col2: | |
| st.markdown("### Calls") | |
| calls_data = plot_3d_calls_implied_volatility(options_data, ticker) | |
| with st.expander("3D Calls Implied Volatility Interpretation", expanded=False): | |
| interpret_3d_calls_implied_volatility(ticker, calls_data) | |
| st.subheader("6. Historical IV Approximation") | |
| historical_iv, current_iv = plot_historical_iv(ticker, start_date) | |
| with st.expander("Historical IV Interpretation", expanded=False): | |
| interpret_historical_iv(ticker, historical_iv, current_iv) | |
| st.subheader("Put to Call Ratio") | |
| total_puts, total_calls, put_call_ratio = calculate_put_call_ratio(options_data) | |
| with st.expander("Put to Call Ratio Interpretation", expanded=False): | |
| interpret_put_call_ratio(ticker, total_puts, total_calls, put_call_ratio) | |
| st.subheader("Greeks Analysis") | |
| calls_data, puts_data = plot_greeks(options_data, recent_price, ticker) | |
| with st.expander("Greeks Analysis Interpretation", expanded=False): | |
| interpret_greeks(ticker, calls_data, puts_data) | |
| st.subheader("Sentiment Score") | |
| high_iv_calls = overall_avg_iv_calls | |
| low_iv_puts = overall_avg_iv_puts | |
| sentiment_score, sentiment_description = calculate_sentiment_score(options_data, high_iv_calls, low_iv_puts, total_calls, total_puts) | |
| with st.expander("Sentiment Score Interpretation", expanded=False): | |
| interpret_sentiment_score(ticker, sentiment_score, sentiment_description, high_iv_calls, low_iv_puts, total_calls, total_puts) | |
| # Add the overall interpretation here | |
| # st.subheader("10. Overall Interpretation") | |
| # avg_delta_calls = calls_data['delta'].mean() | |
| # avg_gamma_calls = calls_data['gamma'].mean() | |
| # avg_theta_calls = calls_data['theta'].mean() | |
| # avg_vega_calls = calls_data['vega'].mean() | |
| # avg_delta_puts = puts_data['delta'].mean() | |
| # avg_gamma_puts = puts_data['gamma'].mean() | |
| # avg_theta_puts = puts_data['theta'].mean() | |
| # avg_vega_puts = puts_data['vega'].mean() | |
| # overall_interpretation(ticker, | |
| # overall_avg_iv_calls, overall_avg_iv_puts, avg_iv_by_strike_calls, avg_iv_by_strike_puts, | |
| # overall_avg_vol_calls, overall_avg_vol_puts, calls_data, puts_data, | |
| # historical_iv, current_iv, | |
| # put_call_ratio, | |
| # sentiment_score, sentiment_description, high_iv_calls, low_iv_puts, total_calls, total_puts, | |
| # avg_delta_calls, avg_delta_puts, avg_gamma_calls, avg_gamma_puts, avg_theta_calls, avg_theta_puts, avg_vega_calls, avg_vega_puts) | |
| # Hide the Streamlit style | |
| hide_streamlit_style = """ | |
| <style> | |
| #MainMenu {visibility: hidden;} | |
| footer {visibility: hidden;} | |
| </style> | |
| """ | |
| st.markdown(hide_streamlit_style, unsafe_allow_html=True) | |