2. Financial instruments and pricing engines

The statement that a financial library must provide the means to price financial instruments would certainly have appealed to Monsieur de La Palisse. However, that is only a part of the whole problem; a financial library must also provide developers with the means to extend it by adding new pricing functionality.

Foreseeable extensions are of two kinds, and the library must allow either one. On the one hand, it must be possible to add new financial instruments; on the other hand, it must be feasible to add new means of pricing an existing instrument. Both kinds have a number of requirements, or in pattern jargon, forces that the solution must reconcile. This chapter details such requirements and describes the design that allows QuantLib to satisfy them.

2.1 The Instrument class

In our domain, a financial instrument is a concept in its own right. For this reason alone, any self-respecting object-oriented programmer will code it as a base class from which specific instruments will be derived.

The idea, of course, is to be able to write code such as

    for (i = portfolio.begin(); i != portfolio.end(); ++i)
        totalNPV += i->NPV();

where we don’t have to care about the specific type of each instrument. However, this also prevents us from knowing what arguments to pass to the NPV method, or even what methods to call. Therefore, even the two seemingly harmless lines above tell us that we have to step back and think a bit about the interface.

2.1.1 Interface and requirements

The broad variety of traded assets—which range from the simplest to the most exotic—implies that any method specific to a given class of instruments (say, equity options) is bound not to make sense for some other kind (say, interest-rate swaps). Thus, very few methods were singled out as generic enough to belong to the Instrument interface. We limited ourselves to those returning its present value (possibly with an associated error estimate) and indicating whether or not the instrument has expired; since we can’t specify what arguments are needed,4 the methods take none; any needed input will have to be stored by the instrument. The resulting interface is shown in the listing below.

Preliminary interface of the Instrument class.
    class Instrument {
      public:
        virtual ~Instrument();
        virtual Real NPV() const = 0;
        virtual Real errorEstimate() const = 0;
        virtual bool isExpired() const = 0;
    };

As is good practice, the methods were first declared as pure virtual ones; but—as Sportin’ Life points out in Gershwin’s Porgy and Bess—it ain’t necessarily so. There might be some behavior that can be coded in the base class. In order to find out whether this was the case, we had to analyze what to expect from a generic financial instrument and check whether it could be implemented in a generic way. Two such requirements were found at different times, and their implementation changed during the development of the library; I present them here in their current form.

One is that a given financial instrument might be priced in different ways (e.g., with one or more analytic formulas or numerical methods) without having to resort to inheritance. At this point, you might be thinking “Strategy pattern”. It is indeed so; I devote section 2.2 to its implementation.

The second requirement came from the observation that the value of a financial instrument depends on market data. Such data are by their nature variable in time, so that the value of the instrument varies in turn; another cause of variability is that any single market datum can be provided by different sources. We wanted financial instruments to maintain links to these sources so that, upon different calls, their methods would access the latest values and recalculate the results accordingly; also, we wanted to be able to transparently switch between sources and have the instrument treat this as just another change of the data values.

We were also concerned with a potential loss of efficiency. For instance, we could monitor the value of a portfolio in time by storing its instruments in a container, periodically poll their values, and add the results. In a simple implementation, this would trigger recalculation even for those instruments whose inputs did not change. Therefore, we decided to add to the instrument methods a caching mechanism: one that would cause previous results to be stored and only recalculated when any of the inputs change.

2.1.2 Implementation

The code managing the caching and recalculation of the instrument value was written for a generic financial instrument by means of two design patterns.

When any of the inputs change, the instrument is notified by means of the Observer pattern (Gamma et al, 1995). The pattern itself is briefly described5 in appendix A; I describe here the participants.

Obviously enough, the instrument plays the role of the observer while the input data play that of the observables. In order to have access to the new values after a change is notified, the observer needs to maintain a reference to the object representing the input. This might suggest some kind of smart pointer; however, the behavior of a pointer is not sufficient to fully describe our problem. As I already mentioned, a change might come not only from the fact that values from a data feed vary in time; we might also want to switch to a different data feed. Storing a (smart) pointer would give us access to the current value of the object pointed; but our copy of the pointer, being private to the observer, could not be made to point to a different object. Therefore, what we need is the smart equivalent of a pointer to pointer. This feature was implemented in QuantLib as a class template and given the name of Handle. Again, details are given in appendix A; relevant to this discussion is the fact that copies of a given Handle share a link to an object. When the link is made to point to another object, all copies are notified and allow their holders to access the new pointee. Furthermore, Handles forward any notifications from the pointed object to their observers.

