Monte Carlo Pricing in C++
9.3 Generating random numbers for Monte Carlo
As discussed briefly above, conventional computers cannot generate true random numbers, they can only generate pseudo random numbers.
Exercise 7.7.5 was to write a function to generate uniformly distributed random numbers using the rand function that is built into C++. You should have written some code that looks like this:
vector < double >␣r a n d u n i f o r m O l d (␣int␣n␣)␣{ vector < double >␣ret ( n ,␣0 . 0 ) ;
for␣( int␣i =0;␣i < n ;␣i ++)␣{
int␣r a n d I n t␣=␣r a n d ();
ret [ i ]␣=␣( r a n d I n t␣+␣0 . 5 ) / ( R A N D _ M A X + 1 . 0 ) ; }
r e t u r n␣ret ; }
We’ve called this randuniformOld because it uses the old C random number generator rand. This isn’t a good choice for Monte Carlo algorithms because the sequence of pseudo random numbers it generates start to repeat themselves rather quickly.
Fortunately, experts in generating random numbers on computers have produced much better algorithms for generating pseudo random numbers. The Mersenne Twister algorithm is a commonly used random number generator for Monte Carlo simulations which takes advantage of the properties of so-called Mersenne primes to generate pseudo random numbers. The details are not important for the purposes of this book. The C++ class mt19337 allows you to use the Mersenne Twister algorithm without much more difficulty than using the old rand function. Admittedly the class name mt19337 is substantially more intimidating than the old function name rand but that is the only big difference. The name comes from the fact that it takes a whopping 219337− 1 calls to the random number generator before the sequence of random numbers it generates repeats.
The code to generate random numbers using the mt19337 class is shown below. It uses several C++ features we have not seen before. First we create a global variable of type mt19337 called mersenneTwister.
s t a t i c␣m t 1 9 9 3 7␣m e r s e n n e T w i s t e r ;
The mt19937 class is a standard class in modern versions of C++. To use it one has to #include␣<random>.
The object mersenneTwister is our random number generator. We have rewritten the randuniform function in matlib.cpp so that it uses this random number generator.
vector < double >␣r a n d u n i f o r m (␣int␣n␣)␣{
To generate a random integer we write mersenneTwister(). This call re-turns a random integer in the range mersenneTwister.min()=0 to mersenne-Twister.max(). We’ve tried to ensure that the values 0 and 1 are never actu-ally achieved, which is why we have added in the +0.5 and +1.0. However, the code is really not very different from the code using the old random number generator.
One new feature is that the class mt19337 has overloaded the meaning of (). When we write mersenneTwister() it looks as though we are calling a function called mersenneTwister. But mersenneTwister is an object and not a function, so that can’t be right. What is happening is that the mt19337 has redefined what parentheses mean, so that mersenneTwister() means “gener-ate a random number”.
You can overload the meaning of most symbols in C++ and this is dis-cussed in detail in Chapter 16. For example, we will show how you can overload + and * to allow you to perform matrix multiplication.
It is very common in C++ to overload the meaning of () so that your objects can be used as though they were functions. This is called writing a functor and is discussed in Chapter 19.
We also need a function to reset the state of the random number generator.
The code is given below. We simply call seed and pass in the default value for the seed.
v o i d␣rng (␣c o n s t␣s t r i n g &␣d e s c r i p t i o n␣)␣{
A S S E R T (␣d e s c r i p t i o n == " d e f a u l t "␣);
m e r s e n n e T w i s t e r . s e e d ( m t 1 9 9 3 7 :: d e f a u l t _ s e e d );
}
A new feature here is that you can associate data with an entire class. The default value for the seed for the Mersenne Twister algorithm is a constant and so it doesn’t make sense to give every instance of the mt19937 class its own copy of the default value. That would be a waste of computer memory. So instead we associate the default value with the entire class mt19337. To access a member variable of a class you use the :: notation as shown above. We’re accessing the variable default_seed which is defined on the class mt19937.
We will discuss this idea further in Section 12.8.
The function rng above is designed to work in a similar way as the equiva-lent function in MATLAB. In general we have tried to make our library matlib
very similar to the MATLAB library. This is intended to make it easier for users familiar with MATLAB to work with our library.
Note that C++ already contains built-in classes to help with generating random numbers with a given distribution. It obviously makes more sense in practice to use the built-in functions rather than to develop your own. We have written our own randn function primarily for educational purposes. To find out about the built-in methods for generating random numbers, see the documentation for the the standard library <random>.
Exercises
9.3.1. Add a new function to MonteCarloPricer to price a PutOption. What aspects of your answer do you find unsatisfactory?
9.3.2. What distribution of prices do you expect at time T according to the Black–Scholes model? Use this to run both visual and automated tests on our generation of stock price paths.
9.3.3. A continuous-time knock-out call option with strike K, barrier B, and maturity T is an option which pays off:
(max{ST − K, 0} if St< B for all t ∈ [0, T ]
0 otherwise
In other words, it has the same payoff as a call option with strike K unless the stock price hits the barrier level B before maturity, in which case it pays zero (in which one says it has knocked out).
A more practical contract is a discrete-time knock-out call option. This is essentially the same as the continuous-time version, except that you only test whether the stock price is below the barrier at some fixed time points.
Write a class UpAndOutOption that represents such an option. Give it a func-tion computePayoff that takes a vector of stock prices taken at fixed time points and returns the payoff of the corresponding discrete-time knock-out option.
Add a new function to MonteCarloPricer that prices discrete-time knock-out options by simulating price paths of length nSteps and computing the payoff for these steps. By taking a large number of steps, the same function can be used to price continuous-time knock-out options.
9.3.4. Write some tests for your answer to the previous question. The chal-lenge is to think of good tests.
9.3.5. To compute the delta, ∆, of an option by Monte Carlo, you can use the following algorithm.
• Choose a small value for h, say h = S0× 10−6.
• Generate N stock price paths.
• Use the Monte Carlo method to compute the price of the option when the initial stock price is taken to be S0+ h.
• Use the Monte Carlo method with the same price paths to compute the price of the option when the initial stock price is taken to be S0− h.
• Estimate the delta using the central difference estimate for the derivative f0(x) ≈ f (x + h) − f (x − h)
2h .
Compute the delta of a call option using this algorithm and write a test for your calculation. You will need to modify the class BlackScholesModel a little to implement this.
Explain why it is essential to use the same random numbers in the calculation of the stock prices.
9.3.6. You can use the central limit theorem to estimate the error in our Monte Carlo estimate. In the Monte Carlo method we have generated a sample of N discounted payoffs and taken the mean to compute an estimate for the true expectation. By the central limit theorem, this sample mean is normally distributed about the true expectation with standard deviation sN−1.2where s is the standard deviation of the discounted payoff. We can estimate s by taking the standard deviation of our sample. Use this to compute a 95%
confidence interval for the Monte Carlo price.
9.3.7. There are numerous variance reduction techniques that can be used to improve Monte Carlo calculations. One approach is called antithetic sam-pling. We first simulate N stock price paths using our Q-measure model Equa-tion (A.4). We recall the formula
log(St+δt) = log(St) +
We then simulate N further stock price paths using the same random values
tbut now using the formula log(St+δt) = log(St) +
We can compute an estimate for the risk-neutral price by computing the dis-counted average payoff for all 2N paths. It turns out that for many options this gives a more accurate answer than one obtains by simulating 2N independent stock paths. Implement antithetic sampling to price a call option.
You should consult the literature on Monte Carlo methods for more informa-tion on this and other variance reducinforma-tion techniques.
9.4 Summary
Table 9.1 summarises all the files you will find in FMLib9. It states which files you are meant to understand completely and which one you should simply use without understanding.
Financial and mathematical functionality:
matlib Basic maths functions
BlackScholesModel Represents the Black–Scholes Model CallOption Represents a call option contract PutOption Represents a put option contract MonteCarloPricer Prices options by Monte Carlo Non-financial functionality:
LineChart Plots line charts Histogram Plots histograms PieChart Plots pie charts
geometry Some elementary mathematical examples Code you can use, but don’t fully understand:
testing Macros to make testing less boring TABLE 9.1: Summary of the files found in FMLib9
Consider for a moment what we have achieved. We have demonstrated that by writing code using a number of small classes and functions you can build something very complex out of very little. We have also shown how easy it is to automatically test a large project.
Chapter 10
Interfaces
To complete Exercise 9.3.1 to price a PutOption in the last chapter you should have written a function that was simply a copy of the code for a CallOption except that the type of the option has changed. This violates the “Once and Only Once” principle (see Section 3.1).
In this chapter we will see how to write a function that can price an infinite number of option contracts by Monte Carlo. It will work equally well with put options, call options, digital put options, digital call options, and even options with payoff functions we’ve not thought of yet. The possibilities are endless.