Hello again.

This post starts a new series that will cover chapter 3 of my book. Most of the content was already available (even though I’ll review it a bit before posting, so there might be some revisions) but the chapter is still missing the last section. Hopefully, I’ll write it by the end of the series. (Suspense. That’s what makes you come back here again and again.)

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

CHANGE IS the only constant, as Heraclitus said. Paradoxically, the aphorism still holds after twenty-five centuries; also in quantitative finance, where practically all quantities obviously vary—sometimes spectacularly—over time.

This leads us straight to the subject of term structures. This chapter describes the basic facilities available for their construction, as well as a few existing term structures that can be used as provided.

The TermStructure class

The current base class for term structures is a fine example of design ex-post. After some thinking, you might come up with a specification for such class. When we started the library, we didn’t; we just started growing classes as we needed them. A couple of years later, older and somewhat wiser, we looked at the existing term structures and abstracted out their common features. The result is the TermStructure class as described in this section.

Interface and requirements

Once abstracted out, the base term-structure class (whose interface is shown in listing 3.1) was responsible for three basic tasks.

Listing 3.1: Interface of the TermStructure class.

    class TermStructure : public virtual Observer,
                          public virtual Observable,
                          public Extrapolator {
      public:
        TermStructure(const DayCounter& dc = DayCounter());
        TermStructure(const Date& referenceDate,
                      const Calendar& calendar = Calendar(),
                      const DayCounter& dc = DayCounter());
        TermStructure(Natural settlementDays,
                      const Calendar&,
                      const DayCounter& dc = DayCounter());
        virtual ~TermStructure();

        virtual DayCounter dayCounter() const;
        virtual Date maxDate() const = 0;
        virtual Time maxTime() const;
        virtual const Date& referenceDate() const;
        virtual Calendar calendar() const;
        virtual Natural settlementDays() const;
        Time timeFromReference(const Date& date) const;

        void update();
      protected:
        void checkRange(const Date&, bool extrapolate) const;
        void checkRange(Time, bool extrapolate) const;

        bool moving_;
    };

The first is to keep track of its own reference date, i.e., the date at which—in a manner of speaking—the future begins. (This is not strictly true of all term structures. However, we’ll leave it at that for the time being.) For a volatility term structure, that would most likely be today’s date. For a yield curve, it might be today’s date, too; but depending on the conventions used at one’s desk (for instance, an interest-rate swap desk whose deals are all settled spot; that’s on the second business day for you equity folks) the reference date might be the result of advancing today’s date by a few business days. Our term-structure class must be able to perform such a calculation if needed. Also, there might be cases in which the reference date is specified externally (such as when a sequence of dates, including the reference, is tabulated somewhere together with the corresponding discount factors). Finally, the calculation of the reference date might be altogether delegated to some other object; we’ll see such an arrangement in a later example. In all these cases, the reference date will be provided to client code by means of the referenceDate method. The related calendar and settlementDays methods return the calendar and the number of days used for the calculation (“settlement” applies to instruments, but is probably not the correct word for a term structure).

The second (and somewhat mundane) task is to convert dates to times, i.e., points on a real-valued time axis starting with t=0 at the reference date. Such times might be used in the mathematical model underlying the curve, or simply to convert, say, from discount factors to zero-yield rates. The calculation is made available by means of the timeFromReference method.

The third task (also a mundane one) is to check whether a given date or time belongs to the domain covered by the term structure. The TermStructure class delegates to derived classes the specification of the latest date in the domain—which must be implemented in the maxDate method—and provides a corresponding maxTime method as well as an overloaded checkRange method performing the actual test; there is no minDate method, as the domain is assumed to start at the reference date.

Implementation

The first task—keeping track of the reference date—starts when the term structure is instantiated.

Listing 3.2: Implementation of the TermStructure class.

   TermStructure::TermStructure(const DayCounter& dc)
   : moving_(false), updated_(true),
     settlementDays_(Null<Natural>()), dayCounter_(dc) {}

   TermStructure::TermStructure(const Date& referenceDate,
                                const Calendar& calendar,
                                const DayCounter& dc)
   : moving_(false), referenceDate_(referenceDate),
     updated_(true), settlementDays_(Null<Natural>()),
     calendar_(calendar), dayCounter_(dc) {}

   TermStructure::TermStructure(Natural settlementDays,
                                const Calendar& calendar,
                                const DayCounter& dc)
   : moving_(true), updated_(false),
     settlementDays_(settlementDays),
     calendar_(calendar), dayCounter_(dc) {
       registerWith(Settings::instance().evaluationDate());
   }

   DayCounter TermStructure::dayCounter() const {
       return dayCounter_;
   }

   Time TermStructure::maxTime() const {
       return timeFromReference(maxDate());
   }

   const Date& TermStructure::referenceDate() const  {
       if (!updated_) {
           Date today = Settings::instance().evaluationDate();
           referenceDate_ =
               calendar().advance(today,settlementDays_,Days);
           updated_ = true;
       }
       return referenceDate_;
   }

   Calendar TermStructure::calendar() const {
       return calendar_;
   }

   Natural TermStructure::settlementDays() const {
       return settlementDays_;
   }

   Time TermStructure::timeFromReference(const Date& d) const {
       return dayCounter().yearFraction(referenceDate(),d);
   }

   void TermStructure::update() {
       if (moving_)
           updated_ = false;
       notifyObservers();
   }

   void TermStructure::checkRange(const Date& d,
                                  bool extrapolate) const {
       checkRange(timeFromReference(d),extrapolate);
   }

   void TermStructure::checkRange(Time t,
                                  bool extrapolate) const {
       QL_REQUIRE(t >= 0.0,
                  "negative time (" << t << ") given");
       QL_REQUIRE(extrapolate || allowsExtrapolation()
                  || t <= maxTime(),
                  "time (" << t
                  << ") is past max curve time ("
                  << maxTime() << ")");
   }