Finally, classes were implemented which act as observable data and can be stored into Handles. The most basic is the Quote class, representing a single varying market value. Other inputs for financial instrument valuation can include more complex objects such as yield or volatility term structures.6

Another problem was to abstract out the code for storing and recalculating cached results, while still leaving it to derived classes to implement any specific calculations. In earlier versions of QuantLib, the functionality was included in the Instrument class itself; later, it was extracted and coded into another class—somewhat unsurprisingly called LazyObject—which is now reused in other parts of the library. An outline of the class is shown in the following listing.

Outline of the LazyObject class.
    class LazyObject : public virtual Observer,
                       public virtual Observable {
      protected:
        mutable bool calculated_;
        virtual void performCalculations() const = 0;
      public:
        void update() { calculated_ = false; }
        virtual void calculate() const {
            if (!calculated_) {
                calculated_ = true;
                try {
                    performCalculations();
                } catch (...) {
                    calculated_ = false;
                    throw;
                }
            }
        }
    };

The code is not overly complex. A boolean data member calculated_ is defined which keeps track of whether results were calculated and still valid. The update method, which implements the Observer interface and is called upon notification from observables, sets such boolean to false and thus invalidates previous results.

The calculate method is implemented by means of the Template Method pattern (Gamma et al, 1995), sometimes also called non-virtual interface. As explained in the Gang of Four book, the constant part of the algorithm (in this case, the management of the cached results) is implemented in the base class; the varying parts (here, the actual calculations) are delegated to a virtual method, namely, performCalculations, which is called in the body of the base-class method. Therefore, derived classes will only implement their specific calculations without having to care about caching: the relevant code will be injected by the base class.

The logic of the caching is simple. If the current results are no longer valid, we let the derived class perform the needed calculations and flag the new results as up to date. If the current results are valid, we do nothing.

However, the implementation is not as simple. You might wonder why we had to insert a try block setting calculated_ beforehand and a handler rolling back the change before throwing the exception again. After all, we could have written the body of the algorithm more simply—for instance, as in the following, seemingly equivalent code, that doesn’t catch and rethrow exceptions:

    if (!calculated_) {
        performCalculations();
        calculated_ = true;
    }

The reason is that there are cases (e.g., when the lazy object is a yield term structure which is bootstrapped lazily) in which performCalculations happens to recursively call calculate. If calculated_ were not set to true, the if condition would still hold and performCalculations would be called again, leading to infinite recursion. Setting such flag to true prevents this from happening; however, care must now be taken to restore it to false if an exception is thrown. The exception is then rethrown so that it can be caught by the installed error handlers.

A few more methods are provided in LazyObject which enable users to prevent or force a recalculation of the results. They are not discussed here. If you’re interested, you can heed the advice often given by master Obi-Wan Kenobi: “Read the source, Luke.”

The Instrument class inherits from LazyObject. In order to implement the interface outlined earlier, it decorates the calculate method with code specific to financial instruments. The resulting method is shown in the listing below, together with other bits of supporting code.

Excerpt of the Instrument class.
    class Instrument : public LazyObject {
      protected:
        mutable Real NPV_;
      public:
        Real NPV() const {
            calculate();
            return NPV_;
        }
        void calculate() const {
            if (isExpired()) {
                setupExpired();
                calculated_ = true;
            } else {
                LazyObject::calculate();
            }
        }
        virtual void setupExpired() const {
            NPV_ = 0.0;
        }
    };

