Yield Curve in Quantlib

Posted by Grant6899 on November 25, 2017

What is a yield curve?

Yield curve is the term structure of intrest rate. In other words, it shows the relationship between time to maturity and interest rates.

Base class: TermStructure

Overview

TermStructure is derived from Observer, Observable and Extrapolator.

  • Observer: it’s observing evaluation date in global settings
  • Observable: it’s observed by instrments whose valuation depending on it
  • Extrapolator: base class for classes possibly allowing extrapolation, contains a bool extrapolate_ to indicate if extrapolation is allowed

Source code

class TermStructure : public virtual Observer,
                      public virtual Observable,
                      public Extrapolator {
      public:
		// term structures initialized by means of this constructor must manage their own reference date by overriding the referenceDate() method
        explicit TermStructure(const DayCounter& dc = DayCounter());
        
        //! initialize with a fixed reference date
        explicit TermStructure(const Date& referenceDate, const Calendar& calendar = Calendar(), const DayCounter& dc = DayCounter());
        
        //! calculate the reference date based on the global evaluation date
        TermStructure(Natural settlementDays,
                      const Calendar&,
                      const DayCounter& dc = DayCounter());
        virtual ~TermStructure() {}
        
        //! the day counter used for date/time conversion
        virtual DayCounter dayCounter() const;
        //! date/time conversion
        Time timeFromReference(const Date& date) const;
        //! the latest date for which the curve can return values
        virtual Date maxDate() const = 0;
        //! the latest time for which the curve can return values
        virtual Time maxTime() const;
        //! the date at which discount = 1.0 and/or variance = 0.0
        virtual const Date& referenceDate() const;
        //! the calendar used for reference and/or option date calculation
        virtual Calendar calendar() const;
        //! the settlementDays used for reference date calculation
        virtual Natural settlementDays() const;
        
        void update();
        
      protected:
        //! date-range check
        void checkRange(const Date& d,
                        bool extrapolate) const;
        //! time-range check
        void checkRange(Time t,
                        bool extrapolate) const;
        bool moving_;
        mutable bool updated_;
        Calendar calendar_;
      private:
        mutable Date referenceDate_;
        Natural settlementDays_;
        DayCounter dayCounter_;
    };

    // inline definitions

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

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

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

    inline Natural TermStructure::settlementDays() const {
        QL_REQUIRE(settlementDays_!=Null<Natural>(),
                   "settlement days not provided for this instance");
        return settlementDays_;
    }

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

Analysis

Reference date: the date at which discount = 1.0 and/or variance = 0.0.

Three ways to track Reference date:

  • Reference date is fixed, the constructor takes a date is to be used; the default implementation of referenceDate() will then return such date
  • Determined by advancing the current date (evaluation date in global setting) by several business days, the constructor takes a number of days and a calendar is to be used
  • Reference date is based on that of some other structure, the referenceDate() method must be overridden in derived classes so that it fetches and return the appropriate date

Its update() is not defined in the class, waiting for overwriting in derived classes.

Intermediate class: YieldTermStructure

Source Code

class YieldTermStructure : public TermStructure {
public:
	YieldTermStructure(const DayCounter& dc = DayCounter(),
                           const std::vector<Handle<Quote> >& jumps = std::vector<Handle<Quote> >(),
                           const std::vector<Date>& jumpDates = std::vector<Date>());
                           
    YieldTermStructure(const Date& referenceDate,
                           const Calendar& cal = Calendar(),
                           const DayCounter& dc = DayCounter(),
                           const std::vector<Handle<Quote> >& jumps = std::vector<Handle<Quote> >(),
                           const std::vector<Date>& jumpDates = std::vector<Date>());
                           
    YieldTermStructure(Natural settlementDays,
                           const Calendar& cal,
                           const DayCounter& dc = DayCounter(),
                           const std::vector<Handle<Quote> >& jumps = std::vector<Handle<Quote> >(),
                           const std::vector<Date>& jumpDates = std::vector<Date>());