Depending on how the reference date is to be calculated, different constructors must be called. All such constructors set two boolean data members. The first is called moving_; it is set to true if the reference date moves when today’s date changes, or to false if the date is fixed. The second, updated_, specifies whether the value of another data member (referenceDate_, storing the latest calculated value of the reference date) is currently up to date or should be recalculated.

Three constructors are available. One simply takes a day counter (used for time calculations, as we will see later) but no arguments related to reference-date calculation. Of course, the resulting term structure can’t calculate such date; therefore, derived classes calling this constructor must take care of the calculation by overriding the virtual referenceDate method. The implementation sets moving_ to false and updated_ to true to inhibit calculations in the base class.

Another constructor takes a date, as well as an optional calendar and a day counter. When this one is used, the reference date is assumed to be fixed and equal to the given date. Accordingly, the implementation sets referenceDate_ to the passed date, moving_ to false, and updated_ to true.

Finally, a third constructor takes a number of days and a calendar. When this one is used, the reference date will be calculated as today’s date advanced by the given number of business days according to the given calendar. Besides copying the passed data to the corresponding data members, the implementation sets moving_ to true and updated_ to false (since no calculation is performed at this time). However, that’s not the full story; if today’s date changes, the term structure must be notified so that it can update its reference date. The Settings class (described elsewhere) provides global access to the current evaluation date, with which the term structure registers as an observer. When a change is notified, the update method is executed. If the reference date is moving, the body of the method sets updated_ to false before forwarding the notification to the term structure’s own observers.

Apart from trivial inspectors such as the calendar method, the implementation of the first task is completed with the referenceDate method. If the reference date needs to be calculated, it does so by retrieving the current evaluation date, advancing it as specified, and storing the result in the referenceDate_ data member before returning it.

The second task is much simpler, since the conversion of dates into times can be delegated entirely to a DayCounter instance. Such day counter is usually passed to the term structure as a constructor argument and stored in the dayCounter_ data member. The conversion is handled by the timeFromReference method, which asks the day counter for the number of years between the reference date and the passed date. Note that, in the body of the method, both the day counter and the reference date are accessed by means of the corresponding methods rather than the data members. This is necessary, since—as I mentioned earlier—the referenceDate method can be overridden entirely and thus disregard the data member; the same applies to the dayCounter method.

You might object that this is, to use the term coined by Kent Beck [1], a code smell. A term-structure instance might store a day counter or a reference date (or likely both) that don’t correspond to the actual ones used by its methods. This disturbs me as well; and indeed, earlier versions of the class declared the dayCounter method as purely virtual and did not include the data member. However, it is a necessary evil in the case of the reference date, since we need a data member to cache its calculated value. Due to the broken-window effect [2], the day counter, calendar and settlement days followed (after a period in which we developed a number of derived term structures, all of which had to define the same data members).

What day counter should be used for a given term structure? Fortunately, it doesn’t matter much. If one is only working with dates (i.e., provides dates as an input for the construction of the term structure and uses dates as arguments to retrieve values) the effects of choosing a specific day counter will cancel out as long as the day counter is sufficiently well behaved: for instance, if it is homogeneous (by which I mean that the time T(d1,d2) between two dates d1 and d2 equals the time T(d3,d4) between d3 and d4 if the two pairs of dates differ by the same number of days) and additive (by which I mean that T(d1,d2) + T(d2,d3) equals T(d1,d3) for all choices of the three dates). Two such day counters are the actual/360 and the actual/365-fixed ones. Similarly, if one is only working with times, the day counter will not be used at all.

Onwards with the third and final task. The job of defining the valid date range is delegated to derived classes, which must define the maxDate method (here declared as purely virtual). The corresponding time range is calculated by the maxTime method, which simply converts the latest valid date to time by means of the timeFromReference method; this, too, can be overridden. Finally, the two checkRange methods implement the actual range checking and throw an exception if the passed argument is not in the valid range; the one that takes a date does so by forwarding the request to the other after converting the given date to a time. The check can be overridden by a request to extrapolate outside the domain of the term-structure; this can be done either by passing an optional boolean argument to checkRange or by using the facilities provided by the Extrapolator class from which TermStructure inherits. Extrapolation is only allowed beyond the maximum date; requests for dates before the reference date are always rejected.

Aside: evaluation date tricks.

If no evaluation date is set, the Settings class defaults to returning today’s date. Unfortunately, the latter will change silently (that is, without notifying its observers) at the strike of midnight, causing mysterious errors. If you run overnight calculations, you’ll have to perform the same feat as Hiro Nakamura in Heroes—freeze time. Explicitly settings today’s date as the evaluation date will keep it fixed, even when today becomes tomorrow.

Another trick worth knowing: if all your term structures are moving, setting the evaluation date to tomorrow and recalculating the value of your instruments while keeping everything else unchanged will give you the daily theta of your portfolio.

Bibliography

[1] M. Fowler, K. Beck, J. Brant, W. Opdyke and D. Roberts, Refactoring: Improving the Design of Existing Code. Addison-Wesley, 1999.
[2] A. Hunt and D. Thomas, The Pragmatic Programmer: From Journeyman to Master. Addison-Wesley, 1999.