Once again, the added code follows the Template Method pattern to delegate instrument-specific calculations to derived classes. The class defines an NPV_ data member to store the result of the calculation; derived classes can declare other data members to store specific results.7 The body of the calculate method calls the virtual isExpired method to check whether the instrument is an expired one. If this is the case, it calls another virtual method, namely, setupExpired, which has the responsibility of giving meaningful values to the results; its default implementation sets NPV_ to 0 and can be called by derived classes. The calculated_ flag is then set to true. If the instrument is not expired, the calculate method of LazyObject is called instead, which in turn will call performCalculations as needed. This imposes a contract on the latter method, namely, its implementations in derived classes are required to set NPV_ (as well as any other instrument-specific data member) to the result of the calculations. Finally, the NPV method ensures that calculate is called before returning the answer.

2.1.3 Example: interest-rate swap

I end this section by showing how a specific financial instrument can be implemented based on the described facilities.

The chosen instrument is the interest-rate swap. As you surely know, it is a contract which consists in exchanging periodic cash flows. The net present value of the instrument is calculated by adding or subtracting the discounted cash-flow amounts depending on whether the cash flows are paid or received.

Not surprisingly, the swap is implemented8 as a new class deriving from Instrument. Its outline is shown in the following listing.

Partial interface of the Swap class.
class Swap : public Instrument {
  public:
    Swap(const vector<shared_ptr<CashFlow> >& firstLeg,
         const vector<shared_ptr<CashFlow> >& secondLeg,
         const Handle<YieldTermStructure>& termStructure);
    bool isExpired() const;
    Real firstLegBPS() const;
    Real secondLegBPS() const;
  protected:
    // methods
    void setupExpired() const;
    void performCalculations() const;
    // data members
    vector<shared_ptr<CashFlow> > firstLeg_, secondLeg_;
    Handle<YieldTermStructure> termStructure_;
    mutable Real firstLegBPS_, secondLegBPS_;
};

It contains as data members the objects needed for the calculations—namely, the cash flows on the first and second leg and the yield term structure used to discount their amounts—and two variables used to store additional results. Furthermore, it declares methods implementing the Instrument interface and others returning the swap-specific results. The class diagram of Swap and the related classes is shown in the figure below.

Class diagram of the `Swap` class.
Class diagram of the Swap class.

The fitting of the class to the Instrument framework is done in three steps, the third being optional depending on the derived class; the relevant methods are shown in the next listing.

Partial implementation of the Swap class.
Swap::Swap(const vector<shared_ptr<CashFlow> >& firstLeg,
           const vector<shared_ptr<CashFlow> >& secondLeg,
           const Handle<YieldTermStructure>& termStructure)
: firstLeg_(firstLeg), secondLeg_(secondLeg),
  termStructure_(termStructure) {
    registerWith(termStructure_);
    vector<shared_ptr<CashFlow> >::iterator i;
    for (i = firstLeg_.begin(); i!= firstLeg_.end(); ++i)
        registerWith(*i);
    for (i = secondLeg_.begin(); i!= secondLeg_.end(); ++i)
        registerWith(*i);
}

bool Swap::isExpired() const {
    Date settlement = termStructure_->referenceDate();
    vector<shared_ptr<CashFlow> >::const_iterator i;
    for (i = firstLeg_.begin(); i!= firstLeg_.end(); ++i)
        if (!(*i)->hasOccurred(settlement))
            return false;
    for (i = secondLeg_.begin(); i!= secondLeg_.end(); ++i)
        if (!(*i)->hasOccurred(settlement))
            return false;
    return true;
}

void Swap::setupExpired() const {
    Instrument::setupExpired();
    firstLegBPS_= secondLegBPS_ = 0.0;
}

void Swap::performCalculations() const {
    NPV_ = - Cashflows::npv(firstLeg_,**termStructure_)
           + Cashflows::npv(secondLeg_,**termStructure_);
    errorEstimate_ = Null<Real>();

    firstLegBPS_ = - Cashflows::bps(firstLeg_, **termStructure_);
    secondLegBPS_ = Cashflows::bps(secondLeg_, **termStructure_);
}

Real Swap::firstLegBPS() const {
    calculate();
    return firstLegBPS_;
}
  
Real Swap::secondLegBPS() const {
    calculate();
    return secondLegBPS_;
}

