Spaces:
Sleeping
Sleeping
| 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) |