Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Monte Carlo Simulation for Investment Returns

Hello! Welcome back.

In our last lesson, we explored scenario analysis by creating 'Best Case', 'Base Case', and 'Worst Case' models. This gave us a good sense of the range of potential outcomes based on a few plausible "stories" about the future. However, reality isn't limited to just three stories; it's a continuous spectrum of possibilities.

Today, we take a major step forward in quantifying uncertainty. Instead of three scenarios, what if we could analyze ten thousand? Or a million? This is precisely what a Monte Carlo simulation allows us to do. Our learning outcome for this lesson is to perform a Monte Carlo simulation to generate a probabilistic distribution of investment returns. This technique moves us from simple "what-if" analysis to a full-fledged probabilistic risk assessment, a standard practice in institutional-grade investment analysis.

1. From Scenarios to Simulations

The core limitation of scenario analysis is that it only provides a few discrete data points. A Monte Carlo simulation, also known as stochastic modeling, bridges the gaps between those points. It builds a comprehensive picture of risk and reward by running a financial model over and over, each time with slightly different, randomly generated inputs.

To get a clear conceptual overview, let's watch a brief introduction.

Video Tutorial - Apartment Acquisition Model with Monte Carlo Simulation Module

This video from Adventures in CRE provides an excellent, concise explanation of what a Monte Carlo simulation is and how it differs from the deterministic (single-outcome) models we've used so far.

Please watch from 00:42 to 02:05. Focus on the definitions of Monte Carlo simulation and a deterministic model.

As the video explains, the key is to "layer in probability into your assumptions." Instead of picking one value for rent growth (e.g., 2%), we define it as a probability distribution.

2. The Core Components of a Monte Carlo Simulation

A Monte Carlo simulation consists of three main steps:

  1. Define Inputs as Probability Distributions: We identify the key uncertain variables in our model (the same ones we used for sensitivity analysis) and assign each a probability distribution that reflects its likely behavior.
  2. Run Iterations: We run the model thousands of times. In each iteration, we draw one random value for each uncertain variable from its assigned distribution.
  3. Analyze Outputs: We collect the output from each iteration (e.g., the NPV or IRR) to form a large dataset. Analyzing this dataset gives us a probability distribution of the investment's potential outcomes.

The article "Stop Guessing About Risk" eloquently frames this shift from deterministic to probabilistic thinking.

Stop Guessing About Risk: Use Monte Carlo Simulation in Python to Make Data-Driven Decisions Under Uncertainty

This article by Dr. Ernesto Lee provides the philosophical and practical motivation for using Monte Carlo simulation. It will help you appreciate why this method is so powerful for decision-making under uncertainty.

Please read the introduction, from the beginning down to the section titled 'The Fundamental Principle: Embracing Randomness to Reduce Uncertainty'. Focus on the contrast between deterministic thinking and probabilistic modeling.

Choosing the Right Probability Distribution

The choice of distribution is crucial. For financial modeling, a few common ones cover most use cases:

  • Normal Distribution: Defined by a mean () and a standard deviation (). It's suitable for variables that cluster around an average, like rent growth or inflation. About 68% of outcomes fall within one standard deviation of the mean.
  • Uniform Distribution: Defined by a minimum and a maximum value. It's used when any outcome within a range is considered equally likely. For example, you might believe the time to find a new tenant will be between 1 and 3 months, with no particular duration being more probable.
  • Triangular Distribution: Defined by a minimum, most likely, and maximum value. This is often the most intuitive for real estate, as it's easier for an analyst to estimate a pessimistic, optimistic, and base-case value than it is to estimate a standard deviation.

Let's see how these are applied in a practical model.

Video Tutorial - Apartment Acquisition Model with Monte Carlo Simulation Module

Returning to the Adventures in CRE video, this segment demonstrates how to assign Normal and Uniform distributions to key variables like rent growth in an Excel model. The visual representation is very helpful.

Watch the section from 02:05 to 06:49 to see the Normal distribution explained and applied. Then, skim the section from 08:19 to 09:34 for a clear explanation of the Uniform distribution.

3. Implementing a Monte Carlo Simulation in Python

Now, let's bring this to life with code. Your background in computer engineering will make the logic here very familiar. We'll essentially write a for loop that calls our financial calculation function thousands of times.

