Welcome back.

The content for this post is the fourth part of an ongoing series on the QuantLib finite-difference framework.

But first: this week the final version of Visual Studio 2015 was released and, whaddayaknow, QuantLib 1.6 was not quite prepared for it. We had tried to be forward-looking and had prepared our version-detecting code for VC13, but alas, Visual Studio 2015 turned out to be VC14 instead. The library compiles and links just fine, but has the wrong name and the auto-link pragmas get confused. This week I might be releasing a 1.6.1 version that will fix the issue, God willing and the creek don’t rise—since the other bit of news this week was that Sourceforge had a major outage (you might have noticed that the QuantLib web site and the mailing lists have been down for a few days). Now it’s mostly up again, but as of today it’s still not possible to make new releases. Keep your fingers crossed.

Subscribe to my Substack to receive my posts in your inbox, or follow me on Twitter or LinkedIn if you want to be notified of new posts, or subscribe via RSS if you’re the tech type: the buttons for all that are in the footer. Also, I’m available for training, both online and (when possible) on-site: visit my Training page for more information.

Step conditions

At any step (or in the middle of a step; we’ll see later how we account for this) something might happen that changes the price of the asset; for instance, a right can be exercised or a coupon could be paid.

As opposed to boundary conditions, we called these step conditions. The name might not be the best; I can easily see the model as enforcing the “condition” that the asset price is the maximum between its continuation and the intrinsic value, but a coupon payment is harder to categorize in this way. In any case, the base class for all such conditions is shown in the listing below.

    template <class array_type>
    class StepCondition {
      public:
        virtual ~StepCondition() {}
        virtual void applyTo(array_type& a, Time t) const = 0;
    };

The applyTo method, which modifies the array of asset values in place, must also take care of checking that the condition applies at the given time; so, for instance, an American exercise will change the asset values regardless of the time, whereas a dividend payment will first check the passed time and bail out if it doesn’t match the known payment time.

As you see, the base class is quite simple; but derived classes over-generalized and went South. In the next listing, you can see one such class, from which even simple conditions (like the one for American exercise) are derived. We could have implemented a simple applyTo method comparing the option values and the intrinsic values. Instead, we started adding constructors over the years so that instances could be built from either an existing array of intrinsic values, or a payoff, or some data that could describe the payoff. Of course these alternatives needed to be stored into different data members, so we added type erasure to the mix, we defined an interface to retrieve intrinsic values, and we gave it an array-based and a payoff-based implementation.

    template <class array_type>
    class CurveDependentStepCondition
        : public StepCondition<array_type> {
      public:
        void applyTo(Array &a, Time) const {
            for (Size i = 0; i < a.size(); i++) {
                a[i] = applyToValue(a[i], getValue(a,i));
            }
        }
      protected:
        CurveDependentStepCondition(Option::Type type,
                                    Real strike);
        CurveDependentStepCondition(const Payoff *p);
        CurveDependentStepCondition(const array_type & a);

        class CurveWrapper {
          public:
            Real getValue(const array_type &a,
                          Size index) const = 0;
        };
        boost::shared_ptr<curvewrapper> curveItem_;

        Real getValue(const array_type &a, Size index) const {
            return curveItem_->getValue(a, index);
        }

        virtual Real applyToValue(Real, Real) const = 0;

        class ArrayWrapper : public CurveWrapper {
            array_type value_;
          public:
            ...
        };

        class PayoffWrapper : public CurveWrapper {
            boost::shared_ptr<payoff> payoff_;
          public:
            ...
        };
    };

Ironically, we only ever used half of this. Existing engines use the array-based constructor and implementation, and the payoff-based inner class is not used anywhere in the library, Which is just as well; because, as I reviewed the code to write this chapter, I found out that its implementation is wrong. So all in all, we could have written a much simpler implementation of an American exercise condition that would just take an array of intrinsic values and implement applyTo accordingly. If you ever implement a step condition, I suggest you do the same.

A final note; the step condition is not applied inside the step method of the evolution scheme, as we’ll see shortly. What this means is that there’s no guarantee that the step condition won’t break the boundary condition, or even the other way around. In practice, though, applying a step condition for a model that makes any sense will naturally enforce the chosen boundary condition.

Next time, with all the basic pieces in place, we’ll start to put them together.

Aside: look, Ma, no hands

The faulty implementation of PayoffWrapper was added in February 2003, which probably makes this the longest-lived bug in the library (apart from those we still haven’t found, of course). If I hadn’t been looking at the code to write this chapter, the critter could have got old enough to drive.

Now, bugs happen; nothing shocking about this. What irks me, though, is that the problem would have been caught easily by a couple of unit tests. I and others usually try and review what goes in the library, and the code that gets contributed has often been used in the field and purged of obvious errors; but still I’m somewhat worried about the lack of coverage of old code in the test suite. If you have a coverage tool and some time on your hands, please let me hear from you. I’d love to see some analysis on this.