Monte Carlo Simulation in Python: Advanced Investment Risk Analysis

Unlocking strategies and risk management through practical simulation techniques

Michael Whittle
Level Up Coding

--

Licensed Image from Adobe Stock

What is Monte Carlo Simulation?

Monte Carlo Simulation is a computational algorithm that utilises repeated random sampling to obtain numerical results. The fundamental principle underlying this method is the use of randomness to solve problems that might be deterministic in principle. It is named after the Monte Carlo Casino in Monaco due to the element of chance inherent in the method, similar to gambling. This approach is particularly useful in many fields, including finance and trading, for modelling scenarios that involve uncertainty and predicting the impact of risk.

I’m particularly interested in how it’s used in finance. In the context of stock and cryptocurrency markets, Monte Carlo Simulation is employed to forecast future price movements by simulating a wide range of possible outcomes for the prices of assets. Given the random nature of financial markets, this method is well-suited for assessing risks and uncertainties inherent in investing. Investors and analysts use it to model the probability of different earning potentials, helping them to make informed decisions by understanding the range of possible outcomes and the likelihood of achieving various levels of return.

Monte Carlo Simulation for Stocks and Cryptocurrencies

For stocks and cryptocurrencies, the simulation often involves projecting future prices based on historical volatility and price trends. The process typically involves the following:

  1. Historical Data Analysis: Analysing historical price data (such as Open, High, Low, Close (OHLC) data) to determine the average return and volatility.
  2. Random Sample Generation: Using statistical models to generate random daily returns, based on the historical average return and volatility.
  3. Price Simulation: Repeatedly calculating future price paths by applying the randomly generated returns to the current price.
  4. Result Analysis: Assessing the distribution of the simulated future prices to estimate the probability of different outcomes.

Demonstrating Monte Carlo Simulation in Python

I want to show you how this works in two parts, so it’s easier to understand.

Part 1

The first step is to retrieve some historical trading data to work with. I have a subscription with EODHD APIs, and that’s where I retrieve my data for analysis. They also have a Python library called “eodhd” that makes retrieving the data a trivial task. For this demonstration I’m going to retrieve the S&P 500 daily stock data.

python3 -m pip install eodhd -U
import numpy as np
import matplotlib.pyplot as plt
from eodhd import APIClient

API_KEY = "<YOUR_KEY>"

api = APIClient(API_KEY)
df = api.get_historical_data("GSPC.INDX", "d", results=365)

print(df)
Screenshot by Author
# Calculate daily returns
daily_returns = df["adjusted_close"].pct_change().dropna()
print(daily_returns)
Screenshot by Author
# Simulation parameters
num_simulations = 1000
forecast_days = 365

# Initialise simulation array, all zeros
simulations = np.zeros((num_simulations, forecast_days))

# Simulate future paths
last_price = df["adjusted_close"].iloc[-1]
for i in range(num_simulations):
cumulative_returns = np.random.choice(daily_returns, size=forecast_days, replace=True).cumsum()
simulations[i, :] = last_price * (1 + cumulative_returns)

print(simulations)
Screenshot by Author

Plotting the results…

# Plotting the results
plt.figure(figsize=(10, 6))
plt.plot(simulations.T, color="blue", alpha=0.025)
plt.title("Monte Carlo Simulation of Future Prices")
plt.xlabel("Day")
plt.ylabel("Price")
plt.show()
Screenshot by Author

This looks nice and all, but what is it really showing us?

  1. Volatility Representation: The simulation captures the range of possible price movements based on historical volatility, providing a visual representation of uncertainty. However, it assumes that past volatility patterns will continue, which may not always be the case.
  2. Predictive Limitations: While useful for understanding potential outcomes, the simulation cannot predict specific future prices. Market conditions, economic factors, and unforeseen events can greatly influence actual results.
  3. Scenario Planning: The distribution of simulated outcomes can help investors plan for various scenarios, assessing the potential for gains against the risk of losses. It encourages a probabilistic rather than deterministic view of future market movements.
  4. Model Assumptions: The accuracy of the simulation heavily depends on the assumptions made about return distributions and volatility. Different models (e.g., assuming normal vs. log-normal returns) can yield different outcomes, highlighting the importance of model selection and calibration.

