Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import yfinance as yf
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import plotly.express as px
|
| 5 |
+
import plotly.graph_objects as go
|
| 6 |
+
from plotly.subplots import make_subplots
|
| 7 |
+
from datetime import datetime
|
| 8 |
+
|
| 9 |
+
st.set_page_config(layout="wide")
|
| 10 |
+
|
| 11 |
+
def analyze_options(ticker):
|
| 12 |
+
asset = yf.Ticker(ticker)
|
| 13 |
+
exp_dates = asset.options
|
| 14 |
+
|
| 15 |
+
if not exp_dates:
|
| 16 |
+
st.error(f"No options data available for ticker {ticker}.")
|
| 17 |
+
return
|
| 18 |
+
|
| 19 |
+
recent_price = asset.history(period="1d")["Close"].iloc[-1]
|
| 20 |
+
|
| 21 |
+
options_data = []
|
| 22 |
+
for date in exp_dates:
|
| 23 |
+
calls = asset.option_chain(date).calls
|
| 24 |
+
puts = asset.option_chain(date).puts
|
| 25 |
+
calls["expiration"] = date
|
| 26 |
+
puts["expiration"] = date
|
| 27 |
+
calls["type"] = "call"
|
| 28 |
+
puts["type"] = "put"
|
| 29 |
+
data = pd.concat([calls, puts])
|
| 30 |
+
options_data.append(data)
|
| 31 |
+
|
| 32 |
+
if not options_data:
|
| 33 |
+
st.error(f"No valid options data available for ticker {ticker}.")
|
| 34 |
+
return
|
| 35 |
+
|
| 36 |
+
options_data = pd.concat(options_data)
|
| 37 |
+
options_data = options_data[options_data["strike"].between(recent_price * 0.9, recent_price * 1.1)]
|
| 38 |
+
options_data["implied_volatility"] = options_data["impliedVolatility"] * 100
|
| 39 |
+
|
| 40 |
+
calls_data = options_data[options_data["type"] == "call"]
|
| 41 |
+
puts_data = options_data[options_data["type"] == "put"]
|
| 42 |
+
|
| 43 |
+
expirations = options_data['expiration'].unique()
|
| 44 |
+
color_map_2d = px.colors.qualitative.Prism
|
| 45 |
+
|
| 46 |
+
fig = make_subplots(rows=1, cols=2, subplot_titles=["Calls", "Puts"], shared_yaxes=True)
|
| 47 |
+
|
| 48 |
+
for exp, color in zip(expirations, color_map_2d):
|
| 49 |
+
exp_calls = calls_data[calls_data["expiration"] == exp]
|
| 50 |
+
exp_puts = puts_data[puts_data["expiration"] == exp]
|
| 51 |
+
|
| 52 |
+
fig.add_trace(go.Scatter(x=exp_calls["strike"], y=exp_calls["implied_volatility"], mode='markers',
|
| 53 |
+
marker=dict(color=color), name=exp), row=1, col=1)
|
| 54 |
+
fig.add_trace(go.Scatter(x=exp_puts["strike"], y=exp_puts["implied_volatility"], mode='markers',
|
| 55 |
+
marker=dict(color=color), name=exp, showlegend=False), row=1, col=2)
|
| 56 |
+
|
| 57 |
+
avg_iv_by_strike_calls = calls_data.groupby("strike")["implied_volatility"].mean()
|
| 58 |
+
avg_iv_by_strike_puts = puts_data.groupby("strike")["implied_volatility"].mean()
|
| 59 |
+
|
| 60 |
+
fig.add_trace(go.Scatter(x=avg_iv_by_strike_calls.index, y=avg_iv_by_strike_calls.values, mode='lines',
|
| 61 |
+
line=dict(color='black', dash='dash'), name='Avg. IV by Strike (Calls)'), row=1, col=1)
|
| 62 |
+
fig.add_trace(go.Scatter(x=avg_iv_by_strike_puts.index, y=avg_iv_by_strike_puts.values, mode='lines',
|
| 63 |
+
line=dict(color='black', dash='dash'), name='Avg. IV by Strike (Puts)', showlegend=False), row=1, col=2)
|
| 64 |
+
|
| 65 |
+
overall_avg_iv_calls = calls_data["implied_volatility"].mean()
|
| 66 |
+
overall_avg_iv_puts = puts_data["implied_volatility"].mean()
|
| 67 |
+
|
| 68 |
+
fig.add_hline(y=overall_avg_iv_calls, line=dict(color='gray', dash='dash'),
|
| 69 |
+
annotation_text=f"Overall Avg. IV (Calls): {overall_avg_iv_calls:.2f}%",
|
| 70 |
+
row=1, col=1)
|
| 71 |
+
fig.add_hline(y=overall_avg_iv_puts, line=dict(color='gray', dash='dash'),
|
| 72 |
+
annotation_text=f"Overall Avg. IV (Puts): {overall_avg_iv_puts:.2f}%",
|
| 73 |
+
row=1, col=2)
|
| 74 |
+
|
| 75 |
+
fig.update_layout(title=f"{ticker} Volatility Smile - Current Price: {recent_price:.2f}", showlegend=True, legend_title_text='Expiration Date')
|
| 76 |
+
fig.update_xaxes(title_text="Strike Price")
|
| 77 |
+
fig.update_yaxes(title_text="Implied Volatility (%)")
|
| 78 |
+
|
| 79 |
+
st.plotly_chart(fig)
|
| 80 |
+
|
| 81 |
+
color_map_3d = {exp: color for exp, color in zip(expirations, px.colors.qualitative.Prism)}
|
| 82 |
+
|
| 83 |
+
fig1 = px.scatter_3d(puts_data, x='strike', y='expiration', z='implied_volatility',
|
| 84 |
+
color='expiration', color_discrete_map=color_map_3d,
|
| 85 |
+
title=f'{ticker} Put Options Implied Volatility',
|
| 86 |
+
labels={'strike': 'Strike Price', 'expiration': 'Expiration Date', 'implied_volatility': 'Implied Volatility (%)'},
|
| 87 |
+
hover_name='expiration')
|
| 88 |
+
|
| 89 |
+
fig2 = px.scatter_3d(calls_data, x='strike', y='expiration', z='implied_volatility',
|
| 90 |
+
color='expiration', color_discrete_map=color_map_3d,
|
| 91 |
+
title=f'{ticker} Call Options Implied Volatility',
|
| 92 |
+
labels={'strike': 'Strike Price', 'expiration': 'Expiration Date', 'implied_volatility': 'Implied Volatility (%)'},
|
| 93 |
+
hover_name='expiration')
|
| 94 |
+
|
| 95 |
+
col1, col2 = st.columns(2)
|
| 96 |
+
with col1:
|
| 97 |
+
st.plotly_chart(fig1, use_container_width=True)
|
| 98 |
+
with col2:
|
| 99 |
+
st.plotly_chart(fig2, use_container_width=True)
|
| 100 |
+
|
| 101 |
+
high_iv_calls = calls_data["implied_volatility"].mean()
|
| 102 |
+
low_iv_puts = puts_data["implied_volatility"].mean()
|
| 103 |
+
near_term_expirations = expirations[:len(expirations)//2]
|
| 104 |
+
long_term_expirations = expirations[len(expirations)//2:]
|
| 105 |
+
|
| 106 |
+
market_sentiment = "bullish" if high_iv_calls > low_iv_puts else "bearish"
|
| 107 |
+
sentiment_reason = "higher implied volatility for call options suggests positive sentiment or potential stock price increase." if market_sentiment == "bullish" else "higher implied volatility for put options suggests negative sentiment or potential stock price decrease."
|
| 108 |
+
|
| 109 |
+
near_term_iv = calls_data[calls_data["expiration"].isin(near_term_expirations)]["implied_volatility"].mean()
|
| 110 |
+
long_term_iv = calls_data[calls_data["expiration"].isin(long_term_expirations)]["implied_volatility"].mean()
|
| 111 |
+
|
| 112 |
+
term_outlook = f"Short-term concerns due to higher implied volatility for near-term expirations ({near_term_iv:.2f}%)." if near_term_iv > long_term_iv else f"Long-term stability as the implied volatility for near-term expirations is lower ({near_term_iv:.2f}%)."
|
| 113 |
+
|
| 114 |
+
expiry_insight = f"The higher implied volatility in near-term expirations ({near_term_iv:.2f}%) indicates that traders expect more significant price movements soon." if near_term_iv > long_term_iv else f"Lower implied volatility in near-term expirations ({near_term_iv:.2f}%) suggests traders do not expect significant price movements soon."
|
| 115 |
+
|
| 116 |
+
long_term_stability = f"Long-term expirations with lower implied volatility ({long_term_iv:.2f}%) suggest confidence in the stock's stability over a longer horizon." if long_term_iv < near_term_iv else f"Long-term expirations with higher implied volatility ({long_term_iv:.2f}%) suggest uncertainty about the stock's stability over a longer horizon."
|
| 117 |
+
|
| 118 |
+
risk_assessment = "Higher call option volatility often points to potential upside, but it also brings higher risk." if high_iv_calls > 20 else "Lower call option volatility suggests lower risk but also lower potential upside."
|
| 119 |
+
|
| 120 |
+
interpretation = f"""
|
| 121 |
+
**Analysis of {ticker} Options:**
|
| 122 |
+
|
| 123 |
+
**Call Options:**
|
| 124 |
+
- Average Implied Volatility: {high_iv_calls:.2f}%
|
| 125 |
+
|
| 126 |
+
**Put Options:**
|
| 127 |
+
- Average Implied Volatility: {low_iv_puts:.2f}%
|
| 128 |
+
|
| 129 |
+
**Market Sentiment:**
|
| 130 |
+
- The market sentiment is {market_sentiment}. This is because {sentiment_reason}
|
| 131 |
+
|
| 132 |
+
**Expiration Dates Analysis:**
|
| 133 |
+
- Near-term Expirations: Implied Volatility = {near_term_iv:.2f}%
|
| 134 |
+
- Long-term Expirations: Implied Volatility = {long_term_iv:.2f}%
|
| 135 |
+
- {term_outlook}
|
| 136 |
+
|
| 137 |
+
**Detailed Expiry Insight:**
|
| 138 |
+
- {expiry_insight}
|
| 139 |
+
- {long_term_stability}
|
| 140 |
+
|
| 141 |
+
**Risk Assessment:**
|
| 142 |
+
- {risk_assessment}
|
| 143 |
+
"""
|
| 144 |
+
|
| 145 |
+
st.markdown(interpretation)
|
| 146 |
+
|
| 147 |
+
def main():
|
| 148 |
+
st.title("Options Sentiment Analysis Tool")
|
| 149 |
+
st.markdown("""
|
| 150 |
+
## Options Sentiment Analysis Tool
|
| 151 |
+
|
| 152 |
+
This tool analyzes options data for a given stock ticker symbol to gauge market sentiment. By examining the implied volatility of call and put options, you can infer the market's expectations of future price movements and overall sentiment.
|
| 153 |
+
|
| 154 |
+
**Implied Volatility (IV)** is a critical concept in options trading. It 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.
|
| 155 |
+
|
| 156 |
+
### How It Works:
|
| 157 |
+
1. **Input Ticker Symbol**: Enter the stock ticker symbol in the sidebar (e.g., `AAPL` for Apple Inc.).
|
| 158 |
+
2. **Analyze**: Click the "Analyze" button to fetch and process the options data.
|
| 159 |
+
3. **Visualize**: View the visualizations and interpretations of the options data to understand market sentiment.
|
| 160 |
+
|
| 161 |
+
### Understanding Implied Volatility:
|
| 162 |
+
Implied volatility is derived from the market price of an option. It reflects the market's view of the likelihood of changes in a given security's price. Higher implied volatility means the market expects the stock to have large price swings (either up or down). Here’s a simplified representation of how implied volatility is calculated.
|
| 163 |
+
""")
|
| 164 |
+
|
| 165 |
+
st.latex(r'''
|
| 166 |
+
IV = \frac{Market \, Price \, of \, Option - Intrinsic \, Value}{Time \, Value \, of \, Money \, + \, Volatility \, Expectation}
|
| 167 |
+
''')
|
| 168 |
+
|
| 169 |
+
st.markdown("""
|
| 170 |
+
|
| 171 |
+
In practice, implied volatility (IV) 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.
|
| 172 |
+
|
| 173 |
+
### Visualizations:
|
| 174 |
+
- **Volatility Smile**: Displays the implied volatility of call and put options across different strike prices and expiration dates, providing insights into expected price volatility.
|
| 175 |
+
- **3D Scatter Plot**: Shows the implied volatility of options in a 3D view, categorized by expiration date, to identify patterns and sentiment shifts.
|
| 176 |
+
- **Market Sentiment**: Indicates whether the market sentiment is bullish or bearish based on the average implied volatility of call and put options.
|
| 177 |
+
- **Expiration Dates Analysis**: Provides insights into the implied volatility for near-term and long-term expiration dates, highlighting market expectations over different time horizons.
|
| 178 |
+
- **Risk Assessment**: Assesses the risk based on the implied volatility of call options, helping to understand potential price swings.
|
| 179 |
+
|
| 180 |
+
Use this tool to gain a deeper understanding of market sentiment and potential price movements based on options data.
|
| 181 |
+
""")
|
| 182 |
+
|
| 183 |
+
st.sidebar.title("Input Parameters")
|
| 184 |
+
|
| 185 |
+
ticker = st.sidebar.text_input("Ticker Symbol", value="ASML", max_chars=10)
|
| 186 |
+
|
| 187 |
+
if st.sidebar.button("Analyze"):
|
| 188 |
+
today = datetime.today().strftime('%Y-%m-%d')
|
| 189 |
+
st.subheader(f"Options Price Sentiment Analysis for {ticker} - {today}")
|
| 190 |
+
analyze_options(ticker)
|
| 191 |
+
|
| 192 |
+
if __name__ == "__main__":
|
| 193 |
+
main()
|
| 194 |
+
|
| 195 |
+
hide_streamlit_style = """
|
| 196 |
+
<style>
|
| 197 |
+
#MainMenu {visibility: hidden;}
|
| 198 |
+
footer {visibility: hidden;}
|
| 199 |
+
</style>
|
| 200 |
+
"""
|
| 201 |
+
st.markdown(hide_streamlit_style, unsafe_allow_html=True)
|