The first step is performed in the class constructor, which takes as arguments (and copies into the corresponding data members) the two sequences of cash flows to be exchanged and the yield term structure to be used for discounting their amounts. The step itself consists in registering the swap as an observer of both the cash flows and the term structure. As previously explained, this enables them to notify the swap and trigger its recalculation each time a change occurs.

The second step is the implementation of the required interface. The logic of the isExpired method is simple enough; its body loops over the stored cash flows checking their payment dates. As soon as it finds a payment which still has not occurred, it reports the swap as not expired. If none is found, the instrument has expired. In this case, the setupExpired method will be called. Its implementation calls the base-class one, thus taking care of the data members inherited from Instrument; it then sets to 0 the swap-specific results.

The last required method is performCalculations. The calculation is performed by calling two external functions from the Cashflows class.9 The first one, namely, npv, is a straightforward translation of the algorithm outlined above: it cycles on a sequence of cash flows adding the discounted amount of its future cash flows. We set the NPV_ variable to the difference of the results from the two legs. The second one, bps, calculates the basis-point sensitivity (BPS) of a sequence of cash flows. We call it once per leg and store the results in the corresponding data members. Since the result carries no numerical error, the errorEstimate_ variable is set to Null<Real>()—a specific floating-point value which is used as a sentinel value indicating an invalid number.10

The third and final step only needs to be performed if—as in this case—the class defines additional results. It consists in writing corresponding methods (here, firstLegBPS and secondLegBPS) which ensure that the calculations are (lazily) performed before returning the stored results.

The implementation is now complete. Having been written on top of the Instrument class, the Swap class will benefit from its code. Thus, it will automatically cache and recalculate results according to notifications from its inputs—even though no related code was written in Swap except for the registration calls.

2.1.4 Further developments

You might have noticed a shortcoming in my treatment of the previous example and of the Instrument class in general. Albeit generic, the Swap class we implemented cannot manage interest-rate swaps in which the two legs are paid in different currencies. A similar problem would arise if you wanted to add the values of two instruments whose values are not in the same currency; you would have to convert manually one of the values to the currency of the other before adding them together.

Such problems stem from a single weakness of the implementation: we used the Real type (i.e., a simple floating-point number) to represent the value of an instrument or a cash flow. Therefore, such results miss the currency information which is attached to them in the real world.

The weakness might be removed if we were to express such results by means of the Money class. Instances of such class contain currency information; moreover, depending on user settings, they are able to automatically perform conversion to a common currency upon addition or subtraction.

However, this would be a major change, affecting a large part of the code base in a number of ways. Therefore, it will need some serious thinking before we tackle it (if we do tackle it at all).

Another (and more subtle) shortcoming is that the Swap class fails to distinguish explicitly between two components of the abstraction it represents. Namely, there is no clear separation between the data specifying the contract (the cash-flow specification) and the market data used to price the instrument (the current discount curve).

The solution is to store in the instrument only the first group of data (i.e., those that would be in its term sheet) and keep the market data elsewhere.11 The means to do this are the subject of the next section.

2.2 Pricing engines

We now turn to the second of the requirements I stated in the previous section. For any given instrument, it is not always the case that a unique pricing method exists; moreover, one might want to use multiple methods for different reasons. Let’s take the classic textbook example—the European equity option. One might want to price it by means of the analytic Black-Scholes formula in order to retrieve implied volatilities from market prices; by means of a stochastic volatility model in order to calibrate the latter and use it for more exotic options; by means of a finite-difference scheme in order to compare the results with the analytic ones and validate one’s finite-difference implementation; or by means of a Monte Carlo model in order to use the European option as a control variate for a more exotic one.

Therefore, we want it to be possible for a single instrument to be priced in different ways. Of course, it is not desirable to give different implementations of the performCalculations method, as this would force one to use different classes for a single instrument type. In our example, we would end up with a base EuropeanOption class from which AnalyticEuropeanOption, McEuropeanOption and others would be derived. This is wrong in at least two ways. On a conceptual level, it would introduce different entities when a single one is needed: a European option is a European option is a European option, as Gertrude Stein said. On a usability level, it would make it impossible to switch pricing methods at run-time.

