Monday, September 2, 2013

Chapter 5, part 5 of 5: Model example

Hello everybody.

This is the final post in a series of five covering the newly written chapter 5 of my book. The previous posts are here, here, here and here. I'll be grateful for any feedback.

As I publish this, I'm in London for my Introduction to QuantLib Development course. Drop me a line if you want to be notified when a new one is scheduled; my contact info is at 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, continued

Time for the second part of the example I started in this post. The code for the HestonModel class is shown in listing 5.7.

Listing 5.7: Implementation of the HestonModel class.
    class HestonModel : public CalibratedModel {
      public:
        HestonModel(const shared_ptr<HestonProcess>& process)
        : CalibratedModel(5), process_(process) {
            arguments_[0] = ConstantParameter(process->theta(),
                                              PositiveConstraint());
            arguments_[1] = ConstantParameter(process->kappa(),
                                              PositiveConstraint());
            arguments_[2] = ConstantParameter(process->sigma(),
                                              PositiveConstraint());
            arguments_[3] =
                ConstantParameter(process->rho(),
                                  BoundaryConstraint(-1.0, 1.0));
            arguments_[4] = ConstantParameter(process->v0(),
                                              PositiveConstraint());
            generateArguments();
            registerWith(process_->riskFreeRate());
            registerWith(process_->dividendYield());
            registerWith(process_->s0());
        }

        Real theta() const { return arguments_[0](0.0); }
        Real kappa() const { return arguments_[1](0.0); }
        Real sigma() const { return arguments_[2](0.0); }
        Real rho()   const { return arguments_[3](0.0); }
        Real v0()    const { return arguments_[4](0.0); }

        shared_ptr<HestonProcess> process() const {
            return process_;
        }
      protected:
        void generateArguments() {
            process_.reset(
                       new HestonProcess(process_->riskFreeRate(),
                                         process_->dividendYield(),
                                         process_->s0(),
                                         v0(), kappa(), theta(),
                                         sigma(), rho()));
        }
        shared_ptr<HestonProcess> process_;
    };
As you might know, the model has five parameters theta, kappa, sigma, rho and v0. The process it describes for its underlying also depends on the risk-free rate, its current value, and possibly a dividend yield. For reasons that will become more clear in chapter 6, the library groups all of those in a separate HestonProcess class (for brevity, I'm not showing its interface here; we're just using it as the container for the model parameters).

The HestonModel constructor takes an instance of the process class, stores it, and defines the parameters to calibrate. First it passes their number (5) to its base class constructor, then it builds each of them. They are all constant parameters; rho is constrained to be between -1 and 1, while the others must all be positive. Their initial values are taken from the process. The class defines the inspectors theta, kappa, sigma, rho and v0 to retrieve their current values; each of them returns the value of the corresponding Parameter instance at time t=0 (which is as good as any other time, since the parameters are constant).

After the parameters are built, the generateArguments method is called. This will also be called each time the parameters change during calibration, and replaces the stored process instance with another one containing the same term structures and quote as the old one but with the new parameters. The reason for this is that the new process would be ready if any engine were to require it from the model; but I wonder if the process inspector should not build the process on demand instead. If the actual process is not required except as a holder of parameters and curves, we could define inspectors for all of them instead, have the engine use them directly, and save ourselves the creation of a new complex object at each step of the calibration. You're welcome to do the experiment and time the new implementation against the current one.

Finally, the constructor registers with the relevant observables. The process instance will be replaced by generateArguments, so there's no point in registering with it. Instead, we register directly with the contained handles, that will be moved inside each new process instance.

Together with the inspectors I already mentioned, this completes the implementation of the model. The calibration machinery is inherited from the CalibratedModel class, and the only thing that's needed to make it work is an engine that takes a HestonModel instance and uses it to price the VanillaOption instances contained in the calibration helpers.

You'll forgive me for not showing here the AnalyticHestonEngine class provided by QuantLib: it implements a closed formula for European options under the Heston model, cites as many as five papers and books in its comments, and goes on for about 650 lines of code. For the non mathematically-minded, it is Cthulhu fhtagn stuff. If you're interested, the full code is available in the library.

Share this post: