In partnership with

What Will Your Retirement Look Like?

Planning for retirement raises many questions. Have you considered how much it will cost, and how you’ll generate the income you’ll need to pay for it? For many, these questions can feel overwhelming, but answering them is a crucial step forward for a comfortable future.

Start by understanding your goals, estimating your expenses and identifying potential income streams. The Definitive Guide to Retirement Income can help you navigate these essential questions. If you have $1,000,000 or more saved for retirement, download your free guide today to learn how to build a clear and effective retirement income plan. Discover ways to align your portfolio with your long-term goals, so you can reach the future you deserve.

Elite Quant Plan – 7-Day Free Trial (This Week Only)

No card needed. Cancel anytime. Zero risk.

You get immediate access to:

  • Full code from every article (including today’s HMM notebook)

  • Private GitHub repos & templates

  • All premium deep dives (3–5 per month)

  • 2 × 1-on-1 calls with me

  • One custom bot built/fixed for you

Try the entire Elite experience for 14 days — completely free.

→ Start your free trial now 👇

(Doors close in 7 days or when the post goes out of the spotlight — whichever comes first.)

See you on the inside.

👉 Upgrade Now

🔔 Limited-Time Holiday Deal: 20% Off Our Complete 2026 Playbook! 🔔

Level up before the year ends!

AlgoEdge Insights: 30+ Python-Powered Trading Strategies – The Complete 2026 Playbook

30+ battle-tested algorithmic trading strategies from the AlgoEdge Insights newsletter – fully coded in Python, backtested, and ready to deploy. Your full arsenal for dominating 2026 markets.

Special Promo: Use code JANUARY2026 for 20% off

Valid only until January 30, 2026 — act fast!

👇 Buy Now & Save 👇

Instant access to every strategy we've shared, plus exclusive extras.

— AlgoEdge Insights Team

Premium Members – Your Full Notebook Is Ready

The complete Google Colab notebook from today’s article (with live data, full Hidden Markov Model, interactive charts, statistics, and one-click CSV export) is waiting for you.

Preview of what you’ll get:

Inside:

  • Estimating Future Stock Price Probability with Bootstrapping

  • Compute and Plot Distribution Results

  • Plot Distribution on Stock Price

  • Beautiful interactive Plotly charts

  • Regime duration & performance tables

  • Ready-to-use CSV export

  • Bonus: works on Bitcoin, SPX, or any ticker with one line change

Free readers – you already got the full breakdown and visuals in the article. Paid members – you get the actual tool.

Not upgraded yet? Fix that in 10 seconds here👇

Google Collab Notebook With Full Code Is Available In the End Of The Article Behind The Paywall 👇 (For Paid Subs Only)

Bootstrapping is a useful technique in financial forecasting for estimating future price movements.

With the help of historical data and simulation methods, we can estimate potential future stock price changes and their associated probabilities.

This article explains how bootstrapping and historical simulations can be used to estimate future market prices.

Additionally, we will go into the details of their applications and an end-to-end implementation on Google Colab.

2. Bootstrapping Simulation

Bootstrapping is a statistical technique which involves resampling with replacement from an original dataset to create many simulated samples.

Replacement means that each time you pick a data point from your dataset, you put it back before the next pick.

This approach allows us to estimate the distribution of a statistic (e.g, mean, variance) by sampling from the data repeatedly.

It is also useful when the theoretical distribution of the statistic is complicated or unknown.

Background and Concept

The concept of bootstrapping was introduced by Bradley Efron in 1979. The main motive behind bootstrapping is to understand the variability of any statistic through multiple sampling from the observed data.

This approach considers that the sample data is the population, and hence, any inference about the population can be done from the sample.

Steps in Bootstrapping:

1. Original Sample: Consider an original dataset of size n, defined as:

2. Resampling: Randomly draw samples of size n from X with replacement. It means in a single resample, every data point may get selected once, more, or not at all.

