QuantumLearner commited on
Commit
64fbb46
·
verified ·
1 Parent(s): 44d48b7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +144 -125
app.py CHANGED
@@ -9,22 +9,31 @@ from scipy.stats import norm
9
  # Define functions
10
 
11
  def fetch_earnings_data(ticker, limit=99):
12
- msft = yf.Ticker(ticker)
13
- earnings_dates = msft.get_earnings_dates(limit=limit)
14
- earnings_dates.index = earnings_dates.index.tz_localize(None)
15
- earnings_dates = earnings_dates.dropna(subset=['EPS Estimate'])
16
- return earnings_dates
 
 
 
 
17
 
18
  def fetch_stock_data(ticker, start_date, end_date, buffer_days):
19
- start_date = start_date - pd.Timedelta(days=buffer_days)
20
- end_date = end_date + pd.Timedelta(days=buffer_days)
21
- stock_data = yf.download(ticker, start=start_date, end=end_date)
22
- stock_data.index = stock_data.index.tz_localize(None)
23
- return stock_data
 
 
 
 
24
 
25
  def calculate_metrics(stock_data):
26
- stock_data['Returns'] = stock_data['Close'].pct_change()
27
- stock_data['20D Volatility'] = stock_data['Returns'].rolling(window=20).std()
 
28
  return stock_data
29
 
30
  def plot_stock_price_with_earnings(stock_data, earnings_dates, ticker):
@@ -484,120 +493,130 @@ with st.sidebar.expander("Analysis Parameters", expanded=True):
484
 
485
 
486
  if st.sidebar.button("Run Analysis"):