The solution is to use the Strategy pattern, i.e., to let the instrument take an object encapsulating the computation to be performed. We called such an object a pricing engine. A given instrument would be able to take any one of a number of available engines (of course corresponding to the instrument type), pass the chosen engine the needed arguments, have it calculate the value of the instrument and any other desired quantities, and fetch the results. Therefore, the performCalculations method would be implemented roughly as follows:

    void SomeInstrument::performCalculations() const {
        NPV_ = engine_->calculate(arg1, arg2, ... , argN);
    }

where we assumed that a virtual calculate method is defined in the engine interface and implemented in the concrete engines.

Unfortunately, the above approach won’t work as such. The problem is, we want to implement the dispatching code just once, namely, in the Instrument class. However, that class doesn’t know the number and type of arguments; different derived classes are likely to have data members differing wildly in both number and type. The same goes for the returned results; for instance, an interest-rate swap might return fair values for its fixed rate and floating spread, while the ubiquitous European option might return any number of Greeks.

An interface passing explicit arguments to the engine through a method, as the one outlined above, would thus lead to undesirable consequences. Pricing engines for different instruments would have different interfaces, which would prevent us from defining a single base class; therefore, the code for calling the engine would have to be replicated in each instrument class. This way madness lies.

The solution we chose was that arguments and results be passed and received from the engines by means of opaque structures aptly called arguments and results. Two structures derived from those and augmenting them with instrument-specific data will be stored in any pricing engine; an instrument will write and read such data in order to exchange information with the engine.

The listing below shows the interface of the resulting PricingEngine class, as well as its inner argument and results classes and a helper GenericEngine class template. The latter implements most of the PricingEngine interface, leaving only the implementation of the calculate method to developers of specific engines. The arguments and results classes were given methods which ease their use as drop boxes for data: arguments::validate is to be called after input data are written to ensure that their values lie in valid ranges, while results::reset is to be called before the engine starts calculating in order to clean previous results.

Interface of PricingEngine and of related classes.
    class PricingEngine : public Observable {
      public:
        class arguments;
        class results;
        virtual ~PricingEngine() {}
        virtual arguments* getArguments() const = 0;
        virtual const results* getResults() const = 0;
        virtual void reset() const = 0;
        virtual void calculate() const = 0;
    };

    class PricingEngine::arguments {
      public:
        virtual ~arguments() {}
        virtual void validate() const = 0;
    };

    class PricingEngine::results {
      public:
        virtual ~results() {}
        virtual void reset() = 0;
    };

    // ArgumentsType must inherit from arguments;
    // ResultType from results.
    template<class ArgumentsType, class ResultsType>
    class GenericEngine : public PricingEngine {
      public:
        PricingEngine::arguments* getArguments() const {
            return &arguments_;
        }
        const PricingEngine::results* getResults() const {
            return &results_;
        }
        void reset() const { results_.reset(); }
      protected:
        mutable ArgumentsType arguments_;
        mutable ResultsType results_;
    };

Armed with our new classes, we can now write a generic performCalculation method. Besides the already mentioned Strategy pattern, we will use the Template Method pattern to allow any given instrument to fill the missing bits. The resulting implementation is shown in the next listing. Note that an inner class Instrument::result was defined; it inherits from PricingEngine::results and contains the results that have to be provided for any instrument12

Excerpt of the Instrument class.
    class Instrument : public LazyObject {
      public:
       class results;
       virtual void performCalculations() const {
          QL_REQUIRE(engine_, "null pricing engine");
          engine_->reset();
          setupArguments(engine_->getArguments());
          engine_->getArguments()->validate();
          engine_->calculate();
          fetchResults(engine_->getResults());
       }
       virtual void setupArguments(
                         PricingEngine::arguments*) const {
          QL_FAIL("setupArguments() not implemented");
       }
       virtual void fetchResults(
                   const PricingEngine::results* r) const {
          const Instrument::results* results =
              dynamic_cast<const Value*>(r);
          QL_ENSURE(results != 0, "no results returned");
          NPV_ = results->value;
          errorEstimate_ = results->errorEstimate;
       }
       template <class T> T result(const string& tag) const;
      protected:
       shared_ptr<PricingEngine> engine_;
    };

    class Instrument::results
        : public virtual PricingEngine::results {
      public:
       Value() { reset(); }
       void reset() {
           value = errorEstimate = Null<Real>();
       }
       Real value;
       Real errorEstimate;
    };

