Greetings.

This is the last post in a series (started here) that covered chapter 6 of my book, viz., the Monte Carlo framework in QuantLib. I’ve no idea yet what to write in next post: we’ll see.

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.

Follow me on Twitter if you want to be notified of new posts, or add me to your circles, or subscribe via RSS: the buttons for that are in the footer. Also, make sure to check my Training page.

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 itself, 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 the 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 delta and 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.

Listing 6.16: Implementation of the McEuropeanBasketEngine class template.

    template <class RNG = PseudoRandom, class S = Statistics>
    class MCEuropeanBasketEngine
        : public BasketOption::engine,
          private McSimulation<MultiVariate,RNG,S> {
      public:
        typedef McSimulation<MultiVariate,RNG,S> simulation_type;
        typedef typename simulation_type::path_generator_type
            path_generator_type;
        ... // same for the other defined types
        MCEuropeanBasketEngine(
                const shared_ptr<StochasticProcess>&,
                const Handle<YieldTermStructure>& discountCurve,
                Size timeSteps,
                Size timeStepsPerYear,
                bool antitheticVariate,
                Size requiredSamples,
                Real requiredTolerance,
                Size maxSamples,
                BigNatural seed);
        void calculate() const {
            simulation_type::calculate(requiredTolerance_,
                                       requiredSamples_,
                                       maxSamples_);
            const S& stats = this->mcModel_->sampleAccumulator();
            results_.value = stats.mean();
            if (RNG::allowsErrorEstimate)
                results_.errorEstimate = stats.errorEstimate();
        }
      private:
        TimeGrid timeGrid() const;
        shared_ptr<path_generator_type> pathGenerator() const;
        shared_ptr<path_pricer_type> pathPricer() const;
        shared_ptr<StochasticProcess> process_;
        Handle<YieldTermStructure> discountCurve_;
        Size timeSteps_, timeStepsPerYear_;
        Size requiredSamples_;
        Size maxSamples_;
        Real requiredTolerance_;
        BigNatural seed_;
    };

As expected, it inherits publicly from the BasketOption::engine class; however, it also inherits privately from the McSimulation class template.

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 McSimulation in order to implement its purely virtual methods (such as pathGenerator or pathPricer) and would have the effect to make the design of the engine more complex; whereas, by inheriting from McSimulation, we 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 McSimulation constructor) and registers the newly-built instance as an observer of both the stochastic process and the discount curve.

The calculate method, required by the PricingEngine interface, 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 McSimulation, but it could be reasonably expected to inherit from PricingEngine, too. However, it we did that, our engine would inherit from both McEngine and 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 define a 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.

    template <class RNG, class S>
    TimeGrid MCEuropeanBasketEngine<RNG,S>::timeGrid() const {
        Time T = process_->time(arguments_.exercise->lastDate());
        if (timeSteps_ != Null<Size>()) {
            return TimeGrid(T, timeSteps_);
        } else if (timeStepsPerYear_ != Null<Size>()) {
            Size steps = timeStepsPerYear_*T;
            return TimeGrid(T, std::max<Size>(steps, 1));
        } else {
            QL_FAIL("time steps not specified");
        }
    }

The 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 if 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 else clause) an error is raised.

    template <class RNG, class S>
    shared_ptr<typename MCEuropeanBasketEngine<RNG,S>::
               path_generator_type>
    MCEuropeanBasketEngine<RNG,S>::pathGenerator() const {
        Size factors = process_->factors();
        TimeGrid grid = timeGrid();
        Size steps = grid.size() - 1;
        typename RNG::rsg_type gen =
            RNG::make_sequence_generator(factors*steps, seed_);
        return shared_ptr<path_generator_type>(
                   new path_generator_type(process_, grid, gen));
    }

The pathGenerator method asks the process for the number of factors, determines the number of time steps from the result of the timeGrid 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.

    template <class RNG, class S>
    shared_ptr<typename MCEuropeanBasketEngine<RNG,S>::
               path_pricer_type>
    MCEuropeanBasketEngine<RNG,S>::pathPricer() const {
        Date maturity = arguments_.exercise->lastDate();
        return shared_ptr<path_pricer_type>(
            new EuropeanBasketPathPricer(
                arguments_.payoff, arguments_.quantities,
                discountCurve_->discount(maturity)));
    }

Finally, the 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 EuropeanMultiPathPricer class.

    class EuropeanBasketPathPricer : public PathPricer<MultiPath> {
      public:
        EuropeanBasketPathPricer(const shared_ptr<Payoff>& payoff,
                                 const vector<Real>& quantities,
                                 DiscountFactor discount);
        Real operator()(const MultiPath& path) const {
            Real basketValue = 0.0;
            for (Size i=0; i<quantities_.size(); ++i)
                basketValue += quantities_[i] * path[i].back();
            return (*payoff)(basketValue) * discount_;
        }
      private:
        shared_ptr<Payoff> payoff_;
        vector<Real> quantities_;
        DiscountFactor discount_;
    };

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

    engine = MakeMcEuropeanBasketEngine<PseudoRandom>(process,
                                                      discountCurve)
             .withTimeStepsPerYear(12)
             .withAbsoluteTolerance(0.001)
             .withSeed(42)
             .withAntitheticVariate();

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 drift or evolve method takes into account the correction, and you’ll be able to use it without changing the engine code.

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 methods): 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 nested namespace 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 QuantLib namespace—and make it accessible to everybody.