3. Statistic Calculation: On each resample, calculate the statistic of interest such as mean, median, and standard deviation.

4. Repetition: Repeat the resampling and calculation process B times to create a distribution of the statistic.

5. Estimation: Use the distribution of the statistic from the resamples to estimate its properties, including mean, standard error, and confidence intervals.

Mathematical Formulation

Given a dataset

We estimate the statistic θ. This can be, for instance, the average stock return.

1. Resampling: Create a resample X* by drawing n observations from X with replacement:

2. Statistic Calculation: Calculate the statistic θ* for the resample X*

3. Repeat: Repeat the above steps B times to generate B bootstrap statistics

4. Estimate: Use the bootstrap statistics to estimate the mean, standard error, and confidence intervals of θ.

Future Stock Price Esimation

For stock price prediction, the bootstrapping simulation involves resampling daily returns to simulate future price paths.

Here’s how it works in detail:

1. Daily Returns Calculation: Calculate the daily returns of the stock:

Pt​ is the price at time t.

2. Resampling Returns: Randomly sample daily returns with replacement to generate a simulated sequence of returns for n days.

3. Price Path Simulation: Calculate the cumulative product of the sampled returns to simulate the stock price path:

Si​(t) is the simulated price on day t for simulation i, and P0​ is the initial stock price.

Figure 1. Bootstrap Simulation — This GIF shows a bootstrap simulation to forecast future stock prices. Each frame in the GIF is one new simulated price path generated by resampling the historical daily returns with replacement.

4. Multiple Simulations: Repeat the resampling and price path simulation process B times to develop a distribution of simulated final prices.

Implications in Finance

In finance, bootstrapping has extensive applications:

  1. Estimate confidence intervals for future stock prices by generating multiple simulated price paths.

  2. Assess the risk and return profile of an investment by analyzing the distribution of simulated returns.

  3. Allows scenario analysis by simulating various future outcomes based on historical data.

  4. Portfolio optimization by providing insights into the expected performance and risk of different assets.

3. Historical Simulation

Contrary to bootstrapping, which resamples the returns with replacement, a historical simulation does this without replacement.

This means shuffling actual historical returns to create probable future price paths.

Background and Concept

The historical simulation method is based on the assumption that past movements in a stock’s price can help in predicting its future movements.

It keeps the natural characteristics and distribution of the data observed because it uses actual historical returns.

This approach is useful for capturing the historical patterns and volatility of the stock.

Steps in Historical Simulation:

1. Original Sample: Start with the historical returns of the stock:

rt​ represents the return at time t.

2. Shuffling: Randomly shuffle the historical returns to generate a new sequence of returns.

3. Price Path Simulation: Take a cumulative product to calculate the resulting stock price path for a defined period.

4. Repetition: Repeat step 2 the shuffling and price path simulation process multiple times to create a distribution of simulated final prices.

Mathematical Formulation

Given a dataset of historical returns

the goal is to estimate future prices by creating new sequences of returns through shuffling.

1. Shuffling Returns: Randomly permute the historical returns to generate a shuffled sequence

2. Price Path Simulation: Compute the cumulative product of the shuffled returns to simulate the stock price path:

Si​(t) is the simulated price on day t for simulation i. P0​ is the initial stock price.

3. Multiple Simulations: Repeat the shuffling and price path simulation process B times to generate a distribution of simulated final prices.

Figure 2. Historical Simulation: each frame compares original historical returns to shuffled returns and then reveals the resulting simulated price path. The actual distribution of returns is preserved; however, potential future scenarios are generated.

4. Python Implementation

Get Stock Data

The yfinance library helps us fetch stock price data easily.

The function below is gets the closing prices of a given stock within a specified date range:

import yfinance as yf
import numpy as np
import matplotlib.pyplot as plt

# Function to fetch stock data from Yahoo Finance
def get_stock_data(ticker, start_date, end_date):
    stock_data = yf.download(ticker, start=start_date, end=end_date)
    return stock_data['Close']

Bootstrapping Simulation

