Basic Finance Monte Carlo Simulation in R

Contributor Image
Written By
Contributor Image
Written By
Dan Buckley
Dan Buckley is an US-based trader, consultant, and part-time writer with a background in macroeconomics and mathematical finance. He trades and writes about a variety of asset classes, including equities, fixed income, commodities, currencies, and interest rates. As a writer, his goal is to explain trading and finance concepts in levels of detail that could appeal to a range of audiences, from novice traders to those with more experienced backgrounds.
Updated

Monte Carlo simulation is common when doing financial forecasts in order to determine how profitable the return on a certain asset of a given period of time might be. Monte Carlo simulation also allows us to take the sample size that was generated and calculate a couple of useful things.

For one, we can ascertain the expected return. This might be obvious from the average return, where data will normally regularly be available based on historical performance.

But in cases of high volatility, even after maxing out the number of simulations R is capable of handling (10,000), we could get some figure not situated around the mean. We will do one such example in this article.

And second, we can calculate the percent of the time we can expect to be profitable. We can do this through the pnorm() command in R, which we covered a bit more in a previous article and we will get more into that as we dive into our model.

We’ve designed the following fairly basic Monte Carlo model that’s made to emulate a financial security, such as a stock, where we are given two basic properties – its historical return within a particular period of time and its volatility.

This can be modeled very basically by the formula:

Return = µ*∆t + σ*r*√(Δt)

  • µ = expected annual return
  • ∆t = time period (in years)
  • σ = annualized volatility (standard deviation)
  • r = random input probability variable between 0 and 1

Inputting our random input variable is necessary in order to do this simulation so we have varied output. Otherwise, our simulations would be the same throughout.

Given this is made to model a real-world situation where there is inherent unknown in the outcome, we run this simulation to get a feel for what is likely to occur based on inputs obtained from a hypothetical dataset.

In this model, we will calculate monthly returns, so our ∆t will be 1/12 to represent 1/12th of one year.

Our µ will be 3 percent per year and our annualized volatility, σ, will be 25 percent. With a high volatility, this makes our Monte Carlo simulation interesting.

Normally with 10,000 iterations (the maximum allowed in R), we should obtain a pretty good understanding of what our results may be typically.

But sometimes if the volatility is high enough, we can have swings that can make our prediction less secure, and may require additional simulations with the maximum number of iterations to obtain a closer look.

Let’s look at the model that we’ve set up:

#Monte Carlo (Return and Volatility) n <- 10000 #set.seed(50) r <- rnorm(n) m <- 0.03 s <- 0.25 delta_t <- 1/12 Monthly_Returns <- m*delta_t + s*r*sqrt(delta_t) outcome <- c(mean(Monthly_Returns) * 12, sd(Monthly_Returns) * sqrt(12)) names(outcome) <- c("Average Annual Return", "Volatility") outcome hist(Monthly_Returns, breaks = 100, col = "blue") pnorm(0, 0.03, 0.25)

We will go through each line of the code step-by-step:

n <-10000: This represents our number of simulations.

#set.seed(50): We’ve placed this with a # sound before it to make it optional for anyone wanting to simulate this.

The set.seed function simply allows you to recover a simulation should you want to obtain values from this in the future.

The set.seed command always is saved by placing a number inside the parenthetical (e.g., set.seed(50), set.seed(24532), etc.).

r <- rnorm(n): This is our random number generator function that gives us a value between 0 and 1 as default settings.

It generates a different probability to vary how much our function deviates from the mean.

If the rnorm() command generates numbers closer to 0, then the random value that’s generated will be closer to the mean; if closer to 1, then it will be further from the mean.

m <- 0.03: This is our annualized return, µ, at 3 percent.

s <- 0.25: This is our annualized volatility, σ, at 25 percent.

delta_t <- 1/12: This is our ∆t, in terms of years. Since we are doing monthly returns and a month is one-twelfth of one year, we represent this as 1/12.

Monthly_Returns <- m*delta_t + s*r*sqrt(delta_t): Here were program in our formula, as mentioned above. We label it as “Monthly_Returns” to use for future reference.

