This is the second in a series of posts covering new content from my book, namely, chapter 5. Part 1 is here.

Registration for the next Introduction to QuantLib Development course is still open: it is the three-day course that I teach based on the contents of this blog and of my book (plus several exercises; bring your compiler) 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 widgets for that are in the sidebar, at the top right of the page. Also, make sure to check my Training page.

Parameterized models and calibration

Example: the Heston model

In this chapter, I’ll use the Heston model as an example. Here, I’ll describe the helper class; the model class will follow after the discussion of the CalibratedModel class in one of the next posts.

The HestonModelHelper class is shown in listing 5.2. It models a European option, and right here we have a code smell; the name says nothing of the nature of the instrument, and instead it refers to a Heston model that is nowhere to be seen in the implementation of the helper. I, for one, didn’t have the issue clear when this class was added.

Listing 5.2: Implementation of the HestonModelHelper class.

    class HestonModelHelper : public CalibrationHelper {
      public:
        HestonModelHelper(
                  const Period& maturity,
                  const Calendar& calendar,
                  const Real s0,
                  const Real strikePrice,
                  const Handle<Quote>& volatility,
                  const Handle<YieldTermStructure>& riskFreeRate,
                  const Handle<YieldTermStructure>& dividendYield,
                  CalibrationHelper::CalibrationErrorType errorType
                             = CalibrationHelper::RelativePriceError)
        : CalibrationHelper(volatility, riskFreeRate, errorType),
          dividendYield_(dividendYield),
          exerciseDate_(calendar.advance(
                           riskFreeRate->referenceDate(), maturity)),
          tau_(riskFreeRate->dayCounter().yearFraction(
                      riskFreeRate->referenceDate(), exerciseDate_)),
          s0_(s0), strikePrice_(strikePrice) {
            boost::shared_ptr<StrikedTypePayoff> payoff(
                 new PlainVanillaPayoff(Option::Call, strikePrice_));
            boost::shared_ptr<Exercise> exercise(
                                new EuropeanExercise(exerciseDate_));
            option_ = boost::shared_ptr<VanillaOption>(
                           new VanillaOption(payoff, exercise));
            marketValue_ = blackPrice(volatility->value());
        }
        Real modelValue() const {
            option_->setPricingEngine(engine_);
            return option_->NPV();
        }
        Real blackPrice(Real volatility) const {
            return blackFormula(Option::Call,
                                /* ...volatility, stored parameters... */);
        }
        void addTimesTo(std::list<Time>&) const {}
      private:
        boost::shared_ptr<VanillaOption> option_;
        // other data members, not shown
    };

Anyway: the implementation is not complex. The constructor takes information on the underlying contract such as maturity and strike, as well as the quoted volatility and other needed market data; it also allows one to choose how to calculate the calibration error. Some data are passed to the base class constructor, while some other are stored in data members; finally, the data are used to instantiate and store a VanillaOption instance and its market price is calculated. The option and its price are also stored in the corresponding data members.

It’s not easy to spot it at first sight, but there’s a small problem in the initialization of the instance. The time to maturity tau_ is calculated in the constructor as the time between today’s date and the exercise date. Unfortunately, this doesn’t take into account the fact that today’s date might change. In order to work correctly even in this case, this class should recalculate the values of tau_ and marketValue_ each time they might have changed—that is, inside an override of the performCalculations method.

The rest is simple. As I mentioned before, the modelValue method is implemented by setting the model-based engine to the instrument and asking it for its NPV (this would work for any model, not just the Heston one, which explains my discontent at the class name). The blackPrice class just returns the result of the Black-Scholes formula based on the value of the passed volatility and of the other stored parameters. (The price could also be obtained by setting a Black engine to the stored instrument and asking for its NPV. This is the route chosen by other helpers in the library.) Finally, the addTimesTo method does nothing; this is actually the only Heston-specific part, since we don’t have a tree-based Heston model. To be fully generic, we should return here the exercise time (that would be the tau_ data member discussed above) so that it can be used by some other model.

That’s all. The machinery of the base class will use these methods and provide the functionality that is needed for the calibration. But before going into that, I need to make a short detour in next post.

Aside: breaking assumptions

What happens if the assumptions baked in the code don’t hold—for instance, because the volatility is not quoted by means of a Black model, or the instrument is not quoted in terms of its volatility at all?

In this case, the machinery still work but we’re left with misnomers that will make it difficult to understand the code. For instance, if the formula used to pass from volatility to price doesn’t come from a Black model, we can still implement it in the blackPrice method, but the next one that reads the code will be left scratching her head. And if the instrument price is quoted directly instead of the volatility, we’ll have to store the price in the volatility_ data member and to implement the blackPrice method rather puzzlingly as:

    Real blackPrice(Real volatility) const {
        return volatility;
    }