C++ design patterns Best Practices

c++ design patterns with practical examples, c++ design patterns interview questions for experienced, c++ design patterns interview questions pdf free download,
Dr.DavisHardy Profile Pic
Published Date:22-07-2017
Your Website URL(Optional)
1 A simple Monte Carlo model 1.1 Introduction In the first part of this book, we shall study the pricing of derivatives using Monte Carlo simulation. We do this not to study the intricacies of Monte Carlo but because it provides many convenient examples of concepts that can be abstracted. We pro- ceed by example, that is we first give a simple program, discuss its good points, its shortcomings, various ways round them and then move on to a new example. We carry out this procedure repeatedly and eventually end up with a fancy program. We begin with a routine to price vanilla call options by Monte Carlo. 1.2 The theory We commence by discussing the theory. The model for stock price evolution is dS = µ S dt + σ S dW , (1.1) t t t t and a riskless bond, B, grows at a continuously compounding rate r. The Black– Scholes pricing theory then tells us that the price of a vanilla option, with expiry T and pay-off f , is equal to −rT e E( f (S )), T where the expectation is taken under the associated risk-neutral process, dS = rS dt + σ S dW . (1.2) t t t t We solve equation (1.2) by passing to the log and using Ito’s lemma; we compute   1 2 d log S = r − σ dt + σdW . (1.3) t t 2 As this process is constant-coefficient, it has the solution   1 2 log S = log S + r − σ t + σ W . (1.4) t 0 t 2 12 A simple Monte Carlo model Since W is a Brownian motion, W is distributed as a Gaussian with mean zero t T and variance T , so we can write √ W = TN(0, 1), (1.5) T and hence   √ 1 2 log S = log S + r − σ T + σ TN(0, 1), (1.6) T 0 2 or equivalently, √ 1 2 (r− σ )T+σ TN(0,1) 2 S = S e . (1.7) T 0 The price of a vanilla option is therefore equal to √    1 2 −rT (r− σ )T+σ TN(0,1) 2 e E f S e . 0 The objective of our Monte Carlo simulation is to approximate this expectation by using the law of large numbers, which tells us that if Y are a sequence of identically j distributed independent random variables, then with probability 1 the sequence N  1 Y j N j=1 converges toE(Y ). 1 So the algorithm to price a call option by Monte Carlo is clear. We draw a random variable, x, from an N(0, 1) distribution and compute √   1 2 (r− σ )T+σ Tx 2 f S e , 0 where f (S) = (S − K) . We do this many times and take the average. We then + −rT multiply this average by e and we are done. 1.3 A simple implementation of a Monte Carlo call option pricer A first implementation is given in the programSimpleMCMain1.cpp. Listing 1.1 (SimpleMCMain1.cpp) // requires Random1.cpp include Random1.h include iostream include cmath using namespace std;1.3 A simple implementation of a Monte Carlo call option pricer 3 double SimpleMonteCarlo1(double Expiry, double Strike, double Spot, double Vol, double r, unsigned long NumberOfPaths) double variance = VolVolExpiry; double rootVariance = sqrt(variance); double itoCorrection = -0.5variance; double movedSpot = Spotexp(rExpiry +itoCorrection); double thisSpot; double runningSum=0; for (unsigned long i=0; i NumberOfPaths; i++) double thisGaussian = GetOneGaussianByBoxMuller(); thisSpot = movedSpotexp( rootVariancethisGaussian); double thisPayoff = thisSpot - Strike; thisPayoff = thisPayoff 0 ? thisPayoff : 0; runningSum += thisPayoff; double mean = runningSum / NumberOfPaths; mean = exp(-rExpiry); return mean; int main() double Expiry; double Strike; double Spot; double Vol; double r; unsigned long NumberOfPaths; cout "\nEnter expiry\n"; cin Expiry;4 A simple Monte Carlo model cout "\nEnter strike\n"; cin Strike; cout "\nEnter spot\n"; cin Spot; cout "\nEnter vol\n"; cin Vol; cout "\nr\n"; cin r; cout "\nNumber of paths\n"; cin NumberOfPaths; double result = SimpleMonteCarlo1(Expiry, Strike, Spot, Vol, r, NumberOfPaths); cout "the price is " result "\n"; double tmp; cin tmp; return 0; Our program uses the auxiliary filesRandom1.h andRandom1.cpp. Listing 1.2 (Random1.h) ifndef RANDOM1_H define RANDOM1_H double GetOneGaussianBySummation(); double GetOneGaussianByBoxMuller(); endif1.3 A simple implementation of a Monte Carlo call option pricer 5 Listing 1.3 (Random1.cpp) include Random1.h include cstdlib include cmath // the basic math functions should be in namespace // std but aren’t in VCPP6 if defined(_MSC_VER) using namespace std; endif double GetOneGaussianBySummation() double result=0; for (unsigned long j=0;j12; j++) result += rand()/static_castdouble(RAND_MAX); result -= 6.0; return result; double GetOneGaussianByBoxMuller() double result; double x; double y; double sizeSquared; do x = 2.0rand()/static_castdouble(RAND_MAX)-1; y = 2.0rand()/static_castdouble(RAND_MAX)-1; sizeSquared = xx + yy; while ( sizeSquared = 1.0);6 A simple Monte Carlo model result = xsqrt(-2log(sizeSquared)/sizeSquared); return result; We first include the header file Random1.h. Note that the program has Random1.h rather than "Random1.h". This means that we have set our com- piler settings to look for header files in the directory where Random1.h is.Inthis case, this is in the directory C/include. (In Visual C++, the directories for in- clude files can be changed via the menus tools, options, directories.) Random1.h tells the main file that the functions double GetOneGaussianBySummation() and double GetOneGaussianByBoxMuller() exist. We include the system file iostream as we want to use cin and cout for the user interface. The system filecmath is included as it contains the basic math- ematical functionsexp andsqrt. We have the command using namespace std because all the standard library commands are contained in the namespace std. If we did not give the using directive, then we would have to prefix all their uses by std::, so then it would bestd::cout rather thancout. The functionSimpleMonteCarlo1 does all the work. It takes in all the standard inputs for the Black–Scholes model, the expiry and strike of the option, and in addition the number of paths to be used in the Monte Carlo. Before starting the Monte Carlo we precompute as much as possible. Thus we compute the variance of the log of the stock over the option’s life, the adjustment 1 2 term − σ T for the drift of the log, and the square root of the variance. Whilst we 2 cannot precompute the final value of spot, we precompute what we can and put it in the variablemovedSpot. We initialize the variable,runningSum, to zero as it will store the sum so far of the option pay-offs at all times. We now loop over all the paths. For each path, we first draw the random number from the N(0, 1) distribution using the Box–Muller algorithm and put it in the variablethisGaussian. The spot at the end of the path is then computed and placed in thisSpot. Note that although our derivation of the SDE involved working with the log of the spot, we have carefully avoided using log in this routine. The reason is that log and exp1.4 Critiquing the simple Monte Carlo routine 7 are slow to compute in comparison to addition and multiplication, we therefore want to make as few calls to them as possible. We then compute the call option’s pay-off by subtracting the strike and taking the maximum with zero. The pay-off is then added to runningSum and the loop continues. Once the loop is complete, we divide by the number of paths to get the expecta- tion. Finally, we discount to get our estimate of the price which we return. Themain program takes in the inputs from the user, calls the Monte Carlo func- tion, and displays the results. It asks for a final input to stop the routine from re- turning before the user has had a chance to read the results. 1.4 Critiquing the simple Monte Carlo routine The routine we have written runs quickly and does what it was intended to do. It is a simple straightforward procedural program that performs as required. However, if we worked with this program we would swiftly run into annoyances. The essence of good coding is reusability. What does this mean? One simple definition is that code is reusable if someone has reused it. Thus reusability is as much a social concept as a technical one. What will make it easy for someone to reuse your code? Ultimately, the important attributes are clarity and elegance of design. If another coder decides that it would take as much effort to recode your routines as to understand them, then he will recode, and his inclination will be to recode in any case, as it is more fun than poring over someone else’s implementation. The second issue of elegance is equally important. If the code is clear but difficult to adapt then another coder will simply abandon it, rather than put lots of effort into forcing it to work in a way that is unnatural for how it was built. The demands of reusability therefore mean we should strive for clarity and ele- gance. In addition, we should keep in mind when considering our original design the possibility that in future our code might need to be extended. We return to our simple Monte Carlo program. Suppose we have a boss and each day he comes by and asks for our program to do something more. If we have designed it well then we will simply have to add features; if we have designed poorly then we will have to rewrite existing code. So what might the evil boss demand? “Do puts as well as calls” “Ican’t see how accurate the price is, put in the standard error.” “The convergence is too slow, put in anti-thetic sampling.” “I want the most accurate price possible by 9am tomorrow so set it running for 14 hours.”8 A simple Monte Carlo model “It’s crucial that the standard error is less than 0.0001, so run it until that’s achieved. We’re in a hurry though so don’t run it any longer than strictly necessary.” “I read about low-discrepancy numbers at the weekend. Just plug them in and see how good they are.” “Apparently, standard error is a poor measure of error for low-discrepancy sim- ulations. Put in a convergence table instead.” “Hmm, I miss the standard error can we see that too.” “We need a digital call pricer now” “What about geometric average Asian calls?” “How about arithmetic average Asian puts?” “Take care of variable parameters for the volatility and interest rates.” “Use the geometric Asian option as a control variate for the arithmetic one.” “These low-discrepancy numbers apparently only work well if you Brownian bridge. Put that in as well.” “Put in a double digital geometric Asian option.” “What about jump risk? Put in a jump-diffusion model.” To adapt the routine as written would require a rewrite to do any of these. We have written the simplest routine we could think of, without considering design issues. This means that each change is not particularly natural and requires extra work. For example, with this style of programming how would we would do the put option? Option one: copy the function, change the name by adding put at the end, and rewrite the two lines where the pay-off is computed. Option two: pass in an extra parameter, possibly as an enum and compute the pay-off via a switch statement in each loop of the Monte Carlo. The problem with the first option is that when we come to the next task, we have to adapt both the functions in the same way and do the same thing twice. If we then need more pay-offs this will rapidly become a maintenance nightmare. The issues with the other option are more subtle. One problem is that a switch statement is an additional overhead so that the routine will now run a little slower. A deeper problem is that when we come to do a different routine which also uses a pay-off, we will have to copy the code from inside the first routine or rewrite it as necessary. This once again becomes a maintenance problem; every time we want to add a new sort of pay-off we would have to go through every place where pay-offs are used and add it on. A C style approach to this problem would be to use a function pointer, we pass a pointer to a function as an argument to the Monte Carlo. The function pointed to is then called via the pointer in each loop to specify the price. Note that the call to the function would have to specify the strike as well as spot since the function1.5 Identifying the classes 9 could not know its value. Note also that if we wanted to do a double-digital option we would have problems as the double digital pays if and only if spot is between two levels, and we only have one argument, the strike, to play with. The C++ approach to this problem is to use a class. The class would encapsulate the behaviour of the pay-off of a vanilla option. A pay-off object would then be passed into the function as an argument and in each loop a method expressing its value would be called to output the price for that pay-off. We look at the imple- mentation of such a class in the next chapter. 1.5 Identifying the classes In the previous section, we saw that the problem of wanting to add different sorts of vanilla options led naturally to the use of a class to encapsulate the notion of a pay-off. In this section, we look at identifying other natural classes which arise from the boss’s demands. Some of the demands were linked to differing forms that the boss wanted the information in. We could therefore abstract this notion by creating a statistics gath- erer class. We also had differing ways of terminating the Monte Carlo. We could termi- nate on time, on standard error or simply after a fixed number of paths. We could abstract this by writing a terminator class. There were many different issues with the method of random number genera- tion. The routine as it stands relies on the inbuilt generator which we do not know much about. We therefore want to be able to use other random number generators. We also want the flexibility of using low-discrepancy numbers which means an- other form of generation. (In addition, Box–Muller does not work well with low- discrepancy numbers so we will need flexibility in the inputs.) Another natural abstraction is therefore a random number generator class. As long as our option is vanilla then specifying its parameters via pay-off and strike is fairly natural and easy; however, it would be neater to have one class that contains both pieces of information. More generally, when we pass to path- dependent exotic options, it becomes natural to have a class that expresses the option’s properties. What would we expect such a class to do? Ultimately, an easy way to decide what the class should and should not know is to think of whether a piece of information would be contained in the term-sheet. Thus the class would know the pay-off of the option. It would know the expiry time. If it was an Asian it would know the averaging dates. It would also know whether the averaging was geometric or arithmetic. It would not know anything about interest rates, nor the value of spot nor the volatility of the spot rate as none these facts are con- tained in the term-sheet. The point here is that by choosing a real-world concept to10 A simple Monte Carlo model encapsulate, it is easy to decide what to include or not to include. It is also easy for another user to understand how you have decided what to include or not to include. What other concepts can we identify? The concept of a variable parameter could be made into a class. The process from which spot is drawn is another concept. The variable interest rates could be encapsulated via a class that expresses the notion of a discount curve. 1.6 What will the classes buy us? Suppose that having identified all these classes, we implement them. What do we gain? The first gain is that because these classes encapsulate natural financial concepts, we will need them when doing other pieces of coding. For example, if we have a class that does yield curves then we will use it time and time again, as to price any derivative using any reasonable method involves knowledge of the discount curve. Not only will we save time on the writing of code but we will also save time on the debugging. A class that has been tested thoroughly once has been tested forever and in addition, any little quirks that evade the testing regime will be found through repeated reuse. The more times and ways something has been reused the fewer the bugs that will be left. So using reusable classes leads to more reliable code as well as saving us coding time. Debugging often takes at least as much time as coding in any case, so saving time on debugging is a big benefit. A second gain is that our code becomes clearer. We have written the code in terms of natural concepts, so another coder can identify the natural concepts and pick up our code much more easily. A third gain is that the classes will allow us to separate interface from implemen- tation. All the user needs to know about a pay-off class or discount curve class are what inputs yield what outputs? How the class works internally does not matter. This has multiple advantages. The first is that the class can be reused without the coder having to study its internal workings. The second advantage is that because the defining characteristic of the class is what it does but not how it does it, we can change how it does it at will. And crucially, we can do this without rewriting the rest of our program. One aspect of this is that we can first quickly write a suboptimal implementation and improve it later at no cost. This allows us to provide enough functionality to test the rest of the code before devoting a lot of time to the class. A third advantage of separating interface from implementation is that we can write multiple classes that implement the same interface and use them without rewriting all the interface routines. This is one of the biggest advantages of object-oriented design.1.8 Key points 11 In the next chapter, we look at some of these concepts in the concrete case of a pay-off class. 1.7 Why object-oriented programming? This is a book about implementing pricing models using object-oriented C++ pro- grams. The reader may ask why this is worth learning. A short answer is that this is the skill you need if you want a job working as a quantitative analyst or quantitative developer. But this begs the question of why this is the required skill. Object-oriented programming has become popular as computer projects have be- come larger and larger. A single project may now involve millions of lines of code. No single programmer will ever be able to hold all of that code in his mind at once. Object-oriented programming provides us with a way of coding that corresponds to natural mental maps. We know what each class of objects does, and more impor- tantly we tightly define how they can interact with each other. This allows a clear map in the coder’s mind of how the code fits together. And equally importantly, this allows easy communication of the code’s structure to other programmers in the team. When the coder needs to focus in on a particular part of the code, he need only look at the interior of the particular object involved and its interface with other ob- jects. As long as the interface is not broken, and the new object lives up to the same responsibilities as the old one then there is no danger of unexpected ramifications (i.e. bugs) in distant parts of the code. Thus object-oriented programming leads to more robust code that is easier for teams to work on. 1.8 Key points In this chapter, we have looked at how to implement a simple Monte Carlo rou- tine on a procedural program. We then criticized it from the point of view of easy extensibility and reuse. • Options can be priced by risk-neutral expectation. • Monte Carlo uses the Law of Large Numbers to approximate this risk-neutral expectation. • Reuse is as much a social issue as a technical one. • Procedural programs can be hard to extend and reuse. • Classes allow us to encapsulate concepts which makes reuse and extensibility a lot easier. • Making classes closely model real-world concepts makes them easier to design and to explain.12 A simple Monte Carlo model • Classes allow us to separate the design of the interface from the coding of the implementation. 1.9 Exercises Exercise 1.1 Modify the program given to price puts. Exercise 1.2 Modify the program given to price double digitals. Exercise 1.3 Change the program so that the user inputs a string which specifies the option pay-off. Exercise 1.4 Identify as many classes as you can in the evil boss’s list of demands.2 Encapsulation 2.1 Implementing the pay-off class In the last chapter, we looked at a simple Monte Carlo pricer and concluded that the program would be improved if we used a class to encapsulate the notion of the pay-off of a vanilla option. In this section, we look at how such a pay-off might be implemented. In the filesPayOff1.h andPayoff1.cpp, we develop such a class. Before looking at the source file, PayOff1.cpp, we examine the more important header file. The header file is much more important because it is all that other files will see, and ideally it is all that the other files need to see. Listing 2.1 (PayOff1.h) ifndef PAYOFF_H define PAYOFF_H class PayOff public: enum OptionType call, put; PayOff(double Strike_, OptionType TheOptionsType_); double operator()(double Spot) const; private: double Strike; OptionType TheOptionsType; ; endif We use an enum to distinguish between different sorts of pay-offs. If we ever want more than put and call, we would add them to the list. We will discuss 1314 Encapsulation more sophisticated approaches in Chapter 3 but this approach is enough to get us started. The constructor PayOff(double Strike_, OptionType TheOptionsType_) takes in the strike of the option and the type of the option pay-off. The main method of the class is double PayOff::operator()(double spot) const The purpose of this method is that given a value of spot, it returns the value of the pay-off. Note the slightly odd syntax: we have overloaded operator(). To call this method for an object thePayoff with spot given by S we would write thePayoff(S). Thus when use the object it appears more like a function than an object; such objects are often called function objects or functors. (Note that the C++ concept of functor is quite different from the pure mathematical one.) We have definedoperator() to beconst. This means that the method does not modify the state of the object. This is as we would expect: computing the pay-off does not change the strike or the type of an option. Suppose we did not specify that operator() was const; what would happen? The same functionality would be provided and the code would still compile. How- ever, if a pay-off object was declaredconst at some point by a user then the com- piler would complain if we tried to call operator(), and give us a complicated message to the effect that we cannot call non const methods of const objects. Thus if we want a method to be usable in const objects we must declare the methodconst. An alternative approach, adopted by some programmers, is not to use const anywhere. The argument goes along the lines of “If we useconst in some places, we must use it everywhere, and all it does is cause trouble and stop me doing what I want so why bother? Life will be much easier if we just do not use it.” Yet I use const as much as possible. The reason is that it is a safety enforce- ment mechanism. Thinking about const forces me to consider the context of an object and whether or not I wish it to change when doing certain things. If I am woolly in my thinking then the compiler will generally squeal when I at- tempt to compile, and thus errors are picked up at compile time instead of at run time. A second benefit is that by giving the compiler the extra information that objects are const, we allow it to make extra optimizations. Code on a good compiler can therefore run faster whenconst is used. Now we are ready forPayOff1.cpp.2.2 Privacy 15 Listing 2.2 (PayOff1.cpp) include PayOff1.h include MinMax.h PayOff::PayOff(double Strike_, OptionType TheOptionsType_) : Strike(Strike_), TheOptionsType(TheOptionsType_) double PayOff::operator ()(double spot) const switch (TheOptionsType) case call : return max(spot-Strike,0.0); case put: return max(Strike-spot,0.0); default: throw("unknown option type found."); 2.2 Privacy We have declared the data in the pay-off class to be private. This means that the data cannot be accessed by code outside the class. The only code that can see, let alone change, their values are the constructor and the method operator(). What does this buy us? After all, for some reason the user might want to know the strike of an option and we have denied him the possibility of finding it out. The reason is that as soon we let the user access the data directly, it is much harder for us to change how the class works. We can think of the class as a contract between coder and user. We, the coder, have provided the user with an interface: if he gives us spot we will give him the value of the pay-off. This is all we have contracted to provide. The user therefore expects and receives precisely that and no more. For example, if the user could see the strike directly and accessed it, and if we changed the class so that the strike was no longer stored directly (which we shall16 Encapsulation do in the next chapter), then we would get compile errors from everywhere the strike was accessed. If the class had been used a lot in many files, in many different projects (which is after all the objective of reuse), then to find every place where strike had been accessed would be a daunting task. In fact, if this were the case we would probably consider finding them all a considerable waste of time, and we would probably give up reforming the internal workings. Thus by making the data private, we can enforce the contract between coder and user in such a way that the contract does not say anything about the inte- rior workings of the class. If for some reason, we wanted the user to be able to know the strike of the pay-off then we could add in an extra method double GetStrike()const. Whilst this would seem to undo all the benefits of using pri- vate data, this is not so since it still allows us the possibility of storing the data in a totally different way. To conclude, by using private data we can rework the interior workings of a class as and when we need to without worrying about the impact on other code. That is, private data helps us to separate interface and implementation. 2.3 Using the pay-off class Having designed a pay-off class, we want to use it in our Monte Carlo routines. We do this in the function SimpleMonteCarlo2 which is declared in SimpleMC.h and defined inSimpleMC.cpp. Listing 2.3 (SimpleMC.h) ifndef SIMPLEMC_H define SIMPLEMC_H include PayOff1.h double SimpleMonteCarlo2(const PayOff& thePayOff, double Expiry, double Spot, double Vol, double r, unsigned long NumberOfPaths); endif Listing 2.4 (SimpleMC.cpp) include SimpleMC.h include Random1.h include cmath2.3 Using the pay-off class 17 // the basic math functions should be in // namespace std but aren’t in VCPP6 if defined(_MSC_VER) using namespace std; endif double SimpleMonteCarlo2(const PayOff& thePayOff, double Expiry, double Spot, double Vol, double r, unsigned long NumberOfPaths) double variance = VolVolExpiry; double rootVariance = sqrt(variance); double itoCorrection = -0.5variance; double movedSpot = Spotexp(rExpiry +itoCorrection); double thisSpot; double runningSum=0; for (unsigned long i=0; i NumberOfPaths; i++) double thisGaussian = GetOneGaussianByBoxMuller(); thisSpot = movedSpotexp( rootVariancethisGaussian); double thisPayOff = thePayOff(thisSpot); runningSum += thisPayOff; double mean = runningSum / NumberOfPaths; mean = exp(-rExpiry); return mean; The main change from our original Monte Carlo routine is that we now input a PayOff object instead of a strike. The strike is, of course, now hidden inside the inputted object. We pass the object by reference and make itconst as we have no need to change it inside our routine. Note that the only line of our algorithm that is new is double thisPayOff = thePayOff(thisSpot);18 Encapsulation We illustrate how the routine might be called in SimpleMCMain2.cpp. Here we define both call and put objects and this shows that the routine can now be used without change to prices both types of options. Listing 2.5 (SimpleMCMain2.cpp) / requires PayOff1.cpp Random1.cpp, SimpleMC.cpp, / includeSimpleMC.h includeiostream using namespace std; int main() double Expiry; double Strike; double Spot; double Vol; double r; unsigned long NumberOfPaths; cout "\nEnter expiry\n"; cin Expiry; cout "\nEnter strike\n"; cin Strike; cout "\nEnter spot\n"; cin Spot; cout "\nEnter vol\n"; cin Vol; cout "\nr\n"; cin r;2.4 Further extensibility defects 19 cout "\nNumber of paths\n"; cin NumberOfPaths; PayOff callPayOff(Strike, PayOff::call); PayOff putPayOff(Strike, PayOff::put); double resultCall = SimpleMonteCarlo2(callPayOff, Expiry, Spot, Vol, r, NumberOfPaths); double resultPut = SimpleMonteCarlo2(putPayOff, Expiry, Spot, Vol, r, NumberOfPaths); cout "the prices are " resultCall " for the call and " resultPut " for the put\n"; double tmp; cin tmp; return 0; 2.4 Further extensibility defects We have now set-up a pay-off class. One of our original objectives was to be able to extend the code easily without needing to rewrite other code when we add new pay-offs. Have we achieved this? Yes. Suppose we now want to add in the digital call pay-off. What do we need to do? We add in an extra type, digitalcall, in the enum. We also change the switch statement in the operator() to include the extra case of a digital call and in that case return 1 if spot is above strike and 0 otherwise.

Advise: Why You Wasting Money in Costly SEO Tools, Use World's Best Free SEO Tool Ubersuggest.