And of course, the weekly plug: you can still register for my Introduction to QuantLib Development course. It’s three days of presentations (for me) and exercises (for you) based on the material in my book. It’s going to be in London from September 22nd to 24th. Click the link above for more info.
Example: basket option
To close this series, I’ll show and discuss an example of how to build a pricing engine with the Monte Carlo machinery I described so far. The instrument I’ll use is a simple European option on a basket of stocks, giving its owner the right to buy or sell the whole basket at an given price; the quantities of each of the stocks in the basket are also specified by the contract.
For brevity, I won’t show the implementation of the instrument class
BasketOption. (This class is not the same as the
BasketOption class implemented in QuantLib. The one used here is
simplified for illustration purposes.) It would be quite similar to
VanillaOption class I’ve shown in this post, with the addition of a
data member for the quantities (added to both the instrument and its
arguments class). Also, I won’t deal with the Greeks; but if the
BasketOption class were to define them, methods such as
gamma would return a vector.
The main class in this example is the one implementing the pricing
engine. It is the
MCEuropeanBasketEngine class template, shown in
Listing 6.16: Implementation of the
McEuropeanBasketEngine class template.
As expected, it inherits publicly from the
class; however, it also inherits privately from the
In idiomatic C++, the use of private inheritance denotes an “is
implemented in terms of” relationship. We don’t want public
inheritance here, since that would imply an “is a” relationship; in
our conceptual model,
MCEuropeanBasketEngine is a pricing engine for
the basket option and not a simulation that could be used on its
own. The use of multiple inheritance is shunned by some, and in fact
it could be avoided in this case; our engine might use composition
instead, and contain an instance of
McSimulation. However, that
would require inheriting a new simulation class from
order to implement its purely virtual methods (such as
pathPricer) and would have the effect to make the design of the
engine more complex; whereas, by inheriting from
can define such methods in the engine itself. (In order to avoid this
dilemma, we would have to rewrite the
McSimulation class so that it
doesn’t use the Template Method pattern; that is, we should make it a
concrete class which would be passed the used path generator and
pricer as constructor arguments. Apart from breaking backward
compatibility, I’m not sure this would be worth the hassle.) Finally,
note the template parameters: we leave to the user the choice of the
RNG traits, but since we’re modeling a basket option we specify the
MultiVariate class as the Monte Carlo traits.
The constructor (whose implementation is not shown for brevity) takes
the stochastic process for the underlyings, a discount curve, and a
number of parameters related to the simulation. It copies the
arguments into the corresponding data members (except for the
antitheticVariate flag, which is passed to the
constructor) and registers the newly-built instance as an observer of
both the stochastic process and the discount curve.
calculate method, required by the
calls the method by the same name from
McSimulation and passes it
the needed parameters; then, it retrieves the statistics and stores
the mean value and, if possible, the error estimate. This behavior is
not specific of this particular option, and in fact it could be
abstracted out in some generic
McEngine class; but this would muddle
the conceptual model. Such a class would inherit from
but it could be reasonably expected to inherit from
too. However, it we did that, our engine would inherit from both
BasketOption::engine, leading to the dreaded
inheritance diamond. On the other hand, if we didn’t inherit it from
PricingEngine (calling it
McEngineAdapter or something) it would
add more complexity to the inheritance hierarchy, since it would be an
additional layer between
McSimulation and our engine, and wouldn’t
remove all the scaffolding anyway: our engine would still need to
calculate method forwarding to the one in the
adapter. Thus, I guess we’ll leave it at that.
The rest of listing 6.16 (continued below) shows the three methods
required to implement the
McSimulation interface and turn our class
into a working engine.
timeGrid method builds the grid used by the simulation by
specifying the end time (given by the exercise date) and the number of
steps. Depending on the parameters passed to the engine constructor,
they might have been specified as a given total number (the first
clause) or a number per year (the second). In either case, the
specification is turned into a total number of steps and passed to the
TimeGrid constructor. If neither was specified (the
an error is raised.
pathGenerator method asks the process for the number of factors,
determines the number of time steps from the result of the
method I just described, builds a random-sequence generator with the
correct dimensionality (which of course is the product of the two
numbers above) and uses it together with the underlying process and
the time grid to instantiate a multi-path generator.
pathPricer method collects the data required to
determine the payoff on each path—that is, the payoff object
itself, the quantities and the discount at the exercise date, that the
method precalculates—and builds an instance of the
EuropeanBasketPathPricer class, sketched in listing 6.17.
Listing 6.17: Sketch of the
The path pricer stores the passed data and uses them to calculate the
realized value of the option in its
operator() goverloading, whose
implementation calculates the value of the basket at maturity and
returns the corresponding payoff discounted to the present time. The
calculation of the basket value is a straightforward loop that
combines the stored quantities with the asset values at maturity,
retrieved from the end points of the respective paths.
At this point, the engine is completed; but it’s still a bit unwieldy to instantiate. We’d want to add a factory class with a fluent interface, so that one can write
but I’m not showing its implementation here. There’s plenty of such examples in the library for your perusal.
Also, you’ll have noted that I kept the example as simple as possible, and for that reason I avoided a few possible generalizations. For instance, another pricer might need some other specific dates besides the maturity, and the time grid should be built accordingly; or the payoff might have been path-dependent, so that the path pricer should look at several path values besides the last. But I don’t think you’ll have any difficulties in writing the corresponding code.
After all this, you might still have a question: how generic is this
engine, really? Well, somewhat less that I’d like. It is generic in
the sense that you can plug in any process for N asset prices, and it
will work. It can even do more exotic stuff, such as quanto effects:
if you go the traditional way and model the effect as an correction
for the drift of the assets, you can write (or better yet, decorate)
your process so that its
evolve method takes into account
the correction, and you’ll be able to use it without changing the
However, if you want to use a process that models other stochastic
variables besides the asset prices (say, a multi-asset Heston process)
you’re likely to get into some trouble. The problem is that you’ll end
up in the
operator() of the path pricer with N quantities and 2N
paths, without any indication of which are for the prices and which
for the volatilities. How should they be combined to get the correct
basket price? Of course, one can write a specific pricer for any
particular process; but I’d like to provide something more reusable.
One simple solution is for the process and the pricer to share some coding convention. For instance, if you arrange the process so that the first N paths are those of the prices and the last M are those of any other variables, the pricer in listing 6.17 will work; note that it loops over the number of quantities, not the size of the process. However, this leaves one wide open to errors that will go undetected if a process doesn’t conform to the convention.
Another possibility that comes to mind is to decorate the process with
a layer that would show the asset prices and hide the extra
variables. The decorator would appear as a process requiring the same
number of random variates as the original, but exposing less
stochastic variables. It would have to store some state in order to
keep track of the hidden variables (which wouldn’t be passed to its
evolve method, to name one, would have to take the
current prices, add the extra variables stored in the instance, call
evolve in the original process, store the new values of the extra
variables so that they’re available for next call, and return only the
new asset prices.
However, I’m not a fan of this solution. It would only work when the decorated methods are called in the expected order, and would break otherwise; for instance, if you called a method twice with the exact same arguments (starting prices and random variates) you would get different return, due to the changed internal state. The long way to express this is that the methods of the decorated process would lose referential transparency. The short way is that its interface would be a lie.
A more promising possibility (albeit one that requires some changes to
the framework) would be to let the path generator do the filtering,
with some help from the process. If the process could provide a method
returning some kind of mask—say, a list of the indices of the
assets into the whole set of variables, or a vector of booleans where
the i-th element would be set to
true if the i-th path corresponds
to an asset price—then the generator could use it to drive the
evolution of the variables correctly, and at the same time write in
the multi-path only the asset prices. To make the whole thing
backward-compatible, the mask would be passed to the generator only
optionally, and the base
StochasticProcess class would have a
default implementation of the method returning an empty mask. If
either way no mask were provided, the generator would revert to the
current behavior. If needed, the method could be generalized to return
other masks besides that for the prices; for instance, a variance swap
would be interested in the volatility but not the price.
Finally, note that some assumptions are built right into the engine and cannot be relaxed without rewriting it. For instance, we’re assuming that the discount factor for the exercise date is deterministic. If you wrote a process that models stochastic interest rates as well as asset prices, and therefore wanted to discount on each path according to the realization of the rates on that path, you’d have to write a custom engine and path pricer to be used with your specific process. Fortunately, that would be the only things you’d have to write; you’d still be able to reuse the other pieces of the framework.
Aside: need-to-know basis
As usual, I didn’t show in which files one should put the several
classes I described. Well, as a general principle, the more
encapsulation the better; thus, to begin with, helper classes should
be hidden from the public interface. For instance, the
EuropeanBasketPathPricer class is only used by the engine and could
be hidden from client code. The best way would be to define such
classes inside an anonymous namespace in a
.cpp file, but that’s not
always possible: in our case, the engine is a class template, so
that’s not an option. The convention we’re using in QuantLib is that
helper classes that must be declared in a header file are placed in a
detail, and the user agrees to be a gentleman (or a
lady, of course) and to leave it alone.
If, later on, we find out that we need the class elsewhere (for
instance, because we want to use
EuropeanBasketPathPricer as a
control-variate path pricer for another engine) we can move it to a
header file—or, if it’s already in one, to the main
namespace—and make it accessible to everybody.