Hello everybody. This is the final post in a series of four covering chapter 2 of the book; part 1, 2 and 3 can be found here, here and here. I look forward to your feedback. Next time, an interlude with an ubiquitous utility class.

As you might have heard if you follow me in Google+, last week we had a major release in the family (word to the wise: this is where having prepared a few posts in advance paid off). In lesser news, QuantLib 1.3 is ready, too. Like Bob Dylan, it shall be released—that is, after I package and check it, which will take a day or two.

Registration for the next Introduction to QuantLib Development course is still open; it is the course that I teach based on the contents of the Implementing QuantLib blog and book, and you can find more information, a brochure and a booking form by clicking on this link.

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, even if you can’t make it to the next course.

Financial instruments and pricing engines

Example: plain-vanilla option

At this point, an example is necessary to show how the facilities described in the previous post can be used to implement an instruments. A word of warning, though: although a class exists in QuantLib which implements plain-vanilla options—i.e., simple call and put equity options with either European, American or Bermudan exercise—such class is actually the lowermost leaf of a deep class hierarchy. Having the Instrument class at its root, such hierarchy specializes it first with an Option class, then again with a OneAssetOption class generalizing options on a single underlying, passing through another class or two until it finally defines the VanillaOption class we are interested in.

There are good reasons for this; for instance, the code in the OneAssetOption class can naturally be reused for, say, Asian options, while that in the Option class lends itself for reuse when implementing all kinds of basket options. Unfortunately, this causes the code for pricing a plain option to be spread among all the members of the described inheritance chain, which would not make for an extremely clear example. Therefore, I will describe a simplified VanillaOption class with the same implementation as the one in the library, but inheriting directly from the Instrument class; all code implemented in the intermediate classes will be shown as if it were implemented in the example class rather than inherited.

Listing 2.7 shows the interface of our vanilla-option class. It declares the required methods from the Instrument interface, as well as accessors for additional results, namely, the greeks of the options; as pointed out in the previous section, the corresponding data members are declared as mutable so that their values can be set in the logically constant calculate method.

Besides its own data and methods, VanillaOption declares a number of accessory classes: that is, the specific argument and result structures and a base pricing engine. They are defined as inner classes to highlight the relationship between them and the option class; their interface is also shown in the listing.

Listing 2.7: Interface of the VanillaOption class and its inner classes.

    class VanillaOption : public Instrument {
      public:
        // accessory classes
        class arguments;
        class results;
        class engine;
        // constructor
        VanillaOption(const boost::shared_ptr<Payoff>&,
                      const boost::shared_ptr<Exercise>&);
        // implementation of instrument method
        bool isExpired() const;
        void setupArguments(Arguments*) const;
        void fetchResults(const Results*) const;
        // accessors for option-specific results
        Real delta() const;
        Real gamma() const;
        Real theta() const;
        // ...more greeks
      protected:
        void setupExpired() const;
        // option data
        boost::shared_ptr<Payoff> payoff_;
        boost::shared_ptr<Exercise> exercise_;
        // specific results
        mutable Real delta_;
        mutable Real gamma_;
        mutable Real theta_;
        // ...more
    };

    class VanillaOption::arguments
        : public PricingEngine::arguments {
      public:
        // constructor
        arguments();
        void validate() const;
        boost::shared_ptr<Payoff> payoff;
        boost::shared_ptr<Exercise> exercise;
    };

    class Greeks : public virtual PricingEngine::results {
      public:
        Greeks();
        Real delta, gamma;
        Real theta;
        Real vega;
        Real rho, dividendRho;
    };

    class VanillaOption::results : public Instrument::results,
                                   public Greeks {
      public:
        void reset();
    };

    class VanillaOption::engine
        : public GenericEngine<VanillaOption::arguments,
                               VanillaOption::results> {};

Two comments can be made on such accessory classes. The first is that, making an exception to what I said in my introduction to the example, I didn’t declare all data members into the results class. This was done in order to point out an implementation detail. One might want to define structures holding a few related and commonly used results; such structures can then be reused by means of inheritance, as exemplified by the Greeks structure that is here composed with Instrument::results to obtain the final structure. In this case, virtual inheritance from PricingEngine::results must be used to avoid the infamous inheritance diamond (see, for instance, [1]; the name is in the index).

The second comment is that, as shown, it is sufficient to inherit from the class template GenericEngine (instantiated with the right argument and result types) to provide a base class for instrument-specific pricing engines. We will see that derived classes only need to implement their calculate method.

