Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import numpy as np
|
| 3 |
+
import pandas as pd
|
| 4 |
+
from datetime import datetime, timedelta
|
| 5 |
+
import mibian
|
| 6 |
+
from yahoofinancials import YahooFinancials
|
| 7 |
+
import matplotlib.pyplot as plt
|
| 8 |
+
from selenium.webdriver import Chrome
|
| 9 |
+
from selenium.webdriver.common.by import By
|
| 10 |
+
import chromedriver_autoinstaller
|
| 11 |
+
|
| 12 |
+
# Install the correct version of ChromeDriver
|
| 13 |
+
chromedriver_autoinstaller.install()
|
| 14 |
+
|
| 15 |
+
def get_target_date(experations):
|
| 16 |
+
today = datetime.today()
|
| 17 |
+
experations_dates = [datetime.strptime(i.text, '%b %d, %Y') for i in experations if len(i.text)>5 and i.text[-4:]=='2024']
|
| 18 |
+
start_date = today + timedelta(days=60)
|
| 19 |
+
end_date = today + timedelta(days=80)
|
| 20 |
+
filtered_dates = [date for date in experations_dates if start_date <= date <= end_date]
|
| 21 |
+
return filtered_dates[0]
|
| 22 |
+
|
| 23 |
+
def calculate_option_value(row, stock_price, risk_free_rate, maturity):
|
| 24 |
+
strike = row['Strike']
|
| 25 |
+
volatility = row['IV']
|
| 26 |
+
|
| 27 |
+
option = mibian.BS([stock_price, strike, risk_free_rate, maturity], volatility=volatility)
|
| 28 |
+
return [
|
| 29 |
+
option.callPrice,
|
| 30 |
+
option.callDelta,
|
| 31 |
+
option.callTheta,
|
| 32 |
+
option.vega,
|
| 33 |
+
option.gamma,
|
| 34 |
+
option.callRho
|
| 35 |
+
]
|
| 36 |
+
|
| 37 |
+
def main():
|
| 38 |
+
st.title("Options Analysis App")
|
| 39 |
+
|
| 40 |
+
stock = st.text_input("Enter stock symbol (e.g., AAPL):", "AAPL")
|
| 41 |
+
|
| 42 |
+
if st.button("Analyze Options"):
|
| 43 |
+
# Get current date
|
| 44 |
+
today = datetime.today()
|
| 45 |
+
|
| 46 |
+
# Get risk-free rate
|
| 47 |
+
yahoo_financials_treasuries = YahooFinancials('^TNX')
|
| 48 |
+
risk_free_rate = round(yahoo_financials_treasuries.get_current_price()/100, 4)
|
| 49 |
+
st.write(f"Risk-free rate: {risk_free_rate}")
|
| 50 |
+
|
| 51 |
+
# Set up Selenium
|
| 52 |
+
url = f"https://finance.yahoo.com/quote/{stock}/options/?straddle=false"
|
| 53 |
+
driver = Chrome()
|
| 54 |
+
driver.get(url)
|
| 55 |
+
|
| 56 |
+
experations_btn = driver.find_element(By.XPATH, '/html/body/div[1]/main/section/section/section/article/section[2]/div/div[1]/button').click()
|
| 57 |
+
experations = driver.find_elements(By.CLASS_NAME, 'itm.svelte-5qjwyh')
|
| 58 |
+
target_date_dt = get_target_date(experations)
|
| 59 |
+
|
| 60 |
+
# Desired date as a string
|
| 61 |
+
target_date_str = target_date_dt.strftime('%b %d, %Y')
|
| 62 |
+
|
| 63 |
+
# Loop through date elements to find the desired date and click on it
|
| 64 |
+
for element in experations:
|
| 65 |
+
if element.text == target_date_str:
|
| 66 |
+
element.click()
|
| 67 |
+
break
|
| 68 |
+
|
| 69 |
+
driver.implicitly_wait(5)
|
| 70 |
+
tables = driver.find_elements(By.TAG_NAME, "table")
|
| 71 |
+
stock_price = float(driver.find_element(By.CLASS_NAME, "livePrice").text)
|
| 72 |
+
|
| 73 |
+
calls = [i.text.split() for i in tables[0].find_elements(By.TAG_NAME,'tr')]
|
| 74 |
+
|
| 75 |
+
columns_names = ['Contract','Date','Time','ET','Strike','Price','Bid','Ask','Change','% Change','Volume','Open Interest','Implied Volatility']
|
| 76 |
+
calls_df = pd.DataFrame(calls[1:], columns=columns_names)
|
| 77 |
+
|
| 78 |
+
calls_df['Strike'] = calls_df['Strike'].astype(float)
|
| 79 |
+
|
| 80 |
+
lower = calls_df[calls_df['Strike'] < stock_price].tail(3)[::-1]
|
| 81 |
+
higher = calls_df[calls_df['Strike'] > stock_price].head(3)[::-1]
|
| 82 |
+
|
| 83 |
+
calls_df = pd.concat([higher, lower])
|
| 84 |
+
calls_df.reset_index(drop=True, inplace=True)
|
| 85 |
+
|
| 86 |
+
maturity = (target_date_dt - today).days
|
| 87 |
+
|
| 88 |
+
calls_df["IV"] = calls_df["Implied Volatility"].str.strip("%").astype(float)
|
| 89 |
+
|
| 90 |
+
calls_df[['BSM Price','Call Delta', 'Call Theta','Vega',"Gamma",'Rho']] = calls_df[['Strike','IV']].apply(
|
| 91 |
+
lambda row: calculate_option_value(row, stock_price, risk_free_rate, maturity),
|
| 92 |
+
axis=1,
|
| 93 |
+
result_type='expand'
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
calls_df.drop(columns=["Date","Time","ET","Change","% Change","Implied Volatility"], inplace=True)
|
| 97 |
+
|
| 98 |
+
calls_df['Ask'] = calls_df['Ask'].astype(float)
|
| 99 |
+
calls_df["Under"] = np.where(calls_df['Ask'] < calls_df['BSM Price'], True, False)
|
| 100 |
+
|
| 101 |
+
st.write(f"Analysis for {stock} options expiring on {target_date_str}")
|
| 102 |
+
st.write(f"Current stock price: ${stock_price}")
|
| 103 |
+
st.dataframe(calls_df)
|
| 104 |
+
|
| 105 |
+
# Close the browser
|
| 106 |
+
driver.quit()
|
| 107 |
+
|
| 108 |
+
if __name__ == "__main__":
|
| 109 |
+
main()
|