The function below draws samples with replacement many times from historic daily returns to create repeated simulated future paths of price movements.

# Bootstrapping simulation function
def bootstrap_simulation(data, days, n_iterations=10000):
    # Calculate daily returns and remove any missing values
    daily_returns = data.pct_change().dropna()
    # Initialize a matrix to store the simulation results
    simulations = np.zeros((n_iterations, days))

    # Perform the bootstrapping simulation
    for i in range(n_iterations):
        # Randomly sample daily returns with replacement
        sample = np.random.choice(daily_returns, size=days, replace=True)
        # Calculate the cumulative product of the sampled returns and scale by the last known price
        simulations[i] = np.cumprod(1 + sample) * data.iloc[-1]

    # Return the simulation results
    return simulations

Historical Simulation

Historical simulation shuffles actual historical returns without replacement to generate future price paths.

Here’s the implementation:

# Historical simulation function
def historical_simulation(data, days, n_iterations=10000):
    # Calculate daily returns and remove any missing values
    daily_returns = data.pct_change().dropna()
    # Initialize a matrix to store the simulation results
    simulations = np.zeros((n_iterations, days))

    # Perform the historical simulation
    for i in range(n_iterations):
        # Shuffle the historical daily returns
        shuffled_returns = np.random.permutation(daily_returns)
        # Select the first 'days' returns from the shuffled returns
        sample = shuffled_returns[:days]
        # Calculate the cumulative product of the shuffled returns and scale by the last known price
        simulations[i] = np.cumprod(1 + sample) * data.iloc[-1]

    # Return the simulation results
    return simulations

Calculating Probabilities

Now that we ran our simulations (both bootstrapping and historical), we have a large number of simulated final prices.

These simulations allow us to estimate the probability of the stock price falling below, between, or above specific thresholds.

1. Run Simulations:

First, perform the bootstrapping and historical simulations to generate multiple future price paths.

Then, the result of these simulations is a matrix where each row represents a simulation. Each column represents the price at a particular day in the future.

2. Extract Final Prices:

Extract the final prices from each simulation. If each simulation runs for n days, then the final price for a simulation is the price on the n-th day.

3. Define Thresholds:

Define the price thresholds that you are interested in. For example, you might want to know the probability that the price will be below $850, between $850 and $1000, or above $1000.

4. Calculate Probabilities:

Probability Below Threshold 1: Calculate the proportion of simulated final prices that are less than the first threshold.

  • Probability Between Thresholds: Calculate the proportion of simulated final prices that lie between the two thresholds.

  • Probability Above Threshold 2: Calculate the proportion of simulated final prices that are greater than the second threshold.

# Calculate probabilities
def calculate_probabilities(simulations, thresholds):
    # Get the final prices from the simulations
    final_prices = simulations[:, -1]
    # Calculate the probability of final prices being below the first threshold
    below = np.mean(final_prices < thresholds[0])
    # Calculate the probability of final prices being above the second threshold
    above = np.mean(final_prices > thresholds[1])
    # Calculate the probability of final prices being between the two thresholds
    between = np.mean((final_prices >= thresholds[0]) & (final_prices <= thresholds[1]))

    # Return the probabilities as a dictionary
    return {'below': below, 'between': between, 'above': above}

Plotting the Distributions

We can now plot the distributions of the simulated final prices for both bootstrapping and historical simulations:

