Monday, August 26, 2013

Chapter 5, part 4 of 5: Models and calibration

Hello again.

This is the fourth in a series of five post (yes, I found out how many they are) covering chapter 5 of my book. The previous posts are here, here and here.

As I already mentioned (more than once, I seem to remember) during the first half of next week I'll be in London to teach my Introduction to QuantLib Development course. Drop me a line if you want to meet over a pint; probably it won't turn into a QuantLib user group, but it would be nice to meet some of you and chat about what you do with the library. I'll try and tweet the location. And yes, there are still places available for the course; click on this link if you're interested.

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

The CalibratedModel class

The implementation of the CalibratedModel class is shown in listing 5.5. Its core is the calibrate method, with most other features being there in order to support its execution.  (In fact, there's a couple of public methods there that should be used, directly or indirectly, by calculate alone and should belong to the protected section. I'm not sure that, in true Ellery Queen tradition, you have all the clues you need; but you can try looking at the code and guessing which ones.)

Listing 5.5: Implementation of the CalibratedModel class.
    class CalibratedModel : public virtual Observer,
                            public virtual Observable {
      public:
        CalibratedModel(Size nArguments)
        : arguments_(nArguments),
          constraint_(new PrivateConstraint(arguments_)),
          shortRateEndCriteria_(EndCriteria::None) {}

        void update() {
            generateArguments();
            notifyObservers();
        }
        Disposable<Array> params() const;
        virtual void setParams(const Array& params);
        void calibrate(
                   const vector<shared_ptr<CalibrationHelper> >&,
                   OptimizationMethod& method,
                   const EndCriteria& endCriteria,
                   const Constraint& constraint = Constraint(),
                   const vector<Real>& weights = vector<Real>());
        EndCriteria::Type endCriteria();
      protected:
        virtual void generateArguments() {}
        vector<Parameter> arguments_;
        shared_ptr<Constraint> constraint_;
        EndCriteria::Type shortRateEndCriteria_;
      private:
        class PrivateConstraint;
        class CalibrationFunction;
    };

    Disposable<Array> CalibratedModel::params() const {
        Size size = 0, i;
        for (i=0; i<arguments_.size(); i++)
            size += arguments_[i].size();
        Array params(size);
        Size k = 0;
        for (i=0; i<arguments_.size(); i++) {
            for (Size j=0; j<arguments_[i].size(); j++, k++) {
                params[k] = arguments_[i].params()[j];
            }
        }
        return params;
    }

    void CalibratedModel::setParams(const Array& params) {
        Array::const_iterator p = params.begin();
        for (Size i=0; i<arguments_.size(); ++i) {
            for (Size j=0; j<arguments_[i].size(); ++j, ++p) {
                QL_REQUIRE(p!=params.end(),"too few parameters");
                arguments_[i].setParam(j, *p);
            }
        }
        QL_REQUIRE(p==params.end(),"too many parameters");
        generateArguments();
        notifyObservers();
    }

    void CalibratedModel::calibrate(
           const vector<shared_ptr<CalibrationHelper> >& instruments,
           OptimizationMethod& method,
           const EndCriteria& endCriteria,
           const Constraint& additionalConstraint,
           const vector<Real>& weights) {

        Constraint c =
            additionalConstraint.empty()) ?
            *constraint_ :
            CompositeConstraint(*constraint_,additionalConstraint);
        CalibrationFunction f(this, instruments, weights);
        Problem prob(f, c, params());
        shortRateEndCriteria_ = method.minimize(prob, endCriteria);
        setParams(prob.currentValue());
        notifyObservers();
    }

Instances of this class store a vector of Parameter instances, which for some reason are called arguments here; a constraint, to be applied to the set of their underlying parameters; and a member of the EndCriteria::Type enumeration, which tells us how the latest calibration ended (say, because it succeeded, or for reaching the maximum number of evaluations) and whose name still shows the original use of this class for short-rate models. As I mentioned, we gave this class little attention for quite a while.

The constructor takes a single argument specifying the number of model parameters and initializes the data members: the vector of parameters is given the passed size, the constraint is set to an instance of an inner PrivateConstraint class that I'll describe later, and the end criterion is set to None since the model is not yet calibrated.

Now, we have an interesting glitch here. This constructor is obviously meant to be used by derived classes, but is declared as public (probably an oversight). This, together with the fact that the class doesn't define any pure virtual function, makes it possible to create instances of CalibratedModel directly; however, such instances are unusable since they don't provide a way to set their parameters to anything useful (the stored Parameter instances are default-constructed and thus lack any behavior). Technically, fixing this glitch would break backward compatibility; but it might be argued that programs using this feature were broken anyway. We'll think about it in one of the next versions.

When the model receives a notification, the update method notifies the model's own observers after performing any needed calculation. These will be implemented by overriding the virtual generateArguments method. The name might be misleading, since it seems to suggest that parameter instances should be created here; but this can't be, since we don't want to override parameters that we might have already calibrated. (We don't want to recalibrate at each notification, either.) Instead, this method is used either to create parameters that don't need calibration (e.g., the term-structure parameter in some short-rate models, which follows the risk-free rate) or to perform some housekeeping, as we'll see in the continuation of the Heston model example.

The params and setParams methods are used to read and write the underlying parameters. To return them, the params method asks each of the stored Parameter instances for the number of underlying parameters it provides, creates an Array instance that can hold all of them, and collects their values (it can't add them to the array in a single loop because Array doesn't provide a push_back operation). In a similar way, the setParams method reads from the passed array and writes the required number of values in the stored Parameter instances.

