Monte Carlo Pricing in C++
10.6 An interface for integration
The difficulty with interfaces is not in the syntax or in the concept itself, but in spotting when you can use an interface to solve a problem. For exam-ple, we can use interfaces to perform numerical integration, but perhaps the connection between integration and interfaces is not obvious.
If you attempt to write numerical integration code in C++ you will hit a very similar problem to the one we experienced with our Monte Carlo pricer.
For example, you should find it easy to write a function that integrates exp(−x2) from a to b using the rectangle rule. But how could you write a function that can integrate any real valued function from a to b using the rectangle rule? Interfaces solve this problem.
Let us proceed in the same way as the last example by writing our fantasy code. Here it is:
d o u b l e␣i n t e g r a l (␣R e a l F u n c t i o n &␣f , d o u b l e␣a ,
d o u b l e␣b ,
int␣n P o i n t s␣)␣{
d o u b l e␣h␣=␣( b - a )/ n P o i n t s ; d o u b l e␣x␣=␣a␣+␣0 . 5 * h ; d o u b l e␣t o t a l␣=␣0 . 0 ;
for␣( int␣i =0;␣i < n P o i n t s ;␣i ++)␣{ d o u b l e␣y␣=␣f . e v a l u a t e ( x );
t o t a l += y ; x += h ; }
r e t u r n␣h * t o t a l ; }
Our fantasy code involves a type called RealFunction. This name de-scribes the kinds of object we can integrate. We need to write the formal C++ interface definition.
c l a s s␣R e a l F u n c t i o n␣{
p u b l i c :
/*␣ A␣v i r t u a l␣d e s t r u c t o r␣*/
v i r t u a l␣~ R e a l F u n c t i o n ()␣{};
/*␣ T h i s␣m e t h o d␣is␣a b s t r a c t ,␣t h e r e␣is
no␣d e f i n i t i o n␣*/
v i r t u a l␣d o u b l e␣e v a l u a t e (␣d o u b l e␣x␣)␣=␣0;
};
This code contains the necessary virtual and =0 magical incantations, but more importantly it states that a RealFunction has an evaluate method.
Our integrate function can integrate any object that provides an evaluate method. Here is an example of a specific implementation of our fantasy interface:
c l a s s␣S i n F u n c t i o n␣:␣p u b l i c␣R e a l F u n c t i o n␣{
d o u b l e␣e v a l u a t e (␣d o u b l e␣x␣);
};
d o u b l e␣S i n F u n c t i o n :: e v a l u a t e (␣d o u b l e␣x␣)␣{ r e t u r n␣sin ( x );
}
The class SinFunction represents the mathematical sin function through its evaluate method. We can integrate it using our integrate function because our integrate function uses nothing other than the evaluate method.
Combining our integrate function, the RealFunction interface and its implementation, we can now write the code to integrate sin. Here is a test that everything fits together:
s t a t i c␣v o i d␣t e s t I n t e g r a l ()␣{ S i n F u n c t i o n␣i n t e g r a n d ;
d o u b l e␣a c t u a l␣=␣i n t e g r a l ( i n t e g r a n d ,␣1 ,␣3 ,␣1 0 0 0␣);
d o u b l e␣e x p e c t e d␣=␣- cos ( 3 . 0 ) + cos ( 1 . 0 ) ;
A S S E R T _ A P P R O X _ E Q U A L (␣actual ,␣e x p e c t e d ,␣0 . 0 0 0 0 0 1 ) ; }
This code is included in FMLib10. You should check that you can compile and run the test.
Every time we want to integrate a real valued function, we will need to write a new implementation class like SinFunction. We don’t want to create a new cpp file and a new h file every time we want to integrate a function, so C++ gives us a shorthand notation that allows us to define a class inside a function. Here is an alternative way of testing the integral of sin using a local class.
s t a t i c␣v o i d␣t e s t I n t e g r a l V e r s i o n 2 ()␣{
c l a s s␣Sin␣:␣p u b l i c␣R e a l F u n c t i o n␣{ p u b l i c :
d o u b l e␣e v a l u a t e (␣d o u b l e␣x␣)␣{ r e t u r n␣sin ( x );
} };
Sin␣i n t e g r a n d ;
d o u b l e␣a c t u a l␣=␣i n t e g r a l ( i n t e g r a n d ,␣1 ,␣3 ,␣1 0 0 0␣);
d o u b l e␣e x p e c t e d␣=␣- cos ( 3 . 0 ) + cos ( 1 . 0 ) ;
A S S E R T _ A P P R O X _ E Q U A L ( actual ,␣e x p e c t e d ,␣0 . 0 0 0 0 0 1 ) ; }
Local classes defined in this way are “throwaway”. They can only be used inside that one function. The advantage of local classes is that the class is quicker to write. Notice that in the local class declaration we actually provide the definition for all the functions as well as the declaration. Note also that this definition doesn’t contain the usual SinFunction:: qualification you need for a standalone class.
Here is a more elaborate example where the local class uses a member variable to access the parameters passed into the containing function.
s t a t i c␣d o u b l e␣i n t e g r a t e P a y o f f ( d o u b l e␣a ,
d o u b l e␣b ,
c o n s t␣P a t h I n d e p e n d e n t O p t i o n &␣o p t i o n )␣{
c l a s s␣P a y o f f F u n c t i o n␣:␣p u b l i c␣R e a l F u n c t i o n␣{
p u b l i c :
/*␣ M e m b e r␣v a r i a b l e␣*/
c o n s t␣P a t h I n d e p e n d e n t O p t i o n &␣o p t i o n ; /*␣ C o n s t r u c t o r␣*/
P a y o f f F u n c t i o n (
c o n s t␣P a t h I n d e p e n d e n t O p t i o n &␣o p t i o n ) :␣o p t i o n (␣o p t i o n␣)␣{
} /* *
*␣ O v e r r i d i n g␣f u n c t i o n
*/
d o u b l e␣e v a l u a t e ( d o u b l e␣x )␣{ r e t u r n␣o p t i o n . p a y o f f ( x );
} };
P a y o f f F u n c t i o n␣i n t e g r a n d ( o p t i o n );
r e t u r n␣i n t e g r a l ( i n t e g r a n d ,␣a ,␣b ,␣1 0 0 0 ) ; }
This function above integrates the payoff function of an option between the range a and b. We have to store the member variable using a reference because you cannot store polymorphic variables by value. Storing data in member variables by reference can be dangerous, because the data referred to may be deleted before the object holding the reference is deleted. This would cause the program to behave badly and perhaps crash. In this case, the code will delete the variable integrand from memory the moment integratePayoff returns.
So there should be no danger of option being deleted before integrand is deleted.
There is an neater notation called lambda functions which can be used to create local classes. We will revisit the example of writing an integrator in Chapter 19 and show how the use of lambda classes simplifies the code.
We should also add that the rectangle rule is not the best integration method to use. You probably already know of Simpson’s rule. An even more interesting technique for one-dimensional numerical integration is called Gaus-sian quadrature. Numerical libraries such as the GNU scientific library contain sophisticated integration routines that you should use in practice.
Exercises
10.6.1. Write classes DigitalCallOption and DigitalPutOption. Price them using the MonteCarloPricer. See Appendix A if you do not recall what a digital option is.
10.6.2. Write a class NormalPDF and compute its integral from −1.96 to 1.96.
10.6.3. Write a function integralToInfinity that uses integration by sub-stitution to transform an infinite integral to one the function integral can compute.
10.6.4. Test the normcdf function against numerical integration of the pdf of the normal distribution.
10.6.5. Write a function differentiateNumerically that can differentiate any RealFunction.
10.6.6. Write an interface Cipher and provide at least two implementations of your choice. Write a single function void␣testCipher(Cipher&␣toTest) that can test any Cipher works correctly.
10.6.7. Draw a UML diagram for your Cipher classes.
10.6.8. Write a class RectangleRulePricer which can price a Path-DependentOption using the numerical integration routines written in the ear-lier exercises. Use Equation (A.3) to compute the probability density function
for the log of the stock price. Check your solution works with a CallOption.
This gives a numerical test of the derivation of Equation (A.6).
10.6.9. Design an interface called ContinuousTimeOption. It should have a function payoff which takes a vector of stock prices and a vector of times at which the price occurred. This should return the payoff that would have occurred if the stock price had varied linearly between the time points. (Ob-viously we can’t actually supply the stock price at an infinite number of points). The ContinuousTimeOption should also have a function called get-Maturity().
Write an implementation of ContinuousTimeOption called KnockOutCall-Option. Add a function to MonteCarloPricer which can approximate the price of any ContinuousTimeOption given a finite number of time steps.
10.6.10. Show that you could use if statement to write an integrate func-tion which can numerically integrate any of the following specific funcfunc-tions (but no others):
• sin(x)
• cos(x)
• exp(x)
Explain why this is not a good design. Use this to explain why using an if statement to decide how to price puts and calls differently is not as powerful as using an interface.
10.7 Summary
• By defining interfaces we can write code that will work with inputs we haven’t thought of yet.
• This is essential to writing real code for the financial industry where new products are invented every day.
• Interfaces are common in everyday life. Cars, doors, guitars, and plug sockets are all familiar examples of things that are easy to use because they implement a well-known interface.
• Learning how to use interfaces is the most important skill in object-oriented design.