# Plot distribution
def plot_distributions(bootstrap_simulations, historical_simulations, data, thresholds, bootstrap_probabilities, historical_probabilities):
    # Extract final prices from the simulations
    final_bootstrap_prices = bootstrap_simulations[:, -1]
    final_historical_prices = historical_simulations[:, -1]

    # Calculate statistics for bootstrapping
    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])

    # Calculate statistics for historical simulation
    mean_historical_price = np.mean(final_historical_prices)
    median_historical_price = np.median(final_historical_prices)
    ci_68_historical = np.percentile(final_historical_prices, [16, 84])
    ci_95_historical = np.percentile(final_historical_prices, [2.5, 97.5])

    # Get the latest known price
    latest_price = data.iloc[-1]

    # Create subplots for side-by-side comparison
    fig, axs = plt.subplots(1, 2, figsize=(18, 8), sharey=True)

    # Plot for Bootstrapping
    axs[0].hist(final_bootstrap_prices, bins=50, color='blue', alpha=0.7, label='Simulated Final Prices')
    axs[0].axvline(mean_bootstrap_price, color='red', linestyle='dashed', linewidth=1, label=f'Mean: {mean_bootstrap_price:.2f}')
    axs[0].axvline(median_bootstrap_price, color='orange', linestyle='dashed', linewidth=1, label=f'Median: {median_bootstrap_price:.2f}')
    axs[0].axvline(latest_price, color='green', linestyle='dashed', linewidth=1, label=f'Latest Price: {latest_price:.2f}')
    axs[0].axvspan(ci_68_bootstrap[0], ci_68_bootstrap[1], color='yellow', alpha=0.2, label='68% CI')
    axs[0].axvspan(ci_95_bootstrap[0], ci_95_bootstrap[1], color='grey', alpha=0.2, label='95% CI')
    axs[0].set_xlabel('Final Price')
    axs[0].set_ylabel('Frequency')
    axs[0].set_title('Bootstrapping Simulation')
    axs[0].legend(loc='upper right')
    textstr = '\n'.join((
        f'Probability below {thresholds[0]}: {bootstrap_probabilities["below"]:.2%}',
        f'Probability between {thresholds[0]} and {thresholds[1]}: {bootstrap_probabilities["between"]:.2%}',
        f'Probability above {thresholds[1]}: {bootstrap_probabilities["above"]:.2%}'
    ))
    axs[0].text(0.02, 0.95, textstr, transform=axs[0].transAxes, fontsize=12,
                verticalalignment='top', bbox=dict(facecolor='white', alpha=0.5))

    # Plot for Historical Simulation
    axs[1].hist(final_historical_prices, bins=50, color='blue', alpha=0.7, label='Simulated Final Prices')
    axs[1].axvline(mean_historical_price, color='red', linestyle='dashed', linewidth=1, label=f'Mean: {mean_historical_price:.2f}')
    axs[1].axvline(median_historical_price, color='orange', linestyle='dashed', linewidth=1, label=f'Median: {median_historical_price:.2f}')
    axs[1].axvline(latest_price, color='green', linestyle='dashed', linewidth=1, label=f'Latest Price: {latest_price:.2f}')
    axs[1].axvspan(ci_68_historical[0], ci_68_historical[1], color='yellow', alpha=0.2, label='68% CI')
    axs[1].axvspan(ci_95_historical[0], ci_95_historical[1], color='grey', alpha=0.2, label='95% CI')
    axs[1].set_xlabel('Final Price')
    axs[1].set_title('Historical Simulation')
    axs[1].legend(loc='upper right')
    textstr = '\n'.join((
        f'Probability below {thresholds[0]}: {historical_probabilities["below"]:.2%}',
        f'Probability between {thresholds[0]} and {thresholds[1]}: {historical_probabilities["between"]:.2%}',
        f'Probability above {thresholds[1]}: {historical_probabilities["above"]:.2%}'
    ))
    axs[1].text(0.02, 0.95, textstr, transform=axs[1].transAxes, fontsize=12,
                verticalalignment='top', bbox=dict(facecolor='white', alpha=0.5))

    plt.tight_layout()
    plt.show()

# Parameters
ticker = 'ASML.AS'
start_date = '2020-01-01'
end_date = '2025-01-01'
days = 30
thresholds = [850, 1000]  # Example thresholds

# Fetch data
data = get_stock_data(ticker, start_date, end_date)

# Perform bootstrapping simulations
bootstrap_simulations = bootstrap_simulation(data, days)

# Perform historical simulations
historical_simulations = historical_simulation(data, days)