		/*! \name Discount factors

            These methods return the discount factor from a given date or time
            to the reference date.  In the latter case, the time is calculated
            as a fraction of year from the reference date.
        */
        //@{
        DiscountFactor discount(const Date& d,
                                bool extrapolate = false) const;
        /*! The same day-counting rule used by the term structure
            should be used for calculating the passed time t.
        */
        DiscountFactor discount(Time t,
                                bool extrapolate = false) const;
        //@}

        /*! \name Zero-yield rates

            These methods return the implied zero-yield rate for a
            given date or time.  In the former case, the time is
            calculated as a fraction of year from the reference date.
        */
        //@{
        /*! The resulting interest rate has the required daycounting
            rule.
        */
        InterestRate zeroRate(const Date& d,
                              const DayCounter& resultDayCounter,
                              Compounding comp,
                              Frequency freq = Annual,
                              bool extrapolate = false) const;

        /*! The resulting interest rate has the same day-counting rule
            used by the term structure. The same rule should be used
            for calculating the passed time t.
        */
        InterestRate zeroRate(Time t,
                              Compounding comp,
                              Frequency freq = Annual,
                              bool extrapolate = false) const;
        //@}

        /*! \name Forward rates

            These methods returns the forward interest rate between two dates
            or times.  In the former case, times are calculated as fractions
            of year from the reference date.

            If both dates (times) are equal the instantaneous forward rate is
            returned.
        */
        //@{
        /*! The resulting interest rate has the required day-counting
            rule.
        */
        InterestRate forwardRate(const Date& d1,
                                 const Date& d2,
                                 const DayCounter& resultDayCounter,
                                 Compounding comp,
                                 Frequency freq = Annual,
                                 bool extrapolate = false) const;
        /*! The resulting interest rate has the required day-counting
            rule.
            \warning dates are not adjusted for holidays
        */
        InterestRate forwardRate(const Date& d,
                                 const Period& p,
                                 const DayCounter& resultDayCounter,
                                 Compounding comp,
                                 Frequency freq = Annual,
                                 bool extrapolate = false) const;

        /*! The resulting interest rate has the same day-counting rule
            used by the term structure. The same rule should be used
            for calculating the passed times t1 and t2.
        */
        InterestRate forwardRate(Time t1,
                                 Time t2,
                                 Compounding comp,
                                 Frequency freq = Annual,
                                 bool extrapolate = false) const;
        //@}

        //! \name Jump inspectors
        //@{
        const std::vector<Date>& jumpDates() const;
        const std::vector<Time>& jumpTimes() const;
        //@}

        //! \name Observer interface
        //@{
        void update();
        //@}
      protected:
        /*! \name Calculations

            This method must be implemented in derived classes to
            perform the actual calculations. When it is called,
            range check has already been performed; therefore, it
            must assume that extrapolation is required.
        */
        //@{
        //! discount factor calculation
        virtual DiscountFactor discountImpl(Time) const = 0;
        //@}
      private:
        // methods
        void setJumps();
        // data members
        std::vector<Handle<Quote> > jumps_;
        std::vector<Date> jumpDates_;
        std::vector<Time> jumpTimes_;
        Size nJumps_;
        Date latestReference_;
    };

    // inline definitions

    inline
    DiscountFactor YieldTermStructure::discount(const Date& d,
                                                bool extrapolate) const {
        return discount(timeFromReference(d), extrapolate);
    }

    inline
    InterestRate YieldTermStructure::forwardRate(const Date& d,
                                                 const Period& p,
                                                 const DayCounter& dayCounter,
                                                 Compounding comp,
                                                 Frequency freq,
                                                 bool extrapolate) const {
        return forwardRate(d, d+p, dayCounter, comp, freq, extrapolate);
    }

    inline const std::vector<Date>& YieldTermStructure::jumpDates() const {
        return this->jumpDates_;
    }

    inline const std::vector<Time>& YieldTermStructure::jumpTimes() const {
        return this->jumpTimes_;
    }

}

Analysis

Note that same day count convention as parent term structure is used for discount factor calculation, daycounter_ is protected in term structrue class.

