|
import streamlit as st |
|
import yfinance as yf |
|
import numpy as np |
|
import pandas as pd |
|
from plotly.subplots import make_subplots |
|
import plotly.graph_objects as go |
|
|
|
def analyze_and_visualize_straddle(ticker, strike_call, strike_put, premium_call, premium_put): |
|
""" |
|
This function is used to analyze a given ticker and visualize the payoffs for both long and short straddle strategies. |
|
|
|
Parameters: |
|
- ticker: Symbol of the underlying asset as is on Yahoo Finance. |
|
- strike_call: Strike price for the call option. |
|
- strike_put: Strike price for the put option. |
|
- premium_call: Premium paid for the call option. |
|
- premium_put: Premium paid for the put option. |
|
""" |
|
|
|
|
|
df = yf.download(ticker, multi_level_index=False, auto_adjust=False) |
|
|
|
|
|
df['Returns'] = df['Adj Close'].pct_change() |
|
|
|
|
|
df['Vol'] = df['Returns'].rolling(20).std() * np.sqrt(252) |
|
df.dropna(axis = 0, inplace = True) |
|
|
|
|
|
quantiles = np.quantile(df['Vol'], np.linspace(0,1,11)) |
|
|
|
|
|
quantile_labels = pd.cut(df['Vol'], bins = quantiles, |
|
labels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) |
|
|
|
|
|
df['Vol_Rank'] = quantile_labels |
|
|
|
|
|
fig = make_subplots(rows = 3, cols = 1, horizontal_spacing=0.2, row_heights=[.60, .20, .20], shared_xaxes=True) |
|
|
|
|
|
fig.add_trace(go.Scatter( |
|
x = df.index, y = df['Adj Close'], name = 'Closing Price' |
|
), row = 1, col = 1) |
|
|
|
|
|
fig.add_trace(go.Scatter( |
|
x = df.index, y = df['Vol'], name = 'Annualized Volatility' |
|
), row = 2, col = 1) |
|
|
|
|
|
fig.add_trace(go.Scatter( |
|
x = df.index, y = df['Vol_Rank'], name = 'Volatility Rank' |
|
), row = 3, col = 1) |
|
|
|
|
|
fig.update_layout(title = {'text': f'<b>{ticker} Closing Price & Volatility</b>'}, |
|
template = 'plotly_white', |
|
height = 900, width = 950, |
|
showlegend=False, |
|
hovermode='x unified') |
|
|
|
|
|
fig.update_yaxes(title_text = 'Closing Price ($)', row = 1) |
|
fig.update_yaxes(title_text = 'Annualized Volatility', row = 2) |
|
fig.update_yaxes(title_text = 'Volatility Rank', row = 3) |
|
|
|
|
|
stock_prices = np.linspace(0.79 * min(strike_put, strike_call), 1.19 * max(strike_put, strike_call), 100).round(2) |
|
|
|
|
|
|
|
long_call_payoff = np.maximum(stock_prices - strike_call, 0) - premium_call |
|
long_put_payoff = np.maximum(strike_put - stock_prices, 0) - premium_put |
|
|
|
|
|
short_call_payoff = -long_call_payoff |
|
short_put_payoff = -long_put_payoff |
|
|
|
|
|
combined_long_payoff = long_call_payoff + long_put_payoff |
|
combined_short_payoff = short_call_payoff + short_put_payoff |
|
|
|
|
|
profit_long = np.maximum(combined_long_payoff, 0) |
|
loss_long = np.minimum(combined_long_payoff, 0) |
|
|
|
|
|
loss_short = np.minimum(combined_short_payoff, 0) |
|
profit_short = np.maximum(combined_short_payoff, 0) |
|
|
|
|
|
fig2 = make_subplots( |
|
rows=2, cols=1, |
|
horizontal_spacing=0.2, |
|
row_heights=[0.5, 0.5], |
|
shared_xaxes=False, |
|
shared_yaxes=False |
|
) |
|
|
|
|
|
fig2.add_trace( |
|
go.Scatter(x=stock_prices, y=long_call_payoff, mode='lines', line=dict(color='grey', dash='dash'), name='Long Call', hovertemplate='%{y:.2f}'), |
|
row=1, col=1 |
|
) |
|
fig2.add_trace( |
|
go.Scatter(x=stock_prices, y=long_put_payoff, mode='lines', line=dict(color='grey', dash='dash'), name='Long Put', hovertemplate='%{y:.2f}'), |
|
row=1, col=1 |
|
) |
|
|
|
|
|
fig2.add_trace( |
|
go.Scatter(x=stock_prices, y=short_call_payoff, mode='lines', line=dict(color='grey', dash='dash'), name='Short Call', hovertemplate='%{y:.2f}'), |
|
row=2, col=1 |
|
) |
|
fig2.add_trace( |
|
go.Scatter(x=stock_prices, y=short_put_payoff, mode='lines', line=dict(color='grey', dash='dash'), name='Short Put', hovertemplate='%{y:.2f}'), |
|
row=2, col=1 |
|
) |
|
|
|
|
|
|
|
fig2.add_trace( |
|
go.Scatter(x=stock_prices, y=profit_long, mode='lines', line=dict(color='black', dash='solid'), name='Profit', hovertemplate='%{y:.2f}', |
|
fill='tozeroy', fillcolor='rgba(0, 255, 0, 0.5)'), |
|
row=1, col=1 |
|
) |
|
fig2.add_trace( |
|
go.Scatter(x=stock_prices, y=loss_long, mode='lines', line=dict(color='black', dash='solid'), name='Loss', hovertemplate='%{y:.2f}', |
|
fill='tozeroy', fillcolor='rgba(255, 0, 0, 0.5)'), |
|
row=1, col=1 |
|
) |
|
|
|
|
|
fig2.add_trace( |
|
go.Scatter(x=stock_prices, y=loss_short, mode='lines', line=dict(color='black', dash='solid'), name = 'Loss', hovertemplate='%{y:.2f}', |
|
fill='tozeroy', fillcolor='rgba(255, 0, 0, 0.5)'), |
|
row=2, col=1 |
|
) |
|
fig2.add_trace( |
|
go.Scatter(x=stock_prices, y=profit_short, mode='lines', line=dict(color='black', dash='solid'), name = 'Profit', hovertemplate='%{y:.2f}', |
|
fill='tozeroy', fillcolor='rgba(0, 255, 0, 0.5)'), |
|
row=2, col=1 |
|
) |
|
|
|
|
|
|
|
fig2.update_layout( |
|
title={'text': f'<b>{ticker} Long & Short Straddle</b>'}, |
|
template='plotly_white', |
|
height=1000, width=750, |
|
showlegend=False, |
|
hovermode='x unified') |
|
|
|
|
|
fig2.update_yaxes(title_text='Long Payoff ($)', row=1) |
|
fig2.update_yaxes(title_text='Short Payoff ($)', row=2) |
|
fig2.update_xaxes(title_text = 'Underlying Asset Price at Expiration', row=1) |
|
fig2.update_xaxes(title_text = 'Underlying Asset Price at Expiration', row=2) |
|
|
|
|
|
return fig, fig2 |
|
|
|
|
|
st.set_page_config(page_title="Long and Short Straddle", |
|
page_icon=":chart_with_upwards_trend", |
|
layout="wide", |
|
initial_sidebar_state="expanded", |
|
|
|
) |
|
|
|
title = '<h1 style="font-family:Didot; font-size: 56px; text-align: left"><b>Options Trading: Long & Short Straddle</b></h1>' |
|
st.markdown(title, unsafe_allow_html=True) |
|
|
|
st.markdown(""" |
|
This app helps you compute the payoffs for long and short straddles for options trading. It will also plot the historical closing prices for the |
|
underlying asset, its annualized volatility, and its volatility rank.""") |
|
|
|
st.markdown(""" |
|
Annualized volatility is computed by multiplying the standard deviation of the last 20 trading days by the square root |
|
of 252. The volatility rank is computed by breaking down the annualized volatility into ten equally-sized bins, and it |
|
ranges from 1 to 10, where 1 represents low volatility, and 10 represents high volatility.""") |
|
|
|
|
|
st.markdown('For more information about options and straddles, you can see my Kaggle Notebook, [Options Trading: Long & Short Straddle 📈](https://www.kaggle.com/code/lusfernandotorres/options-trading-long-short-straddle), where I describe the process of building this demo app.') |
|
|
|
st.write(f"""**Parameters:**""") |
|
|
|
st.write(f"""**• Ticker:** The asset symbol as it is on Yahoo Finance.""") |
|
st.write(f"""**• Strike Price (Call):** Strike price for the call option.""") |
|
st.write(f"""**• Strike Price (Put):** Strike price for the put option.""") |
|
st.write(f"""**• Premium (Call):** Premium paid for the call option.""") |
|
st.write(f"""**• Premium (Put):** Premium paid for the put option.""") |
|
|
|
ticker = st.text_input("Ticker", placeholder="Enter the symbol for the underlying asset.") |
|
strike_call = st.number_input("Strike Price (Call)", min_value=0.0, format="%f") |
|
strike_put = st.number_input("Strike Price (Put)", min_value=0.0, format="%f") |
|
premium_call = st.number_input("Premium (Call)", min_value=0.0, format="%f") |
|
premium_put = st.number_input("Premium (Put)", min_value=0.0, format="%f") |
|
|
|
|
|
if st.button('Analyze and Visualize Straddle'): |
|
|
|
if not ticker: |
|
st.error("Please enter a ticker symbol to proceed.") |
|
elif strike_call <= 0: |
|
st.error("Please enter a valid strike price for the call option.") |
|
elif strike_put <= 0: |
|
st.error("Please enter a valid strike price for the put option.") |
|
elif premium_call <= 0: |
|
st.error("Please enter a valid premium for the call option.") |
|
elif premium_put <= 0: |
|
st.error("Please enter a valid premium for the put option.") |
|
else: |
|
|
|
fig, fig2 = analyze_and_visualize_straddle(ticker, strike_call, strike_put, premium_call, premium_put) |
|
st.plotly_chart(fig) |
|
st.plotly_chart(fig2) |
|
|
|
signature_html = """ |
|
<hr style="border: 0; height: 1px; border-top: 0.85px solid #b2b2b2"> |
|
|
|
<div style="text-align: left; color: #8d8d8d; padding-left: 15px; font-size: 14.25px;"> |
|
Luis Fernando Torres, 2024<br><br> |
|
Let's connect!🔗<br> |
|
<a href="https://www.linkedin.com/in/luuisotorres/" target="_blank">LinkedIn</a> • <a href="https://medium.com/@luuisotorres" target="_blank">Medium</a> • <a href="https://www.kaggle.com/lusfernandotorres" target="_blank">Kaggle</a><br><br> |
|
</div> |
|
<div style="text-align: center; margin-top: 50px; color: #8d8d8d; padding-left: 15px; font-size: 14.25px;"> |
|
<b>Like my content? Feel free to <a href="https://www.buymeacoffee.com/luuisotorres" target="_blank">Buy Me a Coffee ☕</a></b> |
|
</div> |
|
<div style="text-align: center; margin-top: 80px; color: #8d8d8d; padding-left: 15px; font-size: 14.25px;"> |
|
<b><a href="https://luuisotorres.github.io/" target="_blank">https://luuisotorres.github.io/</a></b> |
|
</div> |
|
""" |
|
|
|
st.markdown(signature_html, unsafe_allow_html=True) |