We will adapt our calculate_npv function from the previous lesson and wrap it in a Monte Carlo simulation engine. We'll make the following probabilistic assumptions:

  • Monthly Rent: We'll model the starting rent with a triangular distribution. Let's assume a minimum of 4800€, a most likely value of 5000€, and a maximum of 5500€.
  • Vacancy Rate: We'll use a triangular distribution here as well. Min: 3%, Most Likely: 5%, Max: 10%.
  • Exit Cap Rate: We'll model this with a normal distribution. Let's assume a mean of 6% and a standard deviation of 0.5%. This implies that while 6% is the average, there's a 68% chance it will fall between 5.5% and 6.5%.
import numpy as np
import matplotlib.pyplot as plt

# --- 1. Financial Model Function (unchanged) ---
def calculate_npv(monthly_rent, vacancy_rate, exit_cap_rate,
                  opex_ratio=0.35, purchase_price=1_000_000, discount_rate=0.08, hold_period=5):
    """
    Calculates NPV for a holding period based on a set of parameters.
    """
    annual_rent = monthly_rent * 12
    op_ex = opex_ratio * annual_rent
    
    # Calculate NOI
    noi = annual_rent * (1 - vacancy_rate) - op_ex
    
    # Calculate cash flows for the hold period
    cash_flows = [noi] * hold_period
    
    # Calculate reversion value (sale price) at the end of the hold period
    sale_price = noi / exit_cap_rate
    cash_flows[-1] += sale_price
    
    # Combine initial investment with future cash flows
    all_cash_flows = [-purchase_price] + cash_flows
    
    # Calculate and return NPV
    return np.npv(discount_rate, all_cash_flows)

# --- 2. Monte Carlo Simulation Setup ---
num_simulations = 10000
npv_results = []

print(f"Running {num_simulations} simulations...")

# --- 3. The Simulation Loop ---
for i in range(num_simulations):
    # Draw random values from probability distributions for each uncertain variable
    
    # Triangular distribution for rent: (min, most_likely, max)
    sim_rent = np.random.triangular(4800, 5000, 5500)
    
    # Triangular distribution for vacancy: (min, most_likely, max)
    sim_vacancy = np.random.triangular(0.03, 0.05, 0.10)
    
    # Normal distribution for exit cap rate: (mean, standard_deviation)
    sim_exit_cap = np.random.normal(0.06, 0.005)

    # Run the model with the simulated inputs
    npv = calculate_npv(
        monthly_rent=sim_rent,
        vacancy_rate=sim_vacancy,
        exit_cap_rate=sim_exit_cap
    )
    
    # Store the result
    npv_results.append(npv)

print("Simulation complete.")

# --- 4. Analyze and Visualize the Results ---
npv_results = np.array(npv_results)

# Calculate key statistics
mean_npv = np.mean(npv_results)
median_npv = np.median(npv_results)
std_dev_npv = np.std(npv_results)
prob_positive_npv = (npv_results > 0).sum() / num_simulations * 100

print("\n--- Monte Carlo Simulation Results ---")
print(f"Mean NPV: {mean_npv:,.2f}€")
print(f"Median NPV: {median_npv:,.2f}€")
print(f"Standard Deviation of NPV: {std_dev_npv:,.2f}€")
print(f"Probability of Positive NPV: {prob_positive_npv:.2f}%")

# Calculate percentiles
p5 = np.percentile(npv_results, 5)
p95 = np.percentile(npv_results, 95)
print(f"5th Percentile (Value at Risk): {p5:,.2f}€")
print(f"95th Percentile: {p95:,.2f}€")

# Visualize the distribution of NPVs
plt.figure(figsize=(10, 6))
plt.hist(npv_results, bins=50, edgecolor='black', alpha=0.7)
plt.title('Distribution of Net Present Value (NPV) Outcomes')
plt.xlabel('Net Present Value (€)')
plt.ylabel('Frequency')
plt.axvline(mean_npv, color='red', linestyle='dashed', linewidth=2, label=f'Mean NPV: {mean_npv:,.0f}€')
plt.axvline(0, color='black', linestyle='solid', linewidth=2, label='Break-even (NPV = 0)')
plt.legend()
plt.grid(axis='y', alpha=0.5)
plt.show()

