Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import yfinance as yf
|
| 2 |
+
import numpy as np
|
| 3 |
+
import pandas as pd
|
| 4 |
+
import streamlit as st
|
| 5 |
+
import plotly.graph_objects as go
|
| 6 |
+
|
| 7 |
+
# Fetch stock data
|
| 8 |
+
def get_stock_data(ticker, start_date, end_date):
|
| 9 |
+
stock_data = yf.download(ticker, start=start_date, end=end_date)
|
| 10 |
+
return stock_data['Close']
|
| 11 |
+
|
| 12 |
+
# Bootstrapping simulation function
|
| 13 |
+
def bootstrap_simulation(data, days, n_iterations=10000):
|
| 14 |
+
daily_returns = data.pct_change().dropna()
|
| 15 |
+
simulations = np.zeros((n_iterations, days))
|
| 16 |
+
|
| 17 |
+
for i in range(n_iterations):
|
| 18 |
+
sample = np.random.choice(daily_returns, size=days, replace=True)
|
| 19 |
+
simulations[i] = np.cumprod(1 + sample) * data.iloc[-1]
|
| 20 |
+
|
| 21 |
+
return simulations
|
| 22 |
+
|
| 23 |
+
# Calculate probabilities
|
| 24 |
+
def calculate_probabilities(simulations, thresholds):
|
| 25 |
+
final_prices = simulations[:, -1]
|
| 26 |
+
below = np.mean(final_prices < thresholds[0])
|
| 27 |
+
above = np.mean(final_prices > thresholds[1])
|
| 28 |
+
between = np.mean((final_prices >= thresholds[0]) & (final_prices <= thresholds[1]))
|
| 29 |
+
|
| 30 |
+
return {'below': below, 'between': between, 'above': above}
|
| 31 |
+
|
| 32 |
+
# Calculate percentiles
|
| 33 |
+
def calculate_percentiles(simulations):
|
| 34 |
+
percentiles = np.percentile(simulations, [2.5, 16, 50, 84, 97.5], axis=0)
|
| 35 |
+
return percentiles
|
| 36 |
+
|
| 37 |
+
# Plot distributions
|
| 38 |
+
def plot_distributions(bootstrap_simulations, data, thresholds, bootstrap_probabilities):
|
| 39 |
+
final_bootstrap_prices = bootstrap_simulations[:, -1]
|
| 40 |
+
|
| 41 |
+
mean_bootstrap_price = np.mean(final_bootstrap_prices)
|
| 42 |
+
median_bootstrap_price = np.median(final_bootstrap_prices)
|
| 43 |
+
ci_68_bootstrap = np.percentile(final_bootstrap_prices, [16, 84])
|
| 44 |
+
ci_95_bootstrap = np.percentile(final_bootstrap_prices, [2.5, 97.5])
|
| 45 |
+
latest_price = data.iloc[-1]
|
| 46 |
+
|
| 47 |
+
fig = go.Figure()
|
| 48 |
+
|
| 49 |
+
# Plot for Bootstrapping
|
| 50 |
+
fig.add_trace(go.Histogram(x=final_bootstrap_prices, nbinsx=50, name='Simulated Final Prices',
|
| 51 |
+
marker_color='blue', opacity=0.7))
|
| 52 |
+
fig.add_vline(x=mean_bootstrap_price, line=dict(color='red', dash='dash'), name=f'Mean: {mean_bootstrap_price:.2f}')
|
| 53 |
+
fig.add_vline(x=median_bootstrap_price, line=dict(color='orange', dash='dash'), name=f'Median: {median_bootstrap_price:.2f}')
|
| 54 |
+
fig.add_vline(x=latest_price, line=dict(color='green', dash='dash'), name=f'Latest Price: {latest_price:.2f}')
|
| 55 |
+
fig.add_vrect(x0=ci_68_bootstrap[0], x1=ci_68_bootstrap[1], fillcolor='yellow', opacity=0.2, layer="below", line_width=0, annotation_text="68% CI", annotation_position="top left")
|
| 56 |
+
fig.add_vrect(x0=ci_95_bootstrap[0], x1=ci_95_bootstrap[1], fillcolor='grey', opacity=0.2, layer="below", line_width=0, annotation_text="95% CI", annotation_position="top left")
|
| 57 |
+
|
| 58 |
+
max_freq = np.histogram(final_bootstrap_prices, bins=50)[0].max()
|
| 59 |
+
|
| 60 |
+
# Calculate positions based on a fraction of the max frequency
|
| 61 |
+
mean_y_pos = max_freq * 0.9
|
| 62 |
+
median_y_pos = max_freq * 0.7
|
| 63 |
+
latest_y_pos = max_freq * 0.5
|
| 64 |
+
|
| 65 |
+
# Annotations for the vertical lines
|
| 66 |
+
fig.add_annotation(x=mean_bootstrap_price, y=mean_y_pos, text=f'Mean: {mean_bootstrap_price:.2f}', showarrow=False)
|
| 67 |
+
fig.add_annotation(x=median_bootstrap_price, y=median_y_pos, text=f'Median: {median_bootstrap_price:.2f}', showarrow=False)
|
| 68 |
+
fig.add_annotation(x=latest_price, y=latest_y_pos, text=f'Latest: {latest_price:.2f}', showarrow=False)
|
| 69 |
+
|
| 70 |
+
textstr = f'P(>{thresholds[1]:.2f}): {bootstrap_probabilities["above"]:.2%}<br>' + \
|
| 71 |
+
f'P(<{thresholds[0]:.2f}): {bootstrap_probabilities["below"]:.2%}<br>' + \
|
| 72 |
+
f'P({thresholds[0]:.2f} - {thresholds[1]:.2f}): {bootstrap_probabilities["between"]:.2%}'
|
| 73 |
+
fig.add_annotation(xref='paper', yref='paper', x=0.98, y=0.02, text=textstr, showarrow=False,
|
| 74 |
+
bordercolor="black", borderwidth=1, borderpad=4, bgcolor="white", opacity=0.8)
|
| 75 |
+
|
| 76 |
+
fig.update_layout(title='Bootstrapping Simulation', xaxis_title='Final Price', yaxis_title='Frequency', showlegend=True)
|
| 77 |
+
return fig, mean_bootstrap_price, median_bootstrap_price, ci_68_bootstrap, ci_95_bootstrap, latest_price
|
| 78 |
+
|
| 79 |
+
# Plot price data with simulation cones
|
| 80 |
+
def plot_price_with_cones(data, bootstrap_percentiles, days, thresholds, bootstrap_probabilities):
|
| 81 |
+
last_date = data.index[-1]
|
| 82 |
+
future_dates = pd.date_range(start=last_date + pd.Timedelta(days=1), periods=days, freq='D')
|
| 83 |
+
|
| 84 |
+
fig = go.Figure()
|
| 85 |
+
|
| 86 |
+
# Plot historical prices
|
| 87 |
+
fig.add_trace(go.Scatter(x=data.index, y=data, mode='lines', name='Historical Prices', line=dict(color='black')))
|
| 88 |
+
|
| 89 |
+
# Plot bootstrapping simulation cone
|
| 90 |
+
fig.add_trace(go.Scatter(x=future_dates, y=bootstrap_percentiles[2], mode='lines', name='Bootstrap Median', line=dict(color='red', dash='dash')))
|
| 91 |
+
fig.add_trace(go.Scatter(x=future_dates, y=bootstrap_percentiles[0], fill=None, mode='lines', line=dict(color='lightgrey'), showlegend=False))
|
| 92 |
+
fig.add_trace(go.Scatter(x=future_dates, y=bootstrap_percentiles[4], fill='tonexty', mode='lines', line=dict(color='lightgrey'), name='Bootstrap 95% CI'))
|
| 93 |
+
fig.add_trace(go.Scatter(x=future_dates, y=bootstrap_percentiles[1], fill=None, mode='lines', line=dict(color='lightyellow'), showlegend=False))
|
| 94 |
+
fig.add_trace(go.Scatter(x=future_dates, y=bootstrap_percentiles[3], fill='tonexty', mode='lines', line=dict(color='lightyellow'), name='Bootstrap 68% CI'))
|
| 95 |
+
|
| 96 |
+
# Annotate the thresholds
|
| 97 |
+
fig.add_hline(y=thresholds[0], line=dict(color='blue', dash='dash'), annotation_text=f'Threshold 1: {thresholds[0]}', annotation_position="top left")
|
| 98 |
+
fig.add_hline(y=thresholds[1], line=dict(color='green', dash='dash'), annotation_text=f'Threshold 2: {thresholds[1]}', annotation_position="top left")
|
| 99 |
+
|
| 100 |
+
# Add probability annotations
|
| 101 |
+
textstr_bootstrap = f'Bootstrap Probabilities:<br>Below {thresholds[0]}: {bootstrap_probabilities["below"]:.2%}<br>' + \
|
| 102 |
+
f'Between {thresholds[0]} and {thresholds[1]}: {bootstrap_probabilities["between"]:.2%}<br>' + \
|
| 103 |
+
f'Above {thresholds[1]}: {bootstrap_probabilities["above"]:.2%}'
|
| 104 |
+
fig.add_annotation(xref='paper', yref='paper', x=0.98, y=0.02, text=textstr_bootstrap, showarrow=False,
|
| 105 |
+
bordercolor="black", borderwidth=1, borderpad=4, bgcolor="white", opacity=0.8)
|
| 106 |
+
|
| 107 |
+
fig.update_layout(title='Bootstrapping Simulation Cone', xaxis_title='Date', yaxis_title='Price', showlegend=True)
|
| 108 |
+
fig.update_xaxes(type='date')
|
| 109 |
+
|
| 110 |
+
return fig
|
| 111 |
+
|
| 112 |
+
# Streamlit app
|
| 113 |
+
st.title('Stock Price Simulation')
|
| 114 |
+
|
| 115 |
+
st.sidebar.header('Input Parameters')
|
| 116 |
+
|
| 117 |
+
st.write("""
|
| 118 |
+
### Description
|
| 119 |
+
This application simulates future stock prices using bootstrapping simulation methods.
|
| 120 |
+
You can specify the stock ticker, the date range, the number of simulation days, the number of simulations, and price thresholds.
|
| 121 |
+
The simulation results will show the probability of the stock price falling below, between, or above the specified thresholds.
|
| 122 |
+
|
| 123 |
+
**How to use:**
|
| 124 |
+
1. Enter the stock ticker, start date, and end date.
|
| 125 |
+
2. Set the number of days for the simulation and the number of iterations.
|
| 126 |
+
3. Enter the price thresholds.
|
| 127 |
+
4. Click 'Run Simulation' to start the bootstrapping simulation.
|
| 128 |
+
|
| 129 |
+
**Results:**
|
| 130 |
+
The app will display two charts:
|
| 131 |
+
1. The distribution of the final simulated prices with key statistical measures.
|
| 132 |
+
2. The historical stock prices with simulated future price cones and the specified thresholds.
|
| 133 |
+
""")
|
| 134 |
+
|
| 135 |
+
ticker = st.sidebar.text_input('Enter Stock Ticker', 'ASML.AS')
|
| 136 |
+
start_date = st.sidebar.date_input('Start Date', pd.to_datetime('2020-01-01'))
|
| 137 |
+
end_date = st.sidebar.date_input('End Date', pd.to_datetime('2025-01-01'))
|
| 138 |
+
days = st.sidebar.number_input('Number of Days for Simulation', min_value=1, max_value=365, value=30)
|
| 139 |
+
n_iterations = st.sidebar.number_input('Number of Simulations', min_value=100, max_value=100000, value=10000)
|
| 140 |
+
threshold1 = st.sidebar.number_input('Threshold 1', min_value=0, value=850)
|
| 141 |
+
threshold2 = st.sidebar.number_input('Threshold 2', min_value=0, value=1000)
|
| 142 |
+
thresholds = [threshold1, threshold2]
|
| 143 |
+
|
| 144 |
+
if st.sidebar.button('Run Simulation'):
|
| 145 |
+
data = get_stock_data(ticker, start_date, end_date)
|
| 146 |
+
|
| 147 |
+
bootstrap_simulations = bootstrap_simulation(data, days, n_iterations)
|
| 148 |
+
|
| 149 |
+
bootstrap_probabilities = calculate_probabilities(bootstrap_simulations, thresholds)
|
| 150 |
+
|
| 151 |
+
bootstrap_percentiles = calculate_percentiles(bootstrap_simulations)
|
| 152 |
+
|
| 153 |
+
fig1, mean_bootstrap_price, median_bootstrap_price, ci_68_bootstrap, ci_95_bootstrap, latest_price = plot_distributions(bootstrap_simulations, data, thresholds, bootstrap_probabilities)
|
| 154 |
+
fig2 = plot_price_with_cones(data, bootstrap_percentiles, days, thresholds, bootstrap_probabilities)
|
| 155 |
+
|
| 156 |
+
st.plotly_chart(fig1)
|
| 157 |
+
st.plotly_chart(fig2)
|
| 158 |
+
|
| 159 |
+
st.write(f"""
|
| 160 |
+
### Interpretation of Results
|
| 161 |
+
|
| 162 |
+
**Distribution of Final Simulated Prices:**
|
| 163 |
+
- **Mean Final Price:** {mean_bootstrap_price:.2f}
|
| 164 |
+
- **Median Final Price:** {median_bootstrap_price:.2f}
|
| 165 |
+
- **68% Confidence Interval (CI):** [{ci_68_bootstrap[0]:.2f}, {ci_68_bootstrap[1]:.2f}]
|
| 166 |
+
- **95% Confidence Interval (CI):** [{ci_95_bootstrap[0]:.2f}, {ci_95_bootstrap[1]:.2f}]
|
| 167 |
+
- **Latest Price:** {latest_price:.2f}
|
| 168 |
+
|
| 169 |
+
**Bootstrapping Simulation Cone:**
|
| 170 |
+
- **Bootstrap Median:** The median of the simulated future prices for each day.
|
| 171 |
+
- **Bootstrap 68% CI:** The 68% confidence interval for the simulated future prices.
|
| 172 |
+
- **Bootstrap 95% CI:** The 95% confidence interval for the simulated future prices.
|
| 173 |
+
- **Threshold 1 and Threshold 2:** {threshold1:.2f}, {threshold2:.2f}
|
| 174 |
+
- **Probability Annotations:**
|
| 175 |
+
- The probability of the stock price being below Threshold 1: {bootstrap_probabilities["below"]:.2%}
|
| 176 |
+
- The probability of the stock price being between Threshold 1 and Threshold 2: {bootstrap_probabilities["between"]:.2%}
|
| 177 |
+
- The probability of the stock price being above Threshold 2: {bootstrap_probabilities["above"]:.2%}
|
| 178 |
+
""")
|