Chapter 3, part 2 of n: Yield term structures
Hello everybody.
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.
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
Yield Term Structures
The YieldTermStructure
class predates TermStructure
—in fact,
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
curve.
Interface and implementation
The interface of the YieldTermStructure
class is sketched in listing
3.3.
Listing 3.3: Partial interface of the YieldTermStructure
class.
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
protected 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 YieldTermStructure
class.
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
ZeroYieldStructure
and 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
interface of YieldTermStructure
into interfaces based on zero-yield
and instantaneous-forward rates, respectively.
Listing 3.5: Outline of the ZeroYieldStructure
and
ForwardRateStructure
classes.
The implementation of ZeroYieldStructure
is simple enough. A few
constructors (not shown here) forward their arguments to the
corresponding constructors in the parent YieldTermStructure
class. The Adapter pattern is implemented in the protected section: an
abstract 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
as 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
(InterpolatedForwardCurve
and InterpolatedDiscountCurve
) are
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 [1]. 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
corresponding Interpolation
instance. (The Interpolation
class
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 Interpolator
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 Interpolation
instance
includes a true
argument. This causes the value to be extrapolated
if the passed time is outside the given range.
Finally, the 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
optional 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 (discountImpl
,
zeroYieldImpl
, and 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
the 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 InterestRate
instances; since they can require changes of frequency or compounding,
zeroYield
(to name one method) wouldn’t be allowed to return the
result of 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
(discountImpl
, forwardImpl
, and zeroYieldImpl
) so we still need
all three classes as well as the one containing the common code.
Bibliography
[1] N.C. Myers, Traits: a new and useful template technique. In The C++ Report, June 1995.