Now that we have that programmed, we are ready to generate some statistics from this.

outcome <- c(mean(Monthly_Returns) * 12, sd(Monthly_Returns) * sqrt(12)): Now we program our formula that we just wrote into forming descriptive statistics that we can access later when using the operative name that we choose (“outcome” in this case).

We insert 12 into our formula in order to make our returns in terms of expected annualized return since we are using hypothetical monthly data.

(We chose monthly data in this case just to demonstrate how this might be manipulated in a model should you not have convenient access to yearly data.)

names(outcome) <- c(“Annual Average Return”, “Volatility”): Here we are creating names for the inputs in the parenthetical in the line of code preceding this one.

outcome: This gets statistics to display regarding the outcome of the model, expressed in our named variables “Annual Average Return” (from mean input) and “Volatility” (from standard deviation input).

Now if you wish to graph the results of this (recommended), the most convenient way is to form a histogram of the results.

hist(Monthly_Returns, breaks = 100, col = “light blue”: The “breaks” input literally breaks the results into 100 chunks.

It’s standard practice to break a histogram up into chunks by a factor of 1/100 of the total number of observation/data points.

But it’s up to you. You can remove this and simply go with the default. You can also use “col” to change the color of the bars in the histogram for a more visually appealing graphic. Black is the default histogram color.

With pnorm(), you can understand what percentage of the time you will be profitable:

pnorm(0, 0.03, 0.25): We will go in order in terms of what each of the numbers in the parenthetical functions as.

“0” denotes the fraction of the distribution we want to be left of zero. In other words, what percentage of the time does this financial security lose money. 0.03 is our annualized return (mean). 0.25 is our annualized volatility (standard deviation).

However, these are the theoretical values based on our inputs. To find our actual values based on our simulation results, we will have to use the values derived. We will do this below.

Each part of this code must be submitted line by line. If you copy-paste and hit the “Enter” key on your keyboard it will not work.

We would recommend copy-pasting this code as a script and then running it line by line. And each time you run the “outcome” line, it will generate a new simulation.

 

Results

Now we can get to the fun part, which is actually simulating our results. It will be different for each one you complete.

Average Annual Return: 0.02376

Volatility: 0.2528

Based on this simulation, the stock underperformed, earning (on average) 2.376 percent for the year (3 percent historically), and saw greater than average volatility at 25.28 percent (instead of 25 percent).

As we can see, even despite the large number of simulations here (10,000 months simulated), we did not approach our theoretical value due to the high volatility.

With pnorm(), we can simulate what percentage of those months we lost money.

pnorm(0, 0.02376, 0.2528)

This is equal to 0.4625596.

This means we lost money approximately 46.3 percent of the months. But we are making money most months. If we wanted to reverse this and find out the percentage of the months we are making money we would do:

pnorm(0, 0.02376, 0.2528)

If we wanted to find out the number of months we are more than a 10 percent annualized return, we would write:

1-pnorm(0.10, 0.02376, 0.2528)

And we obtain a value of 0.3814853.

Remember that our average yield is 2.376 percent annual return per month. So having 38.1 percent of our months return more than 10% in annualized return is great.

Nevertheless, since volatility is a double-edged sword, we are probably having some really bad months with this stock where is loses more than 20 percent annualized. To determine this, we write:

pnorm(-0.20, 0.2376, 0.2528)

(Note the negative sign before -0.20 to denote a loss of 20 percent)

From this, we obtain a value of 0.04172475.

So we lose a decent amount of value on this stock in 4.2 percent of the months, or one month every two years.

For more conservative investors who want more of a low beta stock, this wouldn’t be a very good choice, although the risk could be more worth it when selling options.

However, for the annualized return, it isn’t a great choice due to the high amount of volatility.

Conclusion

This model is fairly simple, but in other articles, we discussed more complex models in quantitative finance to represent more complex financial situations through topics such as various options pricing models, Brownian motion, and other matters involving risk and uncertainty.