Part 2

In Part 1, I wanted to give you an introduction to the basics. I don’t expect you will be able to do anything useful with it yet. That’s what I intend to show you now.

The practical application of Monte Carlo Simulation in assessing investment risk and making investment decisions involves several key steps. By understanding the range and distribution of potential future prices generated by the simulation shown in Part 1, investors can quantify the risk and potential return of an investment. Here’s how it’s practically applied:

1. Defining Investment Objectives and Risk Tolerance

An investor or portfolio manager will start by defining their investment objectives, including the expected return and the level of risk they are willing to tolerate. Risk tolerance can be influenced by factors such as the investment horizon, financial goals, and the investor’s personal comfort with uncertainty.

2. Running the Monte Carlo Simulation

Using historical data and statistical models, a large number of simulations are run to project future price paths for the investment under consideration (stocks, cryptocurrencies, etc.). Each simulation represents a possible future scenario based on the random occurrence of daily returns, mirroring historical volatility and return patterns. Hopefully this was clear from Part 1.

3. Analysing the Simulation Output

The output of the Monte Carlo Simulation provides a range of possible future prices at a specified time in the future. This range is then analysed to understand the potential outcomes of the investment. Key metrics derived from the simulation include:

  • Probability Distributions: Investors will look at the distribution of simulated end prices to understand the spread of outcomes. For example, a wide spread indicates high volatility and hence higher risk.
  • Value at Risk (VaR): This metric estimates the maximum potential loss over a specified time frame at a given confidence level. For instance, a 95% VaR of £1,000 means that there is a 95% chance that the investor will not lose more than £1,000 over the specified period.
  • Expected Tail Loss (ETL): Also known as Conditional VaR, ETL provides an average of the losses that occur beyond the VaR threshold, offering insight into the potential losses in the worst-case scenarios.

4. Making Informed Decisions

Based on the analysis, investors can make informed decisions by comparing the risk and return profile revealed by the Monte Carlo Simulation against their investment objectives and risk tolerance. For instance:

  • If the probability of achieving the desired return exceeds the investor’s threshold for success and the associated risks (VaR, ETL) are within their risk tolerance, the investment might be considered acceptable.
  • If the simulation shows a high probability of returns falling short of goals or risks exceeding the investor’s tolerance, they might decide to adjust their investment strategy. This could involve diversifying the portfolio, choosing investments with different risk-return profiles, or altering the investment horizon.

Practical Example:

I want to consider investing £10,000 in the S&P 500, and I want to use Monte Carlo Simulation to assess the risk. The simulation should show that, with 95% confidence, the investment will not lose more than £2,000 over the next year (95% VaR). Additionally, the simulation should indicate there’s a 50% chance of achieving at least a 10% return.

As an example, my risk tolerance allows for a potential £2,000 loss and the goal is a 10% return, this investment might be considered within acceptable risk parameters. However, if the potential loss exceeds the investor’s risk tolerance, or if the likelihood of achieving the desired return is deemed too low, the investor might look for alternatives or modify the investment amount.

This process illustrates the practical use of Monte Carlo Simulation in risk assessment and decision-making for investments. It provides a structured way to consider both the upside potential and the downside risk, supporting more nuanced investment strategies that align with specific risk-return objectives.

What would this look like in Python?

First, I want to make some adjustments to my simulation parameters.

# Calculate daily returns
daily_returns = df["adjusted_close"].pct_change().dropna()

# Simulation parameters
initial_investment = 10000 # Initial investment amount
num_simulations = 1000 # Number of simulations
forecast_days = 365 # Investment horizon in days
desired_return = 0.10 # Desired return (10%)

# Calculate the average daily return
average_daily_return = daily_returns.mean()

# Calculate volatility as the standard deviation of daily returns
volatility = daily_returns.std()

print(f"Average Daily Return: {average_daily_return}")
print(f"Volatility: {volatility}")
Screenshot by Author

Just for interest sake, if you would prefer log-normal returns, you can do this…

daily_returns = np.log(df["adjusted_close"] / df["adjusted_close"].shift(1)).dropna()

I improved on the simulation logic…

