Spaces:
Sleeping
Sleeping
File size: 13,838 Bytes
cd972cc ef37b69 cd972cc 9e21766 cd972cc 100b749 cd972cc 100b749 cd972cc 100b749 cd972cc 100b749 cd972cc 100b749 cd972cc 100b749 cd972cc 100b749 d0fe27a 100b749 d5ef992 d0fe27a 100b749 cd972cc 100b749 65bae9b 100b749 cd972cc 100b749 d5ef992 cd972cc 700757f ffcb5c3 cd972cc e547af5 ffcb5c3 e547af5 9e21766 e547af5 b890fd1 b7d3c9d b890fd1 e547af5 cd972cc ffcb5c3 e547af5 e5d91fe cd972cc 721327a 745fd4b 700757f 745fd4b 721327a 700757f e5d91fe cd972cc e547af5 cd972cc 100b749 bdacd5e 100b749 bdacd5e 100b749 3511e00 0b13902 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | import numpy as np
import yfinance as yf
import pandas as pd
import streamlit as st
import plotly.graph_objects as go
from datetime import datetime, timedelta
# Fetch stock data
def get_stock_data(ticker, start_date, end_date):
data = yf.download(ticker, start=start_date, end=end_date, auto_adjust=False) # Unadjusted prices
if isinstance(data.columns, pd.MultiIndex): # Flatten multi-index columns
data.columns = data.columns.get_level_values(0)
if data.empty:
raise ValueError(f"No data found for {ticker} from {start_date} to {end_date}")
return data['Close']
# Bootstrapping simulation function
def bootstrap_simulation(data, days, n_iterations=10000):
daily_returns = data.pct_change().dropna()
daily_returns = daily_returns.values.flatten() # Ensure 1D array
simulations = np.zeros((n_iterations, days))
last_price = data.iloc[-1].item() # Scalar last price
for i in range(n_iterations):
sample = np.random.choice(daily_returns, size=days, replace=True)
simulations[i] = np.cumprod(1 + sample) * last_price
return simulations
# Calculate probabilities
def calculate_probabilities(simulations, thresholds):
final_prices = simulations[:, -1]
below = np.mean(final_prices < thresholds[0])
above = np.mean(final_prices > thresholds[1])
between = np.mean((final_prices >= thresholds[0]) & (final_prices <= thresholds[1]))
return {'below': below, 'between': between, 'above': above}
# Calculate percentiles
def calculate_percentiles(simulations):
percentiles = np.percentile(simulations, [2.5, 16, 50, 84, 97.5], axis=0)
return percentiles
# Plot distribution of final prices
def plot_distributions(bootstrap_simulations, data, thresholds, bootstrap_probabilities):
final_bootstrap_prices = bootstrap_simulations[:, -1]
mean_bootstrap_price = np.mean(final_bootstrap_prices)
median_bootstrap_price = np.median(final_bootstrap_prices)
ci_68_bootstrap = np.percentile(final_bootstrap_prices, [16, 84])
ci_95_bootstrap = np.percentile(final_bootstrap_prices, [2.5, 97.5])
latest_price = data.iloc[-1].item() # Scalar for plotting
fig = go.Figure()
fig.add_trace(go.Histogram(x=final_bootstrap_prices, nbinsx=50, name='Simulated Final Prices', marker_color='blue', opacity=0.7))
fig.add_trace(go.Scatter(x=[mean_bootstrap_price, mean_bootstrap_price], y=[0, np.histogram(final_bootstrap_prices, bins=50)[0].max()], mode='lines', line=dict(color='red', dash='dash'), name=f'Mean: {mean_bootstrap_price:.2f}'))
fig.add_trace(go.Scatter(x=[median_bootstrap_price, median_bootstrap_price], y=[0, np.histogram(final_bootstrap_prices, bins=50)[0].max()], mode='lines', line=dict(color='orange', dash='dash'), name=f'Median: {median_bootstrap_price:.2f}'))
fig.add_trace(go.Scatter(x=[latest_price, latest_price], y=[0, np.histogram(final_bootstrap_prices, bins=50)[0].max()], mode='lines', line=dict(color='green', dash='dash'), name=f'Latest Price: {latest_price:.2f}'))
fig.add_vrect(x0=ci_68_bootstrap[0], x1=ci_68_bootstrap[1], fillcolor='yellow', opacity=0.2, layer="below", line_width=0)
fig.add_vrect(x0=ci_95_bootstrap[0], x1=ci_95_bootstrap[1], fillcolor='grey', opacity=0.2, layer="below", line_width=0)
textstr = (f'P(>{thresholds[1]:.2f}): {bootstrap_probabilities["above"]:.2%}<br>'
f'P(<{thresholds[0]:.2f}): {bootstrap_probabilities["below"]:.2%}<br>'
f'P({thresholds[0]:.2f} - {thresholds[1]:.2f}): {bootstrap_probabilities["between"]:.2%}')
fig.add_annotation(xref='paper', yref='paper', x=0.98, y=0.02, text=textstr, showarrow=False, bordercolor="black", borderwidth=1, borderpad=4, bgcolor="black", opacity=0.7)
fig.update_layout(title='Bootstrapping Simulation', xaxis_title='Final Price', yaxis_title='Frequency', showlegend=True)
return fig, mean_bootstrap_price, median_bootstrap_price, ci_68_bootstrap, ci_95_bootstrap, latest_price
# Plot price data with simulation cones
def plot_price_with_cones(data, bootstrap_percentiles, days, thresholds, bootstrap_probabilities):
last_date = data.index[-1]
future_dates = pd.date_range(start=last_date + pd.Timedelta(days=1), periods=days, freq='D')
fig = go.Figure()
# Historical prices
fig.add_trace(go.Scatter(x=data.index, y=data, mode='lines', name='Historical Prices', line=dict(color='white')))
# Simulation cone (check for valid percentiles)
if bootstrap_percentiles.shape[1] == len(future_dates):
fig.add_trace(go.Scatter(x=future_dates, y=bootstrap_percentiles[2], mode='lines', name='Bootstrap Median', line=dict(color='red', dash='dash')))
fig.add_trace(go.Scatter(x=future_dates, y=bootstrap_percentiles[0], fill=None, mode='lines', line=dict(color='lightgrey'), showlegend=False))
fig.add_trace(go.Scatter(x=future_dates, y=bootstrap_percentiles[4], fill='tonexty', mode='lines', line=dict(color='lightgrey'), name='Bootstrap 95% CI'))
fig.add_trace(go.Scatter(x=future_dates, y=bootstrap_percentiles[1], fill=None, mode='lines', line=dict(color='lightyellow'), showlegend=False))
fig.add_trace(go.Scatter(x=future_dates, y=bootstrap_percentiles[3], fill='tonexty', mode='lines', line=dict(color='lightyellow'), name='Bootstrap 68% CI'))
else:
st.warning("Simulation percentiles length does not match future dates. Check simulation output.")
# Thresholds
fig.add_hline(y=thresholds[0], line=dict(color='blue', dash='dash'), annotation_text=f'Threshold 1: {thresholds[0]}', annotation_position="top left")
fig.add_hline(y=thresholds[1], line=dict(color='green', dash='dash'), annotation_text=f'Threshold 2: {thresholds[1]}', annotation_position="top left")
# Probability annotations
textstr_bootstrap = (f'Bootstrap Probabilities:<br>Below {thresholds[0]}: {bootstrap_probabilities["below"]:.2%}<br>'
f'Between {thresholds[0]} and {thresholds[1]}: {bootstrap_probabilities["between"]:.2%}<br>'
f'Above {thresholds[1]}: {bootstrap_probabilities["above"]:.2%}')
fig.add_annotation(xref='paper', yref='paper', x=0.98, y=0.02, text=textstr_bootstrap, showarrow=False, bordercolor="black", borderwidth=1, borderpad=4, bgcolor="black", opacity=0.7)
fig.update_layout(title='Bootstrapping Simulation Cone', xaxis_title='Date', yaxis_title='Price', showlegend=True)
fig.update_xaxes(type='date')
return fig
# Streamlit app
st.set_page_config(layout="wide")
st.title('Future Asset Prices Bootstrap Simulation')
st.sidebar.header('Input Parameters')
with st.sidebar.expander("How to Use", expanded=False):
st.write("""
1. Enter the stock ticker or crypto pair (e.g., 'AAPL' or 'BTC-USD') in the 'Ticker' field.
2. Set the start and end dates for historical data.
3. Adjust the number of days for simulation and number of iterations if desired.
4. Set price thresholds for probability calculations.
5. Click 'Run Simulation' to start the bootstrapping simulation.
6. Analyze the resulting charts and statistics.
""")
with st.sidebar.expander("Symbol and Dates", expanded=True):
ticker = st.text_input('Enter Asset Symbol', 'ASML.AS', help="Enter a stock ticker (e.g., AAPL) or a crypto pair (e.g., BTC-USD)")
start_date = st.date_input('Start Date', pd.to_datetime('2020-01-01'), help="Select the start date for historical data")
end_date = st.date_input('End Date', pd.to_datetime('today') + pd.DateOffset(1), help="Select the end date for historical data")
with st.sidebar.expander("Parameter Settings", expanded=True):
days = st.number_input('Number of Days for Simulation', min_value=1, max_value=365, value=30, help="Number of days to simulate into the future")
n_iterations = st.number_input('Number of Simulations', min_value=100, max_value=100000, value=10000, help="Number of bootstrap iterations to run")
threshold1 = st.number_input('Threshold 1', min_value=0, value=850, help="Lower price threshold for probability calculations")
threshold2 = st.number_input('Threshold 2', min_value=0, value=1050, help="Upper price threshold for probability calculations")
thresholds = [threshold1, threshold2]
st.write("""
### Description
This application simulates future asset prices using bootstrapping simulation methods.
You can specify the stock ticker or crypto pair, the date range, the number of simulation days, the number of simulations, and price thresholds.
The simulation results will show the probability of the price falling below, between, or above the specified thresholds.""")
with st.expander("Click here to read the description"):
st.write("""
### Description
This application simulates future stock or cryptocurrency prices using bootstrapping simulation methods.
You can specify the stock ticker or crypto pair, the date range, the number of simulation days, the number of simulations, and price thresholds.
The simulation results will show the probability of the price falling below, between, or above the specified thresholds.""")
st.write("""**Background and Concept**
The concept of bootstrapping was introduced by Bradley Efron in 1979. The primary goal of bootstrapping is to understand the variability of a statistic by generating multiple samples from the observed data. This approach assumes that the sample data represents the population, allowing us to draw inferences about the population from the sample.
**Steps in Bootstrapping:**
Given a dataset \( X = \{x_1, x_2, ..., x_n\} \), we aim to estimate the statistic \( \theta \) (e.g., the mean return of a stock or cryptocurrency).
1. **Resampling**: Create a resample \( X^* \) by drawing \( n \) observations from \( X \) with replacement. This means that each data point can be selected multiple times in a single resample:
""")
st.latex(r'X^* = \{x_1^*, x_2^*, ..., x_n^*\}')
st.write("""
2. **Statistic Calculation**: Calculate the statistic \( \theta^* \) for the resample \( X^* \):
""")
st.latex(r'\theta^* = f(X^*)')
st.write("""
3. **Repeat**: Repeat the above steps \( B \) times to generate \( B \) bootstrap statistics:
""")
st.latex(r'\{\theta_1^*, \theta_2^*, ..., \theta_B^*\}')
st.write("""
4. **Estimate**: Use the bootstrap statistics to estimate the mean, standard error, and confidence intervals of \( \theta \).
""")
st.write("""**Results:**
The app will display two charts:
1. The distribution of the final simulated prices with key statistical measures.
2. The historical prices with simulated future price cones and the specified thresholds.
""")
if st.sidebar.button('Run Simulation'):
try:
st.write(f"Fetching data for {ticker}...")
data = get_stock_data(ticker, start_date, end_date)
# Debug data
#st.write(f"Data shape: {data.shape}")
#st.write(f"Last few rows: {data.tail()}")
bootstrap_simulations = bootstrap_simulation(data, days, n_iterations)
bootstrap_probabilities = calculate_probabilities(bootstrap_simulations, thresholds)
bootstrap_percentiles = calculate_percentiles(bootstrap_simulations)
# Debug simulation output
#st.write(f"Simulations shape: {bootstrap_simulations.shape}")
#st.write(f"Percentiles shape: {bootstrap_percentiles.shape}")
fig1, mean_bootstrap_price, median_bootstrap_price, ci_68_bootstrap, ci_95_bootstrap, latest_price = plot_distributions(bootstrap_simulations, data, thresholds, bootstrap_probabilities)
fig2 = plot_price_with_cones(data, bootstrap_percentiles, days, thresholds, bootstrap_probabilities)
st.plotly_chart(fig1)
if bootstrap_percentiles.shape[1] == days:
st.plotly_chart(fig2)
else:
st.error("Time series plot failed: Percentiles length does not match simulation days.")
st.write(f"""
### Interpretation of Results
**Distribution of Final Simulated Prices:**
- **Mean Final Price:** {mean_bootstrap_price:.2f}
- **Median Final Price:** {median_bootstrap_price:.2f}
- **68% Confidence Interval (CI):** [{ci_68_bootstrap[0]:.2f}, {ci_68_bootstrap[1]:.2f}]
- **95% Confidence Interval (CI):** [{ci_95_bootstrap[0]:.2f}, {ci_95_bootstrap[1]:.2f}]
- **Latest Price:** {latest_price:.2f}
**Bootstrapping Simulation Cone:**
- **Bootstrap Median:** The median of the simulated future prices for each day.
- **Bootstrap 68% CI:** The 68% confidence interval for the simulated future prices.
- **Bootstrap 95% CI:** The 95% confidence interval for the simulated future prices.
- **Threshold 1 and Threshold 2:** {threshold1:.2f}, {threshold2:.2f}
- **Probability Annotations:**
- The probability of the price being below Threshold 1: {bootstrap_probabilities["below"]:.2%}
- The probability of the price being between Threshold 1 and Threshold 2: {bootstrap_probabilities["between"]:.2%}
- The probability of the price being above Threshold 2: {bootstrap_probabilities["above"]:.2%}
These results help in understanding the potential future movements of the stock or cryptocurrency price based on historical data and bootstrapping simulation.
""")
except Exception as e:
st.error(f"Error: {str(e)}. Check ticker, date range, or try again.")
hide_streamlit_style = """
<style>
#MainMenu {visibility: hidden;}
footer {visibility: hidden;}
</style>
"""
st.markdown(hide_streamlit_style, unsafe_allow_html=True)
st.markdown("""
<style>
.stSelectbox, .stNumberInput {
padding-bottom: 20px;
}
.stExpander {
padding-bottom: 20px;
}
</style>
""", unsafe_allow_html=True) |