Hello again.

Back to chapter 3 of my book after the detour of last post (the series on chapter 3 started here). This post covers two sections: default-probability term structures and inflation ter structures. They are both short enough that splitting them into two post would have felt like stretching the content a bit too much.

Follow me on Twitter if you want to be notified of new posts, or add me to your circles, or subscribe via RSS: the buttons for that are in the footer. Also, make sure to check my Training page.

Term structures

Other term structures

So far, the focus of this chapter has been on yield term structures. Of course, other kinds of term structure are implemented in the library. In this section, I’ll review them shortly: mostly, I’ll point out how they differ from yield term structures and what particular features they sport.

Default-probability term structures

Default-probability term structures are the most similar in design to yield term structures. They can be expressed in terms of default probability, survival probability, default density, or hazard rate; any one of the four quantities can be obtained from any other, much like zero rates and discount factors.

Unlike yield term structures (in which all methods are implemented in terms of the discountImpl method) the base default-probability structure has no single method for others to build upon. Instead, as shown in listing 3.13, it declares two abstract methods survivalProbabilityImpl and defaultDensityImpl. It’s left to derived classes to decide which one should be written in terms of the other; the base class implements survivalProbability and defaultDensity based on the respective implementation methods (their implementation is not as simple as shown, of course; here I omitted range checking and extrapolation for clarity), defaultProbability based trivially on survivalProbability, and hazardRate in terms of both survival probability and default density.

Listing 3.13: Sketch of the DefaultProbabilityTermStructure class.

    class DefaultProbabilityTermStructure : public TermStructure {
      public:
        // ...constructors...

        Probability survivalProbability(Time t) const {
            return survivalProbabilityImpl(t);
        }

        Probability defaultProbability(Time t) const {
            return 1.0 - survivalProbability(t);
        }
        Probability defaultProbability(Time t1,
                                       Time t2) const {
            Probability p1 = defaultProbability(t1),
                        p2 = defaultProbability(t2);
            return p2 - p1;
        }

        Real defaultDensity(Time t) const {
            return defaultDensityImpl(t);
        }

        Rate hazardRate(Time t) const {
            Probability S = survivalProbability(t);
            return S == 0.0 ? 0.0 : defaultDensity(t)/S;
        }

        // ...other methods...
      protected:
        virtual Probability survivalProbabilityImpl(Time) const = 0;
        virtual Real defaultDensityImpl(Time) const = 0;
      private:
        // ...data members...
    };

Listing 3.14 sketches the adapter classes that, as for yield term structures, allow one to define a new default-probability structure in terms of the single quantity of his choice—either survival probability, default density, or hazard rate (the default probability is so closely related to survival probability that we didn’t think it necessary to provide a corresponding adapter). The first one, SurvivalProbabilityStructure, defines defaultDensityImpl in terms of the implementation of survivalProbabilityImpl, which is left purely virtual and must be provided in derived classes; the second one, DefaultDensityStructure, does the opposite; and the last one, HazardRateStructure, defines both survival probability and default density in terms of a newly-declared purely abstract hazardRateImpl method.

Unfortunately, some of the adapters rely on numerical integration in order to provide conversions among the desired quantities. The provided implementations use dark magic in both maths and coding (namely, Gaussian quadratures and boost::bind) to perform the integrations efficiently; but when inheriting from such classes, you should consider overriding the adapter methods if a closed formula is available for the integral.

Listing 3.14: Adapter classes for default-probability term structures.

    class SurvivalProbabilityStructure
        : public DefaultProbabilityTermStructure {
      public:
        // ...constructors...
      protected:
        Real defaultDensityImpl(Time t) const {
            // returns the derivative of the survival probability at t
        }
    };

    class DefaultDensityStructure
        : public DefaultProbabilityTermStructure {
      public:
        // ...constructors...
      protected:
        Probability survivalProbabilityImpl(Time t) const {
            // 1 minus the integral of the default density from 0 to t
        }
    };

    class HazardRateStructure
        : public DefaultProbabilityTermStructure {
      public:
        // ...constructors...
      protected:
        virtual Real hazardRateImpl(Time) const = 0;
        Probability survivalProbabilityImpl(Time t) const {
            // exp(-I); I is the integral of the hazard rate from 0 to t
        }
        Real defaultDensityImpl(Time t) const {
            return hazardRateImpl(t)*survivalProbabilityImpl(t);
        }
    };

Like for yield curves, the library provides a few template classes that implement the adapter interfaces by interpolating discrete data, as well as a generic piecewise default-probability curve template and the traits required to select its underlying quantity. Together with the existing interpolation traits, this allows one to instantiate classes such as PiecewiseDefaultCurve<DefaultDensity,Linear>. The implementation is quite similar to the one described in this post; in fact, so much similar that it’s not worth describing here. The only thing worth noting is that the default-probability structure is not self-sufficient: in order to price the instruments required for its bootstrap, a discount curve is needed (then again, the same is true of LIBOR curves in today’s multiple-curve settings). You’ll have to be consistent and use the same curve for your pricing engines; otherwise, you might suddenly find out that your CDS are no longer at the money.

Aside: Cinderella method