# Simulating future returns
simulated_end_returns = np.zeros(num_simulations)
for i in range(num_simulations):
random_returns = np.random.normal(average_daily_return, volatility, forecast_days)
cumulative_return = np.prod(1 + random_returns)
simulated_end_returns[i] = initial_investment * cumulative_return

# Calculate the final investment values
final_investment_values = simulated_end_returns

print(final_investment_values)
Screenshot by Author

This screenshot just shows a subset of the “final_investment_values” array to give you an idea of what it looks like.

Calculate Value at Risk (VaR) and Expected Tail Loss (Conditional VaR)

confidence_level = 0.95
sorted_returns = np.sort(final_investment_values)
index_at_var = int((1-confidence_level) * num_simulations)
var = initial_investment - sorted_returns[index_at_var]
conditional_var = initial_investment - sorted_returns[:index_at_var].mean()

print(f"Value at Risk (95% confidence): £{var:,.2f}")
print(f"Expected Tail Loss (Conditional VaR): £{conditional_var:,.2f}")
Screenshot by Author

What is this telling me?

Value at Risk (VaR) and Expected Tail Loss (Conditional VaR) are measures used to understand the potential for loss in investments, but they approach this understanding in slightly different ways.

Value at Risk (VaR)

When we say “Value at Risk (95% confidence): £-1,926.81”, it means that based on past performance and given current conditions, there’s a 95% chance that your investment will not lose more than £1,926.81 over the specified time period. In simpler terms, it’s like saying, “We’re pretty sure (95% sure) that the worst you’ll do, under most circumstances, is lose £1,926.81.”

Expected Tail Loss (Conditional VaR)

On the other hand, “Expected Tail Loss (Conditional VaR): £-1,301.08” takes a look at those really bad days that fall outside of the 95% confidence level we just talked about. It’s asking, “If things do go worse than we expected and we’re in that unlucky 5%, how bad do we expect it to get, on average?” This figure tells us that, on average, in those worst-case scenarios, you might expect to lose about £1,301.08.

Putting It Together

Both these metrics give you a way to think about risk in familiar monetary terms, which can be very helpful. VaR gives you a threshold, saying “it’s unlikely to get worse than this,” while Expected Tail Loss tells you, “but if it does get worse, here’s what you might expect.”

It’s like planning a picnic and saying, “There’s a 95% chance it won’t rain more than a light drizzle, but if we’re really unlucky and it pours, we expect it to be a moderate rain, not a torrential downpour.” It helps you prepare not just for the likely outcomes, but also understand and plan for the worst-case scenarios, even if they are less likely to occur.

Let’s continue…

num_success = np.sum(final_investment_values >= initial_investment * (1 + desired_return))
probability_of_success = num_success / num_simulations

print(f"Probability of achieving at least a {desired_return*100}% return: {probability_of_success*100:.2f}%")
Screenshot by Author

I also made an adjustment to my matplotlib graph code to show a histogram instead.

  plt.figure(figsize=(10, 6))
plt.hist(final_investment_values, bins=50, alpha=0.75)
plt.axvline(
initial_investment * (1 + desired_return),
color="r",
linestyle="dashed",
linewidth=2,
)
plt.axvline(initial_investment - var, color="g", linestyle="dashed", linewidth=2)
plt.title("Distribution of Final Investment Values")
plt.xlabel("Final Investment Value")
plt.ylabel("Frequency")
plt.show()
Screenshot by Author

It’s important to understand what the red and green dotted lines mean above.

Dotted Red Line

  • Purpose: This line indicates the investment value that corresponds to the desired return.
  • Calculation: It’s calculated as initial_investment * (1 + desired_return). Essentially, if you started with a certain amount of money (initial_investment), this line shows where you'd end up if your investment grew by the percentage of your desired return (desired_return).
  • Interpretation: The red line answers the question, “Where do I hope my investment will end up, at a minimum?” It represents my goal or target for the investment. When looking at the histogram, any outcomes (or bars in the histogram) to the right of this line mean I’ve met or exceeded my desired return. Conversely, outcomes to the left indicate falling short of my goal.

