Chapter 3, part 5 of n: other term structures
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.
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.
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.
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.