Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -11,11 +11,11 @@ from selenium.webdriver.common.by import By
|
|
| 11 |
from selenium.webdriver.support.ui import WebDriverWait
|
| 12 |
from selenium.webdriver.support import expected_conditions as EC
|
| 13 |
|
| 14 |
-
def get_target_date(experations):
|
| 15 |
today = datetime.today()
|
| 16 |
experations_dates = [datetime.strptime(i.text, '%b %d, %Y') for i in experations if len(i.text)>5 and i.text[-4:]=='2024']
|
| 17 |
-
start_date = today + timedelta(days=
|
| 18 |
-
end_date = today + timedelta(days=
|
| 19 |
filtered_dates = [date for date in experations_dates if start_date <= date <= end_date]
|
| 20 |
return filtered_dates[0] if filtered_dates else None
|
| 21 |
|
|
@@ -36,7 +36,14 @@ def calculate_option_value(row, stock_price, risk_free_rate, maturity):
|
|
| 36 |
def main():
|
| 37 |
st.title("Options Analysis App")
|
| 38 |
|
|
|
|
| 39 |
stock = st.text_input("Enter stock symbol (e.g., AAPL):", "AAPL")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
if st.button("Analyze Options"):
|
| 42 |
try:
|
|
@@ -44,9 +51,12 @@ def main():
|
|
| 44 |
today = datetime.today()
|
| 45 |
|
| 46 |
# Get risk-free rate
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
# Set up Selenium with headless Chrome
|
| 52 |
chrome_options = Options()
|
|
@@ -65,10 +75,10 @@ def main():
|
|
| 65 |
experations_btn.click()
|
| 66 |
|
| 67 |
experations = driver.find_elements(By.XPATH, '//span[contains(@class, "C($linkColor)")]')
|
| 68 |
-
target_date_dt = get_target_date(experations)
|
| 69 |
|
| 70 |
if not target_date_dt:
|
| 71 |
-
st.error("Could not find a suitable expiration date. Please try
|
| 72 |
driver.quit()
|
| 73 |
return
|
| 74 |
|
|
@@ -99,37 +109,47 @@ def main():
|
|
| 99 |
driver.quit()
|
| 100 |
return
|
| 101 |
|
| 102 |
-
|
|
|
|
| 103 |
|
| 104 |
columns_names = ['Contract','Date','Time','ET','Strike','Price','Bid','Ask','Change','% Change','Volume','Open Interest','Implied Volatility']
|
| 105 |
-
|
| 106 |
|
| 107 |
-
|
| 108 |
|
| 109 |
-
lower =
|
| 110 |
-
higher =
|
| 111 |
|
| 112 |
-
|
| 113 |
-
|
| 114 |
|
| 115 |
maturity = (target_date_dt - today).days
|
| 116 |
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
st.write(f"Current stock price: ${stock_price}")
|
| 132 |
-
st.dataframe(
|
| 133 |
|
| 134 |
except Exception as e:
|
| 135 |
st.error(f"An error occurred: {str(e)}")
|
|
|
|
| 11 |
from selenium.webdriver.support.ui import WebDriverWait
|
| 12 |
from selenium.webdriver.support import expected_conditions as EC
|
| 13 |
|
| 14 |
+
def get_target_date(experations, min_days, max_days):
|
| 15 |
today = datetime.today()
|
| 16 |
experations_dates = [datetime.strptime(i.text, '%b %d, %Y') for i in experations if len(i.text)>5 and i.text[-4:]=='2024']
|
| 17 |
+
start_date = today + timedelta(days=min_days)
|
| 18 |
+
end_date = today + timedelta(days=max_days)
|
| 19 |
filtered_dates = [date for date in experations_dates if start_date <= date <= end_date]
|
| 20 |
return filtered_dates[0] if filtered_dates else None
|
| 21 |
|
|
|
|
| 36 |
def main():
|
| 37 |
st.title("Options Analysis App")
|
| 38 |
|
| 39 |
+
# User inputs
|
| 40 |
stock = st.text_input("Enter stock symbol (e.g., AAPL):", "AAPL")
|
| 41 |
+
option_type = st.selectbox("Option type:", ["Calls", "Puts"])
|
| 42 |
+
min_days = st.number_input("Minimum days to expiration:", value=60, min_value=0, max_value=365)
|
| 43 |
+
max_days = st.number_input("Maximum days to expiration:", value=80, min_value=0, max_value=365)
|
| 44 |
+
num_strikes = st.number_input("Number of strikes to display (above and below current price):", value=3, min_value=1, max_value=10)
|
| 45 |
+
use_custom_risk_free_rate = st.checkbox("Use custom risk-free rate")
|
| 46 |
+
custom_risk_free_rate = st.number_input("Custom risk-free rate (%):", value=4.0, min_value=0.0, max_value=20.0, disabled=not use_custom_risk_free_rate)
|
| 47 |
|
| 48 |
if st.button("Analyze Options"):
|
| 49 |
try:
|
|
|
|
| 51 |
today = datetime.today()
|
| 52 |
|
| 53 |
# Get risk-free rate
|
| 54 |
+
if use_custom_risk_free_rate:
|
| 55 |
+
risk_free_rate = custom_risk_free_rate / 100
|
| 56 |
+
else:
|
| 57 |
+
yahoo_financials_treasuries = YahooFinancials('^TNX')
|
| 58 |
+
risk_free_rate = round(yahoo_financials_treasuries.get_current_price()/100, 4)
|
| 59 |
+
st.write(f"Risk-free rate: {risk_free_rate:.2%}")
|
| 60 |
|
| 61 |
# Set up Selenium with headless Chrome
|
| 62 |
chrome_options = Options()
|
|
|
|
| 75 |
experations_btn.click()
|
| 76 |
|
| 77 |
experations = driver.find_elements(By.XPATH, '//span[contains(@class, "C($linkColor)")]')
|
| 78 |
+
target_date_dt = get_target_date(experations, min_days, max_days)
|
| 79 |
|
| 80 |
if not target_date_dt:
|
| 81 |
+
st.error(f"Could not find a suitable expiration date between {min_days} and {max_days} days from now. Please try different date ranges.")
|
| 82 |
driver.quit()
|
| 83 |
return
|
| 84 |
|
|
|
|
| 109 |
driver.quit()
|
| 110 |
return
|
| 111 |
|
| 112 |
+
table_index = 0 if option_type == "Calls" else 1
|
| 113 |
+
options = [i.text.split() for i in tables[table_index].find_elements(By.TAG_NAME,'tr')]
|
| 114 |
|
| 115 |
columns_names = ['Contract','Date','Time','ET','Strike','Price','Bid','Ask','Change','% Change','Volume','Open Interest','Implied Volatility']
|
| 116 |
+
options_df = pd.DataFrame(options[1:], columns=columns_names)
|
| 117 |
|
| 118 |
+
options_df['Strike'] = options_df['Strike'].astype(float)
|
| 119 |
|
| 120 |
+
lower = options_df[options_df['Strike'] < stock_price].tail(num_strikes)[::-1]
|
| 121 |
+
higher = options_df[options_df['Strike'] > stock_price].head(num_strikes)[::-1]
|
| 122 |
|
| 123 |
+
options_df = pd.concat([higher, lower])
|
| 124 |
+
options_df.reset_index(drop=True, inplace=True)
|
| 125 |
|
| 126 |
maturity = (target_date_dt - today).days
|
| 127 |
|
| 128 |
+
options_df["IV"] = options_df["Implied Volatility"].str.strip("%").astype(float)
|
| 129 |
+
|
| 130 |
+
if option_type == "Calls":
|
| 131 |
+
options_df[['BSM Price','Delta', 'Theta','Vega',"Gamma",'Rho']] = options_df[['Strike','IV']].apply(
|
| 132 |
+
lambda row: calculate_option_value(row, stock_price, risk_free_rate, maturity),
|
| 133 |
+
axis=1,
|
| 134 |
+
result_type='expand'
|
| 135 |
+
)
|
| 136 |
+
else:
|
| 137 |
+
# For puts, we need to adjust the calculation
|
| 138 |
+
options_df[['BSM Price','Delta', 'Theta','Vega',"Gamma",'Rho']] = options_df[['Strike','IV']].apply(
|
| 139 |
+
lambda row: calculate_option_value(row, stock_price, risk_free_rate, maturity),
|
| 140 |
+
axis=1,
|
| 141 |
+
result_type='expand'
|
| 142 |
+
)
|
| 143 |
+
options_df['Delta'] = options_df['Delta'] - 1 # Adjust delta for puts
|
| 144 |
+
|
| 145 |
+
options_df.drop(columns=["Date","Time","ET","Change","% Change","Implied Volatility"], inplace=True)
|
| 146 |
+
|
| 147 |
+
options_df['Ask'] = options_df['Ask'].astype(float)
|
| 148 |
+
options_df["Under"] = np.where(options_df['Ask'] < options_df['BSM Price'], True, False)
|
| 149 |
+
|
| 150 |
+
st.write(f"Analysis for {stock} {option_type.lower()} expiring on {target_date_str}")
|
| 151 |
st.write(f"Current stock price: ${stock_price}")
|
| 152 |
+
st.dataframe(options_df)
|
| 153 |
|
| 154 |
except Exception as e:
|
| 155 |
st.error(f"An error occurred: {str(e)}")
|