Dotted Green Line

  • Purpose: This line marks the Value at Risk (VaR) at the specified confidence level (in this case, 95%).
  • Calculation: It’s shown as initial_investment - var. var here represents the pound amount I stand to lose with 95% confidence, meaning there's only a 5% chance my losses would exceed this amount.
  • Interpretation: The green line provides a visual cue for risk assessment. It tells me, “Given the risks I’m aware of, this is how much I could lose with fairly high confidence.” In the context of the histogram, it gives me a sense of how much of my investment’s potential outcomes involve losing more than what I’m relatively comfortable with. If many outcomes fall to the left of this line, my investment might be riskier than I am happy to accept.

When looking at the histogram:

  • Areas to the right of the red line indicate successful outcomes where my investment’s final value meets or exceeds my target return.
  • The area to the left of the green line highlights the portion of outcomes where losses exceed what I would expect 95% of the time, indicating the risk of worse-than-expected outcomes.

Together, these lines help visually contextualise the balance between the potential for profit (red line) and the risk of loss (green line) within the simulation results.

Hope you have found this useful…

If you would like my code to try out on different markets, here it is…

import numpy as np
import matplotlib.pyplot as plt
from eodhd import APIClient
import config as cfg

api = APIClient(cfg.API_KEY)


def get_ohlc_data():
df = api.get_historical_data("GSPC.INDX", "d", results=365)
return df


if __name__ == "__main__":
df = get_ohlc_data()

# Calculate daily returns
daily_returns = df["adjusted_close"].pct_change().dropna()

# Simulation parameters
initial_investment = 10000 # Initial investment amount
num_simulations = 1000 # Number of simulations
forecast_days = 365 # Investment horizon in days
desired_return = 0.10 # Desired return (10%)

# Calculate the average daily return
average_daily_return = daily_returns.mean()

# Calculate volatility as the standard deviation of daily returns
volatility = daily_returns.std()

print(f"Average Daily Return: {average_daily_return}")
print(f"Volatility: {volatility}")

# Simulating future returns
simulated_end_returns = np.zeros(num_simulations)
for i in range(num_simulations):
random_returns = np.random.normal(
average_daily_return, volatility, forecast_days
)
cumulative_return = np.prod(1 + random_returns)
simulated_end_returns[i] = initial_investment * cumulative_return

# Calculate the final investment values
final_investment_values = simulated_end_returns

# Calculate Value at Risk (VaR) and Expected Tail Loss (Conditional VaR)
confidence_level = 0.95
sorted_returns = np.sort(final_investment_values)
index_at_var = int((1 - confidence_level) * num_simulations)
var = initial_investment - sorted_returns[index_at_var]
conditional_var = initial_investment - sorted_returns[:index_at_var].mean()

print(f"Value at Risk (95% confidence): £{var:,.2f}")
print(f"Expected Tail Loss (Conditional VaR): £{conditional_var:,.2f}")

num_success = np.sum(
final_investment_values >= initial_investment * (1 + desired_return)
)
probability_of_success = num_success / num_simulations

print(
f"Probability of achieving at least a {desired_return*100}% return: {probability_of_success*100:.2f}%"
)

plt.figure(figsize=(10, 6))
plt.hist(final_investment_values, bins=50, alpha=0.75)
plt.axvline(
initial_investment * (1 + desired_return),
color="r",
linestyle="dashed",
linewidth=2,
)
plt.axvline(initial_investment - var, color="g", linestyle="dashed", linewidth=2)
plt.title("Distribution of Final Investment Values")
plt.xlabel("Final Investment Value")
plt.ylabel("Frequency")
plt.show()

"""
# Plotting the results
plt.figure(figsize=(10, 6))
plt.plot(simulations.T, color="blue", alpha=0.025)
plt.title("Monte Carlo Simulation of Future Prices")
plt.xlabel("Day")
plt.ylabel("Price")
plt.show()
"""

I hope you found this article interesting and useful. If you would like to be kept informed, please don’t forget to follow me and sign up to my email notifications.

If you liked this article, I recommend checking out EODHD APIs on Medium. They have some interesting articles.

Michael Whittle

--

--

Solution Architect — CCIE R&S #24223 | Full-Stack / Blockchain / Web3 Developer | Security Specialist | PyCryptoBot Creator