This post is the second in a series of a still undetermined number; the first part is here.
Call me a slowcoach (or whatever the expression might be in your part of the world), but I only found out this week that there’s a Twitter feed for the Quantitative Finance Stack Exchange site. I’ll be retweeting the QuantLib-related questions when the answers are useful, so you can push that “follow” button on the right if you’re interested in those but don’t want to get the full site feed. Or you can push it anyway. I won’t mind.
Yield Term Structures
YieldTermStructure class predates
it was even called
TermStructure back in the day, when it was the
only kind of term structure in the library and we still hadn’t seen
the world. Its interface provides the means to forecast interest rates
and discount factors at any date in the curve domain; also, it
implements some machinery to ease the task of writing a concrete yield
Interface and implementation
The interface of the
YieldTermStructure class is sketched in listing
Listing 3.3: Partial interface of the
The constructors just forward their arguments to the corresponding
constructors in the
TermStructure class—nothing to write home
about. The other methods return information on the yield structure in
different ways: on the one hand, they can return zero rates, forward
rates, and discount factors (rates are returned as instances of the
InterestRate class, that I’ll describe briefly in a future post); on
the other hand, they are overloaded so that they can return
information as function of either dates or times.
Of course, there is a relationship between zero rates, forward rates,
and discount factors; the knowledge of any one of them is sufficient
to deduce the others. (I won’t bore you with the formulas
here—you know them.) This is reflected in the implementation,
outlined in listing 3.4; the Template Method patterns is used to
implement all public methods directly or indirectly in terms of the
discountImpl abstract method. Derived classes only need to
implement the latter in order to return any of the above quantities.
Listing 3.4: Partial implementation of the
Discount, forward-rate, and zero-rate curves
What if the author of a derived class doesn’t want to implement
discountImpl, though? After all, one might want to describe a yield
curve in terms, say, of zero rates. Ever ready to serve (just like
Jeeves in the P. G. Wodehouse novels—not that you’re Bernie
Wooster, of course) QuantLib provides a couple of classes to be used
in this case. The two classes (outlined in listing 3.5) are called
ForwardRateStructure. They use the Adapter
pattern (in case you’re keeping count, this would be another notch in
the spine of our Gang-of-Four book) to transform the discount-based
YieldTermStructure into interfaces based on zero-yield
and instantaneous-forward rates, respectively.
Listing 3.5: Outline of the
The implementation of
ZeroYieldStructure is simple enough. A few
constructors (not shown here) forward their arguments to the
corresponding constructors in the parent
class. The Adapter pattern is implemented in the protected section: an
zeroYieldImpl method is declared and used to implement the
discountImpl method. Thus, authors of derived classes only need to
provide an implementation of
zeroYieldImpl to obtain a fully
functional yield curve. (Of course, the other required methods (such
maxDate) must be implemented as well.) Note that, due to the
formula used to obtain the discount factor, such method must return
zero yields as continuously-compounded annualized rates.
In a similar way, the
ForwardRateStructure class provides the means
to describe the curve in terms of instantaneous forward rates (again,
on an annual basis) by implementing a
forwardImpl method in derived
classes. However, it has an added twist. In order to obtain the
discount at a given time T, we have to average the instantaneous
forwards between 0 and T, thus retrieving the corresponding zero-yield
rate. This class can’t make any assumption on the shape of the
forwards; therefore, all it can do is to perform a numerical
integration—an expensive calculation. In order to provide a hook
for optimization, the average is performed in a virtual
zeroYieldImpl method that can be overridden if a faster calculation
is available. You might object that if an expression is available for
the zero yields, one can inherit from
ZeroYieldStructure and be done
with it; however, it is conceptually cleaner to express the curve in
terms of the forwards if they were the actual focus of the model.
The two adapter classes I just described and the base
YieldTermStructure class itself were used to implement interpolated
discount, zero-yield, and forward curves. Listing 3.6 outlines the
InterpolatedZeroCurve class template; the other two
implemented in the same way.
Listing 3.6: Outline of the
InterpolatedZeroCurve class template.
The template argument
Interpolator has a twofold task. On the one
hand, it acts as a traits class . It specifies the kind of
interpolation to be used as well as a few of its properties, namely,
how many points are required (e.g., at least two for a linear
interpolation) and whether the chosen interpolation is global (i.e.,
whether or not moving a data point changes the interpolation in
intervals that do not contain such point; this is the case, e.g., for
cubic splines). On the other hand, it doubles as a poor man’s factory;
when given a set of data points, it is able to build and return the
Interpolation instance. (The
will be described in a later post).
The public constructor takes the data needed to build the curve: the
set of dates over which to interpolate, the corresponding zero yields,
the day counter to be used, and an optional
instance. For most interpolations, the last parameter is not needed;
it can be passed when the interpolation needs parameters. The
implementation forwards to the parent
ZeroYieldStructure class the
first of the passed dates, assumed to be the reference date for the
curve, and the day counter; the other arguments are stored in the
corresponding data members. After performing a few consistency checks,
it converts the dates into times (using, of course, the passed
reference date and day counter), asks the interpolator to create an
Interpolation instance, and stores the result.
At this point, the curve is ready to be used. The other required
methods can be implemented as one-liners;
maxDate returns the latest
of the passed dates, and
zeroYieldImpl returns the interpolated
value of the zero yield. Since the
TermStructure machinery already
takes care of range-checking, the call to the
true argument. This causes the value to be extrapolated
if the passed time is outside the given range.
InterpolatedZeroCurve class also defines a few
protected constructors. They take the same arguments as the
constructors of its parent class
ZeroYieldStructure, as well as an
Interpolator instance, and forward them to the
corresponding parent-class constructors; however, they don’t create
the interpolation—they cannot, since they don’t take any
zero-yield data. These constructors are defined so that it is possible
to inherit from
InterpolatedZeroCurve; derived classes will provide
the data and create the interpolation based on whatever arguments they
take (an example of this will be shown in the remainder of this
section). For the same reason, most data members are declared as
mutable; this makes it possible for derived classes to update the
interpolation lazily, should their data change.
Aside: symmetry break.
You might argue that, as in George Orwell’s Animal Farm, some term
structures are more equal than others. The discount-based
implementation seems to have a privileged role, being used in the base
YieldTermStructure class. A more symmetric implementation might
define three abstract methods in the base class (
forwardImpl, to be called from the
corresponding public methods) and provide three adapters, adding a
DiscountStructure class to the existing ones.
Well, the argument is sound; in fact, the very first implementation of
YieldTermStructure class was symmetric. The switch to the
discount-based interface and the reasons thereof are now lost in the
mists of time, but might have to do with the use of
instances; since they can require changes of frequency or compounding,
zeroYield (to name one method) wouldn’t be allowed to return the
zeroYieldImpl directly anyway.
Aside: twin classes.
You might guess that code for interpolated discount and forward curves would be very similar to that for the interpolated zero-yield curve described here. The question naturally arises: would it be possible to abstract out common code? Or maybe we could even do with a single class template?
The answers are yes and no, respectively. Some code can be abstracted
in a template class (in fact, this has been done already). However,
the curves must implement three different abstract methods
zeroYieldImpl) so we still need
all three classes as well as the one containing the common code.
 N.C. Myers, Traits: a new and useful template technique. In The C++ Report, June 1995.