As for performCalculation, the actual work is split between a number of collaborating classes—the instrument, the pricing engine, and the arguments and results classes. The dynamics of such a collaboration (described in the following paragraphs) might be best understood with the help of the UML sequence diagram shown in the first of the next two figures; the static relationship between the classes (and a possible concrete instrument) is shown in the second.

Sequence diagram of the interplay between instruments
  and pricing engines.
Sequence diagram of the interplay between instruments and pricing engines.
Class diagram of `Instrument`, `PricingEngine`, and related classes
  including a derived instrument class.
Class diagram of Instrument, PricingEngine, and related classes including a derived instrument class.

A call to the NPV method of the instrument eventually triggers (if the instrument is not expired and the relevant quantities need to be calculated) a call to its performCalculations method. Here is where the interplay between instrument and pricing engine begins. First of all, the instrument verifies that an engine is available, aborting the calculation if this is not the case. If one is found, the instrument prompts it to reset itself. The message is forwarded to the instrument-specific result structure by means of its reset method; after it executes, the structure is a clean slate ready for writing the new results.

At this point, the Template Method pattern enters the scene. The instrument asks the pricing engine for its argument structure, which is returned as a pointer to arguments. The pointer is then passed to the instrument’s setupArguments method, which acts as the variable part in the pattern. Depending on the specific instrument, such method verifies that the passed argument is of the correct type and proceeds to fill its data members with the correct values. Finally, the arguments are asked to perform any needed checks on the newly-written values by calling the validate method.

The stage is now ready for the Strategy pattern. Its arguments set, the chosen engine is asked to perform its specific calculations, implemented in its calculate method. During the processing, the engine will read the inputs it needs from its argument structure and write the corresponding outputs into its results structure.

After the engine completes its work, the control returns to the Instrument instance and the Template Method pattern continues unfolding. The called method, fetchResults, must now ask the engine for the results, downcast them to gain access to the contained data, and copy such values into its own data members. The Instrument class defines a default implementation which fetches the results common to all instruments; derived classes might extend it to read specific results.

2.2.1 Example: plain-vanilla option

At this point, an example is necessary. 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.

The listing below 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.

Interface of the VanillaOption class.
class VanillaOption : public Instrument {
  public:
    // accessory classes
    class arguments;
    class results;
    class engine;
    // constructor
    VanillaOption(const shared_ptr<Payoff>&,
                  const 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
    shared_ptr<Payoff> payoff_;
    shared_ptr<Exercise> exercise_;
    // specific results
    mutable Real delta_;
    mutable Real gamma_;
    mutable Real theta_;
    // ...more
};

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 shown in the next listing.

Interface of the VanillaOption inner classes.
class VanillaOption::arguments
    : public PricingEngine::arguments {
  public:
    // constructor
    arguments();
    void validate() const;
    shared_ptr<Payoff> payoff;
    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, Stroustrup, 2013; 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 the following listing.

Implementation of the VanillaOption class.
    VanillaOption::VanillaOption(
        const shared_ptr<StrikedTypePayoff>& payoff,
        const 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
    }

    Real VanillaOption::delta() const {
        calculate();
        QL_ENSURE(delta_ != Null<Real>(), "delta not given");
        return delta_;
    }

Its constructor takes a few objects defining the instrument. Most of them will be described in later chapters or in appendix A. 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.

The methods related to expiration are straightforward; 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 copies the results into his own data members.

Any method that returns additional results, such as delta, will do as NPV does: it will call calculate, check that the corresponding result was cached (because any given engine might or might not be able to calculate it) and return it.

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 the listing below and implements the analytic Black-Scholes-Merton formula for European options.

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_;
    };

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.

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.13

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.14

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.