In the implementation of DefaultProbabilityTermStructure, you’ve probably noticed another symmetry break like the one discussed in a previous post. There’s a difference though; in that case, discount factors were singled out to be given a privileged role. In this case, hazard rates are singled out to play the mistreated stepsister; there’s no hazardRateImpl beside the similar methods declared for survival probability or default density. Again, a look at past versions of the code shows that once, it was symmetric; and again, I can give no reason for the change. I’m sure it looked like a good idea at the time.

The effect is that classes deriving from the HazardRateStructure adapter must go through some hoops to return hazard rates, since they’re not able to call hazardRateImpl directly; instead, they have to use the default implementation and return the ratio of default density and survival probability (possibly performing an integration along the way). Unfortunately, even our fairy godmother can’t change this now without risking to break existing code.

Inflation term structures

Inflation term structures have a number of features that set them apart from the term structures I described so far. Not surprisingly, most such features add complexity to the provided classes.

The most noticeable difference is that we have two separate kinds of inflation term structures (and two different interfaces) instead of a single one. The library does provide a single base class InflationTermStructure, that contains some inspectors and some common behavior; however, the interfaces returning the actual inflation rates are declared in two separate child classes, leading to the hierarchy sketched in listing 3.15. The two subclasses model zero-coupon and year-on-year inflation rates, which are not easily converted into one another and thus foil our usual multiple-adapter scheme.

Listing 3.15: Sketch of the InflationTermStructure class and its children.

    class InflationTermStructure : public TermStructure {
      public:
        // ...constructors...

        virtual Date baseDate() const = 0;
        virtual Rate baseRate() const;
        virtual Period observationLag() const;

        Handle<YieldTermStructure> nominalTermStructure() const;

        void setSeasonality(const shared_ptr<Seasonality>&);
      protected:
        Handle<YieldTermStructure> nominalTermStructure_;
        Period observationLag_;
        // ...other data members...
    };

    class ZeroInflationTermStructure
        : public InflationTermStructure {
      public:
        // ...constructors...
        Rate zeroRate(const Date &d,
                      const Period& instObsLag = Period(-1,Days),
                      bool forceLinearInterpolation = false,
                      bool extrapolate = false) const;
      protected:
        virtual Rate zeroRateImpl(Time t) const = 0;
    };

    class YoYInflationTermStructure
        : public InflationTermStructure {
      public:
        // ...        `\ldots{}constructors\ldots{}`
        Rate yoyRate(const Date &d,
                     const Period& instObsLag = Period(-1,Days),
                     bool forceLinearInterpolation = false,
                     bool extrapolate = false) const;
      protected:
        virtual Rate yoyRateImpl(Time time) const = 0;
    };

This state of things has both advantages and disadvantages; possibly more of the latter. On the one hand, it leads to a pair of duplicated sub-hierarchies, which is obviously a smell. (It can get worse. Until now, we haven’t considered period-on-period rates with a frequency other than annual. Hopefully, they will only lead to a generalization of the year-on-year curve.) On the other hand, it simplifies a bit the sub-hierarchies; for instance, there’s no adapter classes since each kind of term structure has only one underlying quantity (that is, either zero-coupon rates or year-on-year rates).

Other differences are due to the specific quirks of inflation fixings. Since inflation figures for a given month are announced after an observation lag, inflation term structures have a base date, as well as a reference date; the base date is the one corresponding to the latest announced fixing. If an inflation figure is needed for a date in the past with respect to today’s date but after the base date, it must be forecast. (You might remember that, at the beginning of this chapter, I obscurely suggested that the future doesn’t always begin at the reference date. This is the exception I was referring to.) Also, since inflation fixings are affected by seasonality, inflation term structures provide the means to store an instance of the polymorphic Seasonality class (which for brevity I won’t describe). If given, such instance models the desired seasonal correction for the returned inflation rates, in a plain implementation of the Strategy pattern.

Unlike other kinds of term structure, the inflation interface doesn’t provide methods taking a time value instead of a date; the whole fixing machinery depends on a number of date calculations (what month we’re in, the corresponding period for the fixing, and whatnot) and there’s simply no reliable way to convert from times to dates, so we called the whole thing off.

Yet another difference is that inflation term structures store a discount curve. Yes, I made the same face as you when I realized it—and shortly before the 1.0 release, too, when the interfaces would have to be frozen. No, let me rephrase that: I made a worse face than you. In the end, though, I left it alone. It’s not the design we used for default-probability structures; but at least it has the advantage that a bootstrapped inflation term structure carries around the discount curve it used, so one doesn’t have to keep track of their association to be consistent. We can choose the best design for release 2.0, when we have used both for a few years; so all’s well that ends well, I guess. Still, I made a mental note to perform code reviews more often.

The remaining machinery (interpolated curves, piecewise bootstrap etc.) is similar to what I described for other kinds of curves. Due to the separate sub-hierarchies for zero-coupon and year-on-year rates, though, there’s a couple of differences. On the one hand, there are no adapter classes that implement the whole interface based on a single underlying quantity—such as, say, ZeroStructure for discounts or HazardRateStructure for default probabilities. On the other hand, piecewise curves don’t use traits to select the underlying quantity (as, for instance, in PiecewiseYieldCurve<Discount,LogLinear>). To select one or the other, you’ll have to choose the appropriate class and write something like PiecewiseZeroInflation<Linear> instead.