487
- # Fetch data
488
- earnings_dates = fetch_earnings_data(ticker)
489
- current_time = pd.Timestamp.now().tz_localize(None)
490
- future_eps_estimate = earnings_dates.loc[earnings_dates.index > current_time]
491
- if not future_eps_estimate.empty:
492
- future_eps_estimate = future_eps_estimate.iloc[0]['EPS Estimate']
493
- else:
494
- future_eps_estimate = None
495
- stock_data = fetch_stock_data(ticker, earnings_dates.index.min(), earnings_dates.index.max(), buffer_days)
496
- stock_data = calculate_metrics(stock_data)
497
-
498
- latest_close_price = stock_data['Close'].iloc[-1]
499
- upper_threshold = 1 + threshold_percentage
500
- lower_threshold = 1 - threshold_percentage
501
-
502
- # Normalize price movements around earnings dates
503
- all_normalized_prices = []
504
- for earning_date in earnings_dates.index:
505
- start = earning_date - pd.Timedelta(days=pre_announcement_window)
506
- end = earning_date + pd.Timedelta(days=post_announcement_window)
507
- subset = stock_data.loc[start:end]['Close'].copy()
508
- subset = ensure_window_size(subset, earning_date, pre_announcement_window, post_announcement_window)
509
- subset.ffill(inplace=True)
510
- subset.bfill(inplace=True)
511
- subset = subset / subset[0]
512
- all_normalized_prices.append(subset.tolist())
513
-
514
- # Display earnings data before processing
515
- st.subheader("Earnings Announcements Data")
516
- st.dataframe(earnings_dates)
517
-
518
- # Plot and display charts
519
- st.subheader("Stock Price with Earnings Surprises")
520
- st.markdown("This chart shows the stock price movements with markers indicating earnings surprises. "
521
- "Positive earnings surprises are marked with green upward triangles, while negative surprises "
522
- "are marked with red downward triangles. The size of the marker indicates the magnitude of the surprise.")
523
- st.plotly_chart(plot_stock_price_with_earnings(stock_data, earnings_dates, ticker), use_container_width=True)
524
-
525
- st.subheader("Normalized Price Movements Around Earnings Dates")
526
- st.markdown("This plot shows the normalized price movements of the stock around earnings dates. "
527
- "The prices are normalized to the price on the earnings date (Day 0). "
528
- "We analyze the price behavior before and after the earnings announcement within a specified window. "
529
- "The plot also calculates the probabilities of price movements exceeding given thresholds.")
530
- st.latex(r"""
531
- \text{Normalized Price} = \frac{\text{Stock Price}}{\text{Stock Price on Day 0}}
532
- """)
533
- st.markdown("To calculate the probabilities, we count the number of times the normalized prices exceed the upper threshold "
534
- "or fall below the lower threshold. These counts are then divided by the total number of observations to get the probabilities.")
535
- st.latex(r"""
536
- \text{Probability Above} = \frac{\text{Count of Prices Above Upper Threshold}}{\text{Total Observations}}
537
- """)
538
- st.latex(r"""
539
- \text{Probability Below} = \frac{\text{Count of Prices Below Lower Threshold}}{\text{Total Observations}}
540
- """)
541
- st.latex(r"""
542
- \text{Probability Between} = 1 - \text{Probability Above} - \text{Probability Below}
543
- """)
544
- st.plotly_chart(plot_normalized_price_movements(stock_data, earnings_dates, ticker, pre_announcement_window, post_announcement_window, upper_threshold, lower_threshold), use_container_width=True)
545
-
546
- st.subheader("Volatility Around Earnings Dates")
547
- st.markdown("This plot shows the 20-day rolling volatility of the stock price around earnings dates. "
548
- "Volatility is calculated as the standard deviation of daily returns over a 20-day window.")
549
- st.latex(r"""
550
- \sigma_{20D} = \sqrt{\frac{1}{19} \sum_{i=1}^{20} (R_i - \bar{R})^2}
551
- """)
552
- st.plotly_chart(plot_volatility_around_earnings(stock_data, earnings_dates), use_container_width=True)
553
-
554
- st.subheader("Volume Around Earnings Dates")
555
- st.markdown("This plot shows the trading volume changes around earnings dates. "
556
- "We analyze the volume trends within a specified window around the earnings announcements.")
557
- st.plotly_chart(plot_volume_around_earnings(stock_data, earnings_dates), use_container_width=True)
558
-
559
- price_effects = earnings_dates.index.to_series().apply(compute_price_effect, stock_data=stock_data)
560
- earnings_dates[['Price Before', 'Price On', 'Price After', 'Price Effect (%)']] = pd.DataFrame(price_effects.tolist(), index=earnings_dates.index)
561
- earnings_dates.dropna(subset=['Price Before', 'Price On', 'Price After'], inplace=True)
562
-
563
- st.subheader("Price Effects Around Earnings Dates")
564
- st.markdown("This bar chart compares the stock prices before, on, and after the earnings dates. "
565
- "It also shows the percentage change in price as the 'Price Effect' due to the earnings announcement.")
566
- st.plotly_chart(plot_price_effects(earnings_dates), use_container_width=True)
567
-
568
- st.subheader("Earnings Surprise vs. Price Effect")
569
- st.markdown("This scatter plot shows the relationship between earnings surprise percentages and the resulting price effects. "
570
- "A regression line is fitted to show the correlation between these two variables.")
571
- st.latex(r"""
572
- \text{Price Effect (\%)} = \beta_0 + \beta_1 \times \text{Surprise (\%)}
573
- """)
574
- st.plotly_chart(plot_surprise_vs_price_effect(earnings_dates), use_container_width=True)
575
-
576
- st.subheader("Monte Carlo Simulation for Normalized Price Movements")
577
- st.markdown("This plot shows the results of a Monte Carlo simulation for normalized price movements around earnings dates. "
578
- "We simulate multiple price paths to estimate the probabilities of price movements exceeding given thresholds.")
579
- window_days = list(range(-pre_announcement_window, post_announcement_window + 1))
580
- st.plotly_chart(monte_carlo_normalized_prices(all_normalized_prices, upper_threshold, lower_threshold, latest_close_price, window_days, ticker), use_container_width=True)
581
-
582
- up_target = latest_close_price * upper_threshold
583
- down_target = latest_close_price * lower_threshold
584
-
585
- st.subheader("Expected Price Range Based on Implied Volatility")
586
- st.markdown("This plot shows the expected price range of the stock based on implied volatility over a specified period. "
587
- "It uses the current stock price and implied volatility to estimate the upper and lower bounds.")
588
- st.latex(r"""
589
- \text{Upper Bound} = S_0 \times (1 + \sigma \sqrt{t})
590
- """)
591
- st.latex(r"""
592
- \text{Lower Bound} = S_0 \times (1 - \sigma \sqrt{t})
593
- """)
594
- st.plotly_chart(plot_price_ranges(ticker, implied_volatility, days_until_earnings, up_target, down_target, stock_data), use_container_width=True)
595
 
596
- st.subheader("Monte Carlo Simulation for Price Movements")
597
- st.markdown("We simulate multiple price paths using the stock's implied volatility to estimate the probabilities of the stock price reaching given targets.")
598
- st.markdown("Implied volatility (IV) is used to model the expected volatility of the stock's price. "
599
- "The simulation generates random price paths based on the IV, the current stock price, and the time remaining until the earnings date.")
600
- st.plotly_chart(monte_carlo_simulation(ticker, implied_volatility, days_until_earnings, up_target, down_target, stock_data, num_simulations), use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
601
 
602
  hide_streamlit_style = """
603
  <style>
 
9
  # Define functions
10
 
11
  def fetch_earnings_data(ticker, limit=99):
12
+ try:
13
+ msft = yf.Ticker(ticker)
14
+ earnings_dates = msft.get_earnings_dates(limit=limit)
15
+ earnings_dates.index = earnings_dates.index.tz_localize(None)
16
+ earnings_dates = earnings_dates.dropna(subset=['EPS Estimate'])
17
+ return earnings_dates
18
+ except Exception as e:
19
+ st.warning("There was an issue fetching earnings data. Please try again later.")
20
+ return pd.DataFrame() # Return an empty DataFrame on failure
21
 
22
  def fetch_stock_data(ticker, start_date, end_date, buffer_days):
23
+ try:
24
+ start_date = start_date - pd.Timedelta(days=buffer_days)
25
+ end_date = end_date + pd.Timedelta(days=buffer_days)
26
+ stock_data = yf.download(ticker, start=start_date, end=end_date)
27
+ stock_data.index = stock_data.index.tz_localize(None)
28
+ return stock_data
29
+ except Exception as e:
30
+ st.warning("There was an issue fetching stock data. Please try again later.")
31
+ return pd.DataFrame() # Return an empty DataFrame on failure
32
 
33
  def calculate_metrics(stock_data):
34
+ if not stock_data.empty:
35
+ stock_data['Returns'] = stock_data['Close'].pct_change()
36
+ stock_data['20D Volatility'] = stock_data['Returns'].rolling(window=20).std()
37
  return stock_data
38
 
39
  def plot_stock_price_with_earnings(stock_data, earnings_dates, ticker):
 
493
 
494
 
495
  if st.sidebar.button("Run Analysis"):
496
+ try:
497
+ # Fetch data
498
+ earnings_dates = fetch_earnings_data(ticker)
499
+ current_time = pd.Timestamp.now().tz_localize(None)
500
+ future_eps_estimate = earnings_dates.loc[earnings_dates.index > current_time]
501
+ if not future_eps_estimate.empty:
502
+ future_eps_estimate = future_eps_estimate.iloc[0]['EPS Estimate']
503
+ else:
504
+ future_eps_estimate = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
505
 
506
+ stock_data = fetch_stock_data(ticker, earnings_dates.index.min(), earnings_dates.index.max(), buffer_days)
507
+ if stock_data.empty:
508
+ st.error("Failed to fetch stock data. Please try again later.")
509
+ else:
510
+ stock_data = calculate_metrics(stock_data)
511
+
512
+ latest_close_price = stock_data['Close'].iloc[-1]
513
+ upper_threshold = 1 + threshold_percentage
514
+ lower_threshold = 1 - threshold_percentage
515
+
516
+ # Normalize price movements around earnings dates
517
+ all_normalized_prices = []
518
+ for earning_date in earnings_dates.index:
519
+ start = earning_date - pd.Timedelta(days=pre_announcement_window)
520
+ end = earning_date + pd.Timedelta(days=post_announcement_window)
521
+ subset = stock_data.loc[start:end]['Close'].copy()
522
+ subset = ensure_window_size(subset, earning_date, pre_announcement_window, post_announcement_window)
523
+ subset.ffill(inplace=True)
524
+ subset.bfill(inplace=True)
525
+ subset = subset / subset[0]
526
+ all_normalized_prices.append(subset.tolist())
527
+
528
+ # Display earnings data before processing
529
+ st.subheader("Earnings Announcements Data")
530
+ st.dataframe(earnings_dates)
531
+
532
+ # Plot and display charts
533
+ st.subheader("Stock Price with Earnings Surprises")
534
+ st.markdown("This chart shows the stock price movements with markers indicating earnings surprises. "
535
+ "Positive earnings surprises are marked with green upward triangles, while negative surprises "
536
+ "are marked with red downward triangles. The size of the marker indicates the magnitude of the surprise.")
537
+ st.plotly_chart(plot_stock_price_with_earnings(stock_data, earnings_dates, ticker), use_container_width=True)
538
+
539
+ st.subheader("Normalized Price Movements Around Earnings Dates")
540
+ st.markdown("This plot shows the normalized price movements of the stock around earnings dates. "
541
+ "The prices are normalized to the price on the earnings date (Day 0). "
542
+ "We analyze the price behavior before and after the earnings announcement within a specified window. "
543
+ "The plot also calculates the probabilities of price movements exceeding given thresholds.")
544
+ st.latex(r"""
545
+ \text{Normalized Price} = \frac{\text{Stock Price}}{\text{Stock Price on Day 0}}
546
+ """)
547
+ st.markdown("To calculate the probabilities, we count the number of times the normalized prices exceed the upper threshold "
548
+ "or fall below the lower threshold. These counts are then divided by the total number of observations to get the probabilities.")
549
+ st.latex(r"""
550
+ \text{Probability Above} = \frac{\text{Count of Prices Above Upper Threshold}}{\text{Total Observations}}
551
+ """)
552
+ st.latex(r"""
553
+ \text{Probability Below} = \frac{\text{Count of Prices Below Lower Threshold}}{\text{Total Observations}}
554
+ """)
555
+ st.latex(r"""
556
+ \text{Probability Between} = 1 - \text{Probability Above} - \text{Probability Below}
557
+ """)
558
+ st.plotly_chart(plot_normalized_price_movements(stock_data, earnings_dates, ticker, pre_announcement_window, post_announcement_window, upper_threshold, lower_threshold), use_container_width=True)
559
+
560
+ st.subheader("Volatility Around Earnings Dates")
561
+ st.markdown("This plot shows the 20-day rolling volatility of the stock price around earnings dates. "
562
+ "Volatility is calculated as the standard deviation of daily returns over a 20-day window.")
563
+ st.latex(r"""
564
+ \sigma_{20D} = \sqrt{\frac{1}{19} \sum_{i=1}^{20} (R_i - \bar{R})^2}
565
+ """)
566
+ st.plotly_chart(plot_volatility_around_earnings(stock_data, earnings_dates), use_container_width=True)
567
+
568
+ st.subheader("Volume Around Earnings Dates")
569
+ st.markdown("This plot shows the trading volume changes around earnings dates. "
570
+ "We analyze the volume trends within a specified window around the earnings announcements.")
571
+ st.plotly_chart(plot_volume_around_earnings(stock_data, earnings_dates), use_container_width=True)
572
+
573
+ price_effects = earnings_dates.index.to_series().apply(compute_price_effect, stock_data=stock_data)
574
+ earnings_dates[['Price Before', 'Price On', 'Price After', 'Price Effect (%)']] = pd.DataFrame(price_effects.tolist(), index=earnings_dates.index)
575
+ earnings_dates.dropna(subset=['Price Before', 'Price On', 'Price After'], inplace=True)
576
+
577
+ st.subheader("Price Effects Around Earnings Dates")
578
+ st.markdown("This bar chart compares the stock prices before, on, and after the earnings dates. "
579
+ "It also shows the percentage change in price as the 'Price Effect' due to the earnings announcement.")
580
+ st.plotly_chart(plot_price_effects(earnings_dates), use_container_width=True)
581
+
582
+ st.subheader("Earnings Surprise vs. Price Effect")
583
+ st.markdown("This scatter plot shows the relationship between earnings surprise percentages and the resulting price effects. "
584
+ "A regression line is fitted to show the correlation between these two variables.")
585
+ st.latex(r"""
586
+ \text{Price Effect (\%)} = \beta_0 + \beta_1 \times \text{Surprise (\%)}
587
+ """)
588
+ st.plotly_chart(plot_surprise_vs_price_effect(earnings_dates), use_container_width=True)
589
+
590
+ st.subheader("Monte Carlo Simulation for Normalized Price Movements")
591
+ st.markdown("This plot shows the results of a Monte Carlo simulation for normalized price movements around earnings dates. "
592
+ "We simulate multiple price paths to estimate the probabilities of price movements exceeding given thresholds.")
593
+ window_days = list(range(-pre_announcement_window, post_announcement_window + 1))
594
+ st.plotly_chart(monte_carlo_normalized_prices(all_normalized_prices, upper_threshold, lower_threshold, latest_close_price, window_days, ticker), use_container_width=True)
595
+
596
+ up_target = latest_close_price * upper_threshold
597
+ down_target = latest_close_price * lower_threshold
598
+
599
+ st.subheader("Expected Price Range Based on Implied Volatility")
600
+ st.markdown("This plot shows the expected price range of the stock based on implied volatility over a specified period. "
601
+ "It uses the current stock price and implied volatility to estimate the upper and lower bounds.")
602
+ st.latex(r"""
603
+ \text{Upper Bound} = S_0 \times (1 + \sigma \sqrt{t})
604
+ """)
605
+ st.latex(r"""
606
+ \text{Lower Bound} = S_0 \times (1 - \sigma \sqrt{t})
607
+ """)
608
+ st.plotly_chart(plot_price_ranges(ticker, implied_volatility, days_until_earnings, up_target, down_target, stock_data), use_container_width=True)
609
+
610
+ st.subheader("Monte Carlo Simulation for Price Movements")
611
+ st.markdown("We simulate multiple price paths using the stock's implied volatility to estimate the probabilities of the stock price reaching given targets.")
612
+ st.markdown("Implied volatility (IV) is used to model the expected volatility of the stock's price. "
613
+ "The simulation generates random price paths based on the IV, the current stock price, and the time remaining until the earnings date.")
614
+ st.plotly_chart(monte_carlo_simulation(ticker, implied_volatility, days_until_earnings, up_target, down_target, stock_data, num_simulations), use_container_width=True)
615
+
616
+ except Exception as e:
617
+ st.error("An error occurred while running the analysis. Please try again.")
618
+ # Optionally log the exception or print it to the console for debugging
619
+ # print(e)
620
 
621
  hide_streamlit_style = """
622
  <style>