Discount factor, zero rate and forward rate can be dereived among each other. So the implementation of zero rate is calling discount() to calculate compond then use InterestRate::ImpliedRate(), so does forwardRate().

You should always define discountImpl(t), which is called in discount(), in a derived class to make it work, since it’s a pure virtual function here in yieldtermstructure. In other words, yieldtermstructure is an abstract class.

Children classes

There are various ways to construct a market consistent yield curve. The methodology depends on the liquidity of available market instruments for the corresponding market. Several choices have to be made; the interpolation procedure and the choice of the market instruments have to be specified.

QuantLib allows to construct a yield curve as:

  • InterpolatedDiscountCurve, construction given discount factors
  • InterpolatedZeroCurve, construction given zero coupon bond rates
  • InterpolatedForwardCurve, construction given forward rates
  • FittedBondDiscountCurve, construction given coupon bond prices
  • PiecewiseYieldCurve, piecewise construction given a mixture of market instruments (i.e. deposit rates, FRA/Future rates, swap rates).

All of the above constructors derive from the base class YieldTermStructure. The YieldTermStructure class derives from TermStructure, which is both, an Observer and Observable. This base class implements some useful functions. For example, functions are implemented which return the reference date, day counter, calendar, the minimum or maximum date for which the curve returns yields.

InterpolatedDiscountCurve

Source Code:

    template <class Interpolator>
    class InterpolatedDiscountCurve
        : public YieldTermStructure,
          protected InterpolatedCurve<Interpolator> {
      public:
        InterpolatedDiscountCurve(
            const std::vector<Date>& dates,
            const std::vector<DiscountFactor>& dfs,
            const DayCounter& dayCounter,
            const Calendar& cal = Calendar(),
            const std::vector<Handle<Quote> >& jumps = std::vector<Handle<Quote> >(),
            const std::vector<Date>& jumpDates = std::vector<Date>(),
            const Interpolator& interpolator = Interpolator());
        InterpolatedDiscountCurve(
            const std::vector<Date>& dates,
            const std::vector<DiscountFactor>& dfs,
            const DayCounter& dayCounter,
            const Calendar& calendar,
            const Interpolator& interpolator);
        InterpolatedDiscountCurve(
            const std::vector<Date>& dates,
            const std::vector<DiscountFactor>& dfs,
            const DayCounter& dayCounter,
            const Interpolator& interpolator);
        //! \name TermStructure interface
        //@{
        Date maxDate() const;
        //@}
        //! \name other inspectors
        //@{
        const std::vector<Time>& times() const;
        const std::vector<Date>& dates() const;
        const std::vector<Real>& data() const;
        const std::vector<DiscountFactor>& discounts() const;
        std::vector<std::pair<Date, Real> > nodes() const;
        //@}
      protected:
        InterpolatedDiscountCurve(
            const DayCounter&,
            const std::vector<Handle<Quote> >& jumps = std::vector<Handle<Quote> >(),
            const std::vector<Date>& jumpDates = std::vector<Date>(),
            const Interpolator& interpolator = Interpolator());
        InterpolatedDiscountCurve(
            const Date& referenceDate,
            const DayCounter&,
            const std::vector<Handle<Quote> >& jumps = std::vector<Handle<Quote> >(),
            const std::vector<Date>& jumpDates = std::vector<Date>(),
            const Interpolator& interpolator = Interpolator());
        InterpolatedDiscountCurve(
            Natural settlementDays,
            const Calendar&,
            const DayCounter&,
            const std::vector<Handle<Quote> >& jumps = std::vector<Handle<Quote> >(),
            const std::vector<Date>& jumpDates = std::vector<Date>(),
            const Interpolator& interpolator = Interpolator());
        //! \name YieldTermStructure implementation
        //@{
        DiscountFactor discountImpl(Time) const;
        //@}
        mutable std::vector<Date> dates_;
      private:
        void initialize();
    };

Analysis

When objects intialized, dates will be passed and converted into time vector, then gets involved into the calculation of interpolation.

nodes() are calculated on the surface of this class, it’s paired by dates_ and data_, and is not part of low-level calculation.