# Calculate probabilities for bootstrapping
bootstrap_probabilities = calculate_probabilities(bootstrap_simulations, thresholds)

# Calculate probabilities for historical simulation
historical_probabilities = calculate_probabilities(historical_simulations, thresholds)

# Print probabilities
print(f"Bootstrapping - Probability of falling below {thresholds[0]}: {bootstrap_probabilities['below']:.2%}")
print(f"Bootstrapping - Probability of falling between {thresholds[0]} and {thresholds[1]}: {bootstrap_probabilities['between']:.2%}")
print(f"Bootstrapping - Probability of rising above {thresholds[1]}: {bootstrap_probabilities['above']:.2%}")

print(f"Historical - Probability of falling below {thresholds[0]}: {historical_probabilities['below']:.2%}")
print(f"Historical - Probability of falling between {thresholds[0]} and {thresholds[1]}: {historical_probabilities['between']:.2%}")
print(f"Historical - Probability of rising above {thresholds[1]}: {historical_probabilities['above']:.2%}")

# Plot distributions side by side
plot_distributions(bootstrap_simulations, historical_simulations, data, thresholds, bootstrap_probabilities, historical_probabilities)

Figure. 3: Comparison of Bootstrapping and Historical Simulation Distributions — The left chart shows the distribution of simulated final prices using bootstrapping, while the right chart shows the same using historical simulation.

Plotting Price Data with Simulation Cones

We also plot the historical stock prices along with the simulation price cones for both bootstrapping and historical simulations. Here’s how:

# Plot price data with simulation cones
def plot_price_with_cones(data, bootstrap_percentiles, historical_percentiles, days, thresholds, bootstrap_probabilities, historical_probabilities):
    plt.figure(figsize=(12, 16))

    # Plot the actual stock data
    plt.subplot(2, 1, 1)
    plt.plot(data.index, data, label='Historical Prices')
    last_date = data.index[-1]

    # Plot the bootstrapping simulation cone
    future_dates = [last_date + np.timedelta64(i, 'D') for i in range(1, days + 1)]
    plt.fill_between(future_dates, bootstrap_percentiles[0], bootstrap_percentiles[4], color='grey', alpha=0.2, label='95% CI')
    plt.fill_between(future_dates, bootstrap_percentiles[1], bootstrap_percentiles[3], color='yellow', alpha=0.2, label='68% CI')
    plt.plot(future_dates, bootstrap_percentiles[2], color='red', linestyle='--', label='Median')

    # Annotate the thresholds
    plt.axhline(thresholds[0], color='blue', linestyle='--', linewidth=1, label=f'Threshold: {thresholds[0]}')
    plt.axhline(thresholds[1], color='green', linestyle='--', linewidth=1, label=f'Threshold: {thresholds[1]}')

    # Annotate the final median price
    final_median_bootstrap = bootstrap_percentiles[2][-1]
    plt.axhline(final_median_bootstrap, color='red', linestyle='--')
    plt.annotate(f'Median: {final_median_bootstrap:.2f}', xy=(future_dates[-1], final_median_bootstrap), xytext=(future_dates[-1], final_median_bootstrap + 20),
                 arrowprops=dict(facecolor='black', shrink=0.05), fontsize=12, color='red')

    # Annotate the probabilities
    textstr = '\n'.join((
        f'Probability below {thresholds[0]}: {bootstrap_probabilities["below"]:.2%}',
        f'Probability between {thresholds[0]} and {thresholds[1]}: {bootstrap_probabilities["between"]:.2%}',
        f'Probability above {thresholds[1]}: {bootstrap_probabilities["above"]:.2%}'
    ))
    plt.text(0.35, 0.95, textstr, transform=plt.gca().transAxes, fontsize=12, verticalalignment='top', bbox=dict(facecolor='white', alpha=0.5))

    plt.title('Bootstrapping Simulation Cone')
    plt.xlabel('Date')
    plt.ylabel('Price')
    plt.legend(loc='upper left')

    # Plot the historical simulation cone
    plt.subplot(2, 1, 2)
    plt.plot(data.index, data, label='Historical Prices')
    plt.fill_between(future_dates, historical_percentiles[0], historical_percentiles[4], color='grey', alpha=0.2, label='95% CI')
    plt.fill_between(future_dates, historical_percentiles[1], historical_percentiles[3], color='yellow', alpha=0.2, label='68% CI')
    plt.plot(future_dates, historical_percentiles[2], color='red', linestyle='--', label='Median')

    # Annotate the thresholds
    plt.axhline(thresholds[0], color='blue', linestyle='--', linewidth=1, label=f'Threshold: {thresholds[0]}')
    plt.axhline(thresholds[1], color='green', linestyle='--', linewidth=1, label=f'Threshold: {thresholds[1]}')

    # Annotate the final median price
    final_median_historical = historical_percentiles[2][-1]
    plt.axhline(final_median_historical, color='red', linestyle='--')
    plt.annotate(f'Median: {final_median_historical:.2f}', xy=(future_dates[-1], final_median_historical), xytext=(future_dates[-1], final_median_historical + 20),
                 arrowprops=dict(facecolor='black', shrink=0.05), fontsize=12, color='red')

    # Annotate the probabilities
    textstr = '\n'.join((
        f'Probability below {thresholds[0]}: {historical_probabilities["below"]:.2%}',
        f'Probability between {thresholds[0]} and {thresholds[1]}: {historical_probabilities["between"]:.2%}',
        f'Probability above {thresholds[1]}: {historical_probabilities["above"]:.2%}'
    ))
    plt.text(0.35, 0.95, textstr, transform=plt.gca().transAxes, fontsize=12, verticalalignment='top', bbox=dict(facecolor='white', alpha=0.5))

    plt.title('Historical Simulation Cone')
    plt.xlabel('Date')
    plt.ylabel('Price')
    plt.legend(loc='upper left')

    plt.tight_layout()
    plt.show()