This Python script is a complete Monte Carlo simulation. It defines our model, runs 10,000 iterations with randomized inputs, and then calculates and visualizes the results.

4. Interpreting the Simulation Output

The output of the simulation isn't a single number, but a rich distribution of possibilities.

  • The Histogram: This is your primary visualization. The shape tells you the story. Is it symmetric? Is it skewed towards positive or negative outcomes? How wide is the spread? A wider spread means higher risk.
    Portfolio Value Distribution
    This is a typical histogram output from a Monte Carlo simulation. It shows the frequency of different final portfolio values. The red line marks the 5% Value at Risk (VaR), indicating the point below which the worst 5% of outcomes fall.

  • Mean vs. Median: The mean is the average outcome. If the distribution is skewed, the median (50th percentile) can be a more representative measure of the central tendency.

  • Standard Deviation: This is a direct measure of risk or volatility. A higher standard deviation means the outcomes are more spread out and the investment is less predictable.

  • Probability of Positive NPV: This is one of the most powerful metrics. It answers the crucial question: "What are the chances this investment will actually create value?" In our example, this is a direct measure of the likelihood of success.

  • Percentiles & Value at Risk (VaR): The 5th percentile tells you the value that you have a 95% chance of exceeding. The difference between your initial investment and this value is often called the "Value at Risk". It answers the question: "In a worst-case scenario (the bottom 5% of outcomes), how much could I lose?"

This entire process is a foundational step for more advanced risk analysis. As the article "Beyond IRR" highlights, this workflow is standard practice for calculating professional risk metrics.

Beyond IRR: The Advanced Risk Management Ratios That ...

This article, 'Beyond IRR', which we will explore more in the next module, outlines a professional workflow for risk analysis. Notice that running a Monte Carlo simulation is a key prerequisite.

Briefly read the section titled 'The Workflow'. You don't need to understand all the ratios yet, but notice that Step 4 is 'Run 10,000-iteration Monte Carlo'. This shows how central today's topic is to advanced analysis.

Test your understanding!

In our Python code, we modeled the exit cap rate using a normal distribution: np.random.normal(0.06, 0.005). This implies the cap rate can theoretically take any value, although values far from the mean are highly unlikely.

Suppose you strongly believe, based on market analysis, that the exit cap rate will absolutely not go below 4.5% or above 7.5%, and you feel the most likely outcome is 6%. Which probability distribution would be a better choice, and how would you change the line of code to implement it?

Show answer

A triangular distribution would be a better choice because it allows you to set hard minimum and maximum boundaries, which a normal distribution doesn't have.

You would change this line of code:
sim_exit_cap = np.random.normal(0.06, 0.005)

To this:
sim_exit_cap = np.random.triangular(0.045, 0.06, 0.075)

This more accurately reflects your market view that the exit cap rate is bounded between 4.5% and 7.5%, with a peak likelihood at 6%.

Conclusion

Today you've learned one of the most powerful techniques in quantitative finance. By moving from deterministic models to probabilistic simulations, you can transform vague feelings about risk into concrete, measurable probabilities.

Key Takeaways:

  • Monte Carlo simulation quantifies risk by running a model thousands of times with randomly sampled inputs from probability distributions.
  • It provides a distribution of potential outcomes (e.g., a histogram of NPVs), not a single-point estimate.
  • Key inputs like rent, vacancy, and cap rates are modeled using distributions like Normal, Uniform, or Triangular, depending on your assumptions about their behavior.
  • The analysis of the output distribution yields critical insights, such as the mean expected return, the volatility (standard deviation), and the probability of achieving a positive return.

Preview of the Next Lesson:

This lesson concludes the main conceptual topics of this module on risk modeling. We've covered sensitivity analysis, tornado diagrams, scenario analysis, and now Monte Carlo simulation. In the final lesson of this module, we will have a capstone exercise: "Write Python code to perform sensitivity and scenario analysis on a property's returns." This will be an opportunity to integrate and solidify the coding skills you've developed throughout this module into a more comprehensive analysis script.

Can't find a good explanation? Sign up and we'll make it for you

Sign up