We now turn to the implementation of the VanillaOption class, shown in listing 2.8. Its constructor takes a few objects defining the instrument. Most of them will be described in later posts or in appendix A of the book. For the time being, suffice to say that the payoff contains the strike and type (i.e., call or put) of the option, and the exercise contains information on the exercise dates and variety (i.e., European, American, or Bermudan). The passed arguments are stored in the corresponding data members. Also, note that they do not include market data; those will be passed elsewhere.

Listing 2.8: Implementation of the VanillaOption class.

    VanillaOption::VanillaOption(
        const boost::shared_ptr<StrikedTypePayoff>& payoff,
        const boost::shared_ptr<Exercise>& exercise)
    : payoff_(payoff), exercise_(exercise) {}

    bool VanillaOption::isExpired() const {
        Date today = Settings::instance().evaluationDate();
        return exercise_->lastDate() < today;
    }

    void VanillaOption::setupExpired() const {
        Instrument::setupExpired();
        delta_ = gamma_ = theta_ = ... = 0.0;
    }

    void VanillaOption::setupArguments(
                       PricingEngine::arguments* args) const {
        VanillaOption::arguments* arguments =
            dynamic_cast<VanillaOption::arguments*>(args);
        QL_REQUIRE(arguments != 0, "wrong argument type");
        arguments->exercise = exercise_;
        arguments->payoff = payoff_;
    }

    void VanillaOption::fetchResults(
                      const PricingEngine::results* r) const {
        Instrument::fetchResults(r);
        const VanillaOption::results* results =
            dynamic_cast<const VanillaOption::results*>(r);
        QL_ENSURE(results != 0, "wrong result type");
        delta_ = results->delta;
        ... // other Greeks
    }

The methods related to expiration are simple enough; isExpired checks whether the latest exercise date is passed, while setupExpired calls the base-class implementation and sets the instrument-specific data to 0.

The setupArguments and fetchResults methods are a bit more interesting. The former starts by downcasting the generic argument pointer to the actual type required, raising an exception if another type was passed; it then turns to the actual work. In some cases, the data members are just copied verbatim into the corresponding argument slots. However, it might be the case that the same calculations (say, converting dates into times) will be needed by a number of engines; setupArguments provides a place to write them just once.

The fetchResults method is the dual of setupArguments. It also starts by downcasting the passed results pointer; after verifying its actual type, it just copies the results into his own data members.

Albeit simple, the above implementation is everything we needed to have a working instrument—working, that is, once it is set an engine which will perform the required calculations. Such an engine is sketched in listing 2.9 and implements the analytic Black-Scholes-Merton formula for European options. Its constructor takes (and registers itself with) a Black-Scholes stochastic process that contains market-data information about the underlying including present value, risk-free rate, dividend yield, and volatility. Once again, the actual calculations are hidden behind the interface of another class, namely, the BlackCalculator class. However, the code has enough detail to show a few relevant features.

Listing 2.9: Sketch of an engine for the VanillaOption class.

    class AnalyticEuropeanEngine
        : public VanillaOption::engine {
      public:
        AnalyticEuropeanEngine(
          const shared_ptr<GeneralizedBlackScholesProcess>&
                                                        process)
        : process_(process) {
            registerWith(process);
        }
        void calculate() const {
          QL_REQUIRE(
              arguments_.exercise->type() == Exercise::European,
              "not an European option");
          shared_ptr<PlainVanillaPayoff> payoff =
             dynamic_pointer_cast<PlainVanillaPayoff>(
                                              arguments_.payoff);
          QL_REQUIRE(process, "Black-Scholes process needed");
          ... // other requirements
            
          Real spot = process_->stateVariable()->value();
          ... // other needed quantities

          BlackCalculator black(payoff, forwardPrice,
                                stdDev, discount);

          results_.value = black.value();
          results_.delta = black.delta(spot);
          ... // other greeks
        }
      private:
        shared_ptr<GeneralizedBlackScholesProcess> process_;
    };

The method starts by verifying a few preconditions. This might come as a surprise, since the arguments of the calculations were already validated by the time the calculate method is called. However, any given engine can have further requirements to be fulfilled before its calculations can be performed. In the case of our engine, one such requirement is that the option is European and that the payoff is a plain call/put one, which also means that the payoff will be cast down to the needed class. (boost::dynamic_pointer_cast is the equivalent of dynamic_cast for shared pointers.)

In the middle section of the method, the engine extracts from the passed arguments any information not already presented in digested form. Shown here is the retrieval of the spot price of the underlying; other quantities needed by the engine, e.g., the forward price of the underlying and the risk-free discount factor at maturity, are also extracted. (You can find the full code of the engine in the QuantLib sources.)

Finally, the calculation is performed and the results are stored in the corresponding slots of the results structure. This concludes both the calculate method and the example.

Bibliography

[1] B. Stroustrup, The C++ Programming Language, 4th edition. Addison-Wesley, 2013.