# Parameters
ticker = 'ASML.AS'
start_date = '2020-01-01'
end_date = '2025-01-01'
days = 30
thresholds = [850, 1000]  # Example thresholds

# Get data
data = get_stock_data(ticker, start_date, end_date)

# Perform bootstrapping simulations
bootstrap_simulations = bootstrap_simulation(data, days)

# Perform historical simulations
historical_simulations = historical_simulation(data, days)

# Calculate percentiles for bootstrapping and historical simulations
bootstrap_percentiles = calculate_percentiles(bootstrap_simulations)
historical_percentiles = calculate_percentiles(historical_simulations)

# Calculate probabilities for bootstrapping and historical simulations
bootstrap_probabilities = calculate_probabilities(bootstrap_simulations, thresholds)
historical_probabilities = calculate_probabilities(historical_simulations, thresholds)

# Plot price data with simulation cones and annotations
plot_price_with_cones(data, bootstrap_percentiles, historical_percentiles, days, thresholds, bootstrap_probabilities, historical_probabilities)

Figure. 4: Comparison of Bootstrapping and Historical Simulation Cones for Stock Price Prediction — The top chart shows the bootstrapping simulation cone. The bottom chart shows the historical simulation cone. Both charts highlight the predicted price ranges with 68% and 95% confidence intervals, along with the probabilities of the stock price falling below $850, between $850 and $1000, and above $1000 by mid-2024.

Concluding Thoughts

Bootstrapping stock prices offers a useful approach to forecast potential financial outcomes.

It has applications in risk management, portfolio optimization and even derivatives trading.

For example, in options trading, the probabilities of different price movements helps in developing strategies more accurately.

A strategy could involve selling put options with a strike price that is unlikely to be reached, based on the simulated probabilities.

logo

Subscribe to our premium content to read the rest.

Become a paying subscriber to get access to this post and other subscriber-only content.

Upgrade

Keep Reading