Now, it you guessed that params and setParams are the methods that I'd rather have in the protected section, you can go and pour yourself the alcoholic beverage of your choice. (Drink responsibly. Also, don't drink and code.) These two methods should only be called from inside the calibrate method; client code isn't even able to know the number of the underlying parameters or which ones belong to each Parameter instance (unless its programmer reads the source code of the particular model used. That's cheating, though) so it shouldn't be able to modify them, and it has very little use for reading them, too.

As I said, the calibrate method is the focus of the class; and in true managerial fashion, it delegates most of the work to other objects. Simply put, it sets up a minimization problem so that solving it gives the calibrated set of parameters. The ingredients of the problem are an instance of a class derived from OptimizationMethod (for instance, it might implement the simplex method or Levenberg-Marquardt. More details on those are in appendix A) which is passed to the method by the calling code; a function to minimize, or rather a function object, which is an instance of its inner CalibrationFunction class and that returns a measure of the calibration error (more on that shortly); and a constraint on the parameter values, which defaults to the instance of PrivateConstraint stored at construction and can optionally be combined with an additional constraint passed as an argument.

The calibrate method collects all of the above, instantiates the problem, and starts the minimization. When that is done, it saves the end criterion, sets the parameter values to those that minimize the error (that is, those returned by problem.currentValue()) and notifies any observers that something has changed. The end criterion (which might be that the minimization succeeded, or that it failed for a number of different reasons) can be retrieved by means of the endCriteria method. If I were to write this class now, I'd probably return it from calibrate instead; but I'm ambivalent about it. It might make sense to store it in the model.

As it is now, the calibrate method provide little exception safety; if an exception were thrown at some point during the calibration, the model would be left with the last parameter values tried by the minimizer (the very same that probably caused the exception to be thrown). We might provide the strong guarantee by storing the parameter values before starting the minimization, catching any exceptions, and restoring the old parameter values before re-throwing; but it's probably best to set an appropriate end criterion, which is what happens if the calibration fails for other reasons.

The last pieces of functionality are implemented in the PrivateConstraint and CalibrationFunction inner classes, shown in listing 5.6.

Listing 5.6: Inner classes of the CalibratedModel class.
    class CalibratedModel::PrivateConstraint : public Constraint {
      private:
        class Impl :  public Constraint::Impl {
            const vector<Parameter>& arguments_;
          public:
            Impl(const vector<Parameter>& arguments);
            bool test(const Array& params) const {
                for (Size i=0; i<arguments_.size(); i++) {
                    Array testParams(/* `select the correct subset` */);
                    if (!arguments_[i].testParams(testParams))
                        return false;
                }
                return true;
            }
        };
      public:
        PrivateConstraint(const vector<Parameter>& arguments);
    };

    class CalibratedModel::CalibrationFunction
        : public CostFunction {
      public:
        CalibrationFunction(
           CalibratedModel* model,
           const vector<shared_ptr<CalibrationHelper> >& instruments,
           const vector<Real>& weights)
        : model_(model, no_deletion), instruments_(instruments),
          weights_(weights) {}

        virtual Disposable<Array> values(const Array& params) const {
            model_->setParams(params);
            Array values(instruments_.size());
            for (Size i=0; i<instruments_.size(); i++) {
                values[i] = instruments_[i]->calibrationError()
                           *sqrt(weights_[i]);
            }
            return values;
        }
        virtual Real value(const Array& params) const;
      private:
        shared_ptr<CalibratedModel> model_;
        const vector<shared_ptr<CalibrationHelper> >& instruments_;
        vector<Real> weights_;
    };

The PrivateConstraint class works by collecting the constraints set to the stored Parameter instances. The logic is similar to that of params or setParams; it loops over the stored parameters, determines the subset of underlying parameters that belong to each one, and asks the Parameter instance to check them by calling its testParams method. The composite constraint is satisfied if and only if all the inner constraints are.

Finally, the CalibrationFunction class provides an estimate of the calibration error for a given set of parameters. Its constructor takes and stores a pointer to the model being calibrated, the set of quoted instruments being used for the calibration, and a set of weights. For some reason, the pointer to the model is stored in a shared_ptr instance, taking care that it's not deleted with the calibration function. Storing the raw pointer would have been enough.

The class inherits from CostFunction, which requires derived classes to implement both a values method returning a set of errors (in this case, one per quoted instrument) and a value method returning a single error estimate; a given optimization method might use the one or the other. The two work in a similar way, so I'm showing the implementation of just the first in the listing: they set the given parameters to the model, and then ask the stored helpers for the calibration error. For this to work, the helpers need to be set a pricing engine that uses the model being calibrated. The setup of the entire thing would be something like this:
    shared_ptr<HestonModel> model(...);
    vector<shared_ptr<CalibrationHelper> > helpers(...);
    shared_ptr<PricingEngine> engine =
        make_shared<AnalyticHestonEngine>(model, ...);
    for (i=0; i<helpers.size(); ++i)
        helpers[i]->setPricingEngine(engine);
    model->calibrate(helpers, ...);
That is, the helpers use the engine to calculate their model prices, and in turn the engine uses the model. Thus, after the call to setParams inside the values method the model prices change and so do the corresponding calibration errors. The values method returns the set of distinct errors, while the value method returns the sum of their squares.

A final note: currently, the CalibrationFunction class is declared as a friend of CalibratedModel. This is actually not necessary, since it only accesses the public setParams method; and if we were using C++11, it wouldn't be necessary even if setParams was protected. According to the new standard, inner classes have access to all member of their enclosing class, public or not.

Next post: an example of model.

Share this post: