Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import yfinance as yf
|
| 4 |
+
import plotly.graph_objects as go
|
| 5 |
+
import streamlit as st
|
| 6 |
+
|
| 7 |
+
#st.title('Analyzing Future Stock Price Movements with Monte Carlo Simulations')
|
| 8 |
+
st.set_page_config(page_title = "Analyzing Future Stock Price Movements with Monte Carlo Simulations", layout = "wide")
|
| 9 |
+
|
| 10 |
+
# Sidebar inputs
|
| 11 |
+
st.sidebar.header('Input Parameters')
|
| 12 |
+
ticker = st.sidebar.text_input('Enter Stock Ticker', 'AFX.DE')
|
| 13 |
+
start_date = st.sidebar.date_input('Start Date', pd.to_datetime('2020-01-01'))
|
| 14 |
+
end_date = st.sidebar.date_input('End Date', pd.to_datetime('2025-12-30'))
|
| 15 |
+
days_to_forecast = st.sidebar.slider('Time Horizon (Days)', min_value=1, max_value=60, value=30)
|
| 16 |
+
num_simulations = st.sidebar.number_input('Number of Simulations', min_value=100, max_value=100000, value=1000, step=100)
|
| 17 |
+
lower_threshold = st.sidebar.number_input('Lower Threshold', min_value=0, max_value=200, value=90)
|
| 18 |
+
upper_threshold = st.sidebar.number_input('Upper Threshold', min_value=0, max_value=200, value=110)
|
| 19 |
+
volatility_bsm = st.sidebar.number_input('BSM Volatility (Annualized)', min_value=0.01, max_value=10.0, value=0.29, step=0.01)
|
| 20 |
+
|
| 21 |
+
st.write("""
|
| 22 |
+
This analysis estimates potential price movements of a selected stock over a specified time horizon. The estimates are based on historical volatility and the implied volatility derived fromthe Black-Scholes-Merton model. You can adjust the time horizon, number of simulations, and implied volatility measure to explore different scenarios of price dynamics.
|
| 23 |
+
""")
|
| 24 |
+
|
| 25 |
+
st.markdown("""
|
| 26 |
+
### How to Use This App
|
| 27 |
+
1. **Input Parameters**: Use the sidebar to enter the stock ticker, date range, and other parameters for the analyses.
|
| 28 |
+
2. **Run the Analysis**: Click the "Run" button to perform the analyses and visualize the results.
|
| 29 |
+
Each analysis is accompanied by a detailed explanation and visual representation, providing insights into the stock's price behavior and helping traders make informed decisions regarding potential price movements.
|
| 30 |
+
""")
|
| 31 |
+
|
| 32 |
+
# Adding LaTeX formatted formulas
|
| 33 |
+
# Display the main formula for Monte Carlo simulations with clear, basic LaTeX
|
| 34 |
+
st.latex(r'''
|
| 35 |
+
P_t = P_0 \times e^{(\mu - \frac{1}{2} \sigma^2) \times t + \sigma \times \sqrt{t} \times Z}
|
| 36 |
+
''')
|
| 37 |
+
|
| 38 |
+
# Provide detailed explanations using plain text
|
| 39 |
+
st.markdown("""
|
| 40 |
+
**Monte Carlo Simulation Explained:**
|
| 41 |
+
- **(P_t)**: Estimated stock price at time (t).
|
| 42 |
+
- **(P_0)**: Current stock price.
|
| 43 |
+
- **(mu)**: Mean of the log returns.
|
| 44 |
+
- **(sigma)**: Standard deviation of the log returns, representing historical volatility.
|
| 45 |
+
- **(t)**: Time horizon in days.
|
| 46 |
+
- **(Z)**: Random variable from the standard normal distribution.
|
| 47 |
+
This formula models future stock prices using a stochastic process known as the Monte Carlo simulation. It considers both the average return and the variability in returns to project future price movements. The random component (Z) introduces randomness reflecting the unpredictable nature of stock price movements.
|
| 48 |
+
""")
|
| 49 |
+
|
| 50 |
+
# Note on usage
|
| 51 |
+
st.markdown("""
|
| 52 |
+
Use this tool to simulate different scenarios by varying the number of simulations and observing the distribution of possible future prices. Adjust the time horizon and volatility to see how these factors influence the projected price range. This analysis is crucial for assessing potential investment risks and rewards.
|
| 53 |
+
""")
|
| 54 |
+
|
| 55 |
+
st.write(f"""
|
| 56 |
+
### Monte Carlo Simulations
|
| 57 |
+
These simulations project multiple potential future price paths for the stock based on the volatility models described. By running {num_simulations} simulations, the tool generates a distribution of possible future prices, allowing us to calculate confidence intervals and probabilities for various price levels.
|
| 58 |
+
""")
|
| 59 |
+
|
| 60 |
+
if st.sidebar.button('Run'):
|
| 61 |
+
stock_data = yf.download(ticker, start=start_date, end=end_date)
|
| 62 |
+
|
| 63 |
+
if not stock_data.empty:
|
| 64 |
+
stock_data['Returns'] = stock_data['Close'].pct_change()
|
| 65 |
+
log_returns = np.log(stock_data['Close'] / stock_data['Close'].shift(1))
|
| 66 |
+
current_price = stock_data.iloc[-1]['Close']
|
| 67 |
+
|
| 68 |
+
def run_simulation(volatility, dt, annualized=False):
|
| 69 |
+
simulated_prices = np.zeros((days_to_forecast, num_simulations))
|
| 70 |
+
simulated_prices[0] = current_price
|
| 71 |
+
|
| 72 |
+
if annualized:
|
| 73 |
+
volatility = volatility / np.sqrt(252)
|
| 74 |
+
|
| 75 |
+
for t in range(1, days_to_forecast):
|
| 76 |
+
random_walk = np.random.normal(loc=log_returns.mean() * dt,
|
| 77 |
+
scale=volatility * np.sqrt(dt),
|
| 78 |
+
size=num_simulations)
|
| 79 |
+
simulated_prices[t] = simulated_prices[t - 1] * np.exp(random_walk)
|
| 80 |
+
return simulated_prices
|
| 81 |
+
|
| 82 |
+
simulated_prices_historical = run_simulation(log_returns.std(), 1)
|
| 83 |
+
simulated_prices_bsm = run_simulation(volatility_bsm, 1, annualized=True)
|
| 84 |
+
|
| 85 |
+
fig1 = go.Figure()
|
| 86 |
+
fig2 = go.Figure()
|
| 87 |
+
|
| 88 |
+
for simulated_prices, fig, title in zip([simulated_prices_historical, simulated_prices_bsm],
|
| 89 |
+
[fig1, fig2], ['Historical Volatility', 'BSM Volatility']):
|
| 90 |
+
mean_price_path = np.mean(simulated_prices, axis=1)
|
| 91 |
+
median_price_path = np.median(simulated_prices, axis=1)
|
| 92 |
+
lower_bound_68 = np.percentile(simulated_prices, 16, axis=1)
|
| 93 |
+
upper_bound_68 = np.percentile(simulated_prices, 84, axis=1)
|
| 94 |
+
lower_bound_95 = np.percentile(simulated_prices, 2.5, axis=1)
|
| 95 |
+
upper_bound_95 = np.percentile(simulated_prices, 97.5, axis=1)
|
| 96 |
+
|
| 97 |
+
for i in range(min(num_simulations, 100)): # Plot a subset of paths for clarity
|
| 98 |
+
fig.add_trace(go.Scatter(x=np.arange(days_to_forecast), y=simulated_prices[:, i], mode='lines', line=dict(color='lightgray', width=0.5), opacity=0.3, showlegend=False))
|
| 99 |
+
|
| 100 |
+
fig.add_trace(go.Scatter(x=np.arange(days_to_forecast), y=mean_price_path, mode='lines', name='Mean Price Path', line=dict(color='black')))
|
| 101 |
+
fig.add_trace(go.Scatter(x=np.arange(days_to_forecast), y=median_price_path, mode='lines', name='Median Price Path', line=dict(color='blue')))
|
| 102 |
+
fig.add_trace(go.Scatter(x=np.arange(days_to_forecast), y=lower_bound_68, mode='lines', name='68% confidence interval (Lower)', line=dict(color='green', dash='dash')))
|
| 103 |
+
fig.add_trace(go.Scatter(x=np.arange(days_to_forecast), y=upper_bound_68, mode='lines', name='68% confidence interval (Upper)', line=dict(color='green', dash='dash')))
|
| 104 |
+
fig.add_trace(go.Scatter(x=np.arange(days_to_forecast), y=lower_bound_95, mode='lines', name='95% confidence interval (Lower)', line=dict(color='blue', dash='dash')))
|
| 105 |
+
fig.add_trace(go.Scatter(x=np.arange(days_to_forecast), y=upper_bound_95, mode='lines', name='95% confidence interval (Upper)', line=dict(color='red', dash='dash')))
|
| 106 |
+
|
| 107 |
+
fig.add_trace(go.Scatter(x=np.arange(days_to_forecast), y=[upper_threshold] * days_to_forecast, mode='lines', name='Upper Threshold', line=dict(color='purple')))
|
| 108 |
+
fig.add_trace(go.Scatter(x=np.arange(days_to_forecast), y=[lower_threshold] * days_to_forecast, mode='lines', name='Lower Threshold', line=dict(color='orange')))
|
| 109 |
+
|
| 110 |
+
above_upper_threshold_prob = (simulated_prices[-1] > upper_threshold).sum() / num_simulations
|
| 111 |
+
below_lower_threshold_prob = (simulated_prices[-1] < lower_threshold).sum() / num_simulations
|
| 112 |
+
between_thresholds_prob = 1 - above_upper_threshold_prob - below_lower_threshold_prob
|
| 113 |
+
|
| 114 |
+
fig.update_layout(
|
| 115 |
+
title=f'Monte Carlo Confidence Cone for {ticker} using {title}',
|
| 116 |
+
xaxis_title='Days',
|
| 117 |
+
yaxis_title='Stock Price',
|
| 118 |
+
legend_title='Legend'
|
| 119 |
+
)
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
fig.add_annotation(text=f'P(>{upper_threshold:.2f}): {above_upper_threshold_prob:.2%}<br>'
|
| 123 |
+
f'P(<{lower_threshold:.2f}): {below_lower_threshold_prob:.2%}<br>'
|
| 124 |
+
f'P({lower_threshold:.2f} - {upper_threshold:.2f}): {between_thresholds_prob:.2%}',
|
| 125 |
+
xref='paper', yref='paper', x=0.02, y=0.95, showarrow=False, bordercolor="black", borderwidth=1)
|
| 126 |
+
|
| 127 |
+
# Add elaboration text below each graph
|
| 128 |
+
st.plotly_chart(fig)
|
| 129 |
+
st.markdown(f"""
|
| 130 |
+
<h4 style='font-size: 16px;'>Interpretation of Results</h4>
|
| 131 |
+
<ul style='font-size: 14px;'>
|
| 132 |
+
<li><strong>Mean Price Path:</strong> The average simulated stock price path over the forecast period.</li>
|
| 133 |
+
<li><strong>Median Price Path:</strong> The median simulated stock price path over the forecast period.</li>
|
| 134 |
+
<li><strong>68% Confidence Interval:</strong> The range within which 68% of the simulated stock prices fall.</li>
|
| 135 |
+
<li><strong>95% Confidence Interval:</strong> The range within which 95% of the simulated stock prices fall.</li>
|
| 136 |
+
<li><strong>Probability of Exceeding Upper Threshold ({upper_threshold}):</strong> {above_upper_threshold_prob:.2%}</li>
|
| 137 |
+
<li><strong>Probability of Falling Below Lower Threshold ({lower_threshold}):</strong> {below_lower_threshold_prob:.2%}</li>
|
| 138 |
+
<li><strong>Probability of Staying Between Thresholds ({lower_threshold} - {upper_threshold}):</strong> {between_thresholds_prob:.2%}</li>
|
| 139 |
+
</ul>
|
| 140 |
+
""", unsafe_allow_html=True)
|
| 141 |
+
|
| 142 |
+
else:
|
| 143 |
+
st.write("No data found for the given ticker and date range.")
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
hide_streamlit_style = """
|
| 147 |
+
<style>
|
| 148 |
+
#MainMenu {visibility: hidden;}
|
| 149 |
+
footer {visibility: hidden;}
|
| 150 |
+
</style>
|
| 151 |
+
"""
|
| 152 |
+
st.markdown(hide_streamlit_style, unsafe_allow_html=True)
|