## Bootstrapping with QuantLib

Key words: Interest rates, Day-count basis, Quantlib, Bootstraping, IMM dates, Cash Rate, Eurodollars, Swaps, Interpolation, Curve constraction, Date adjustment, Spot curve, Forward curve, Discounts

In my previous article Continuously compounded linear interpolation of interest rates , I described the procedure of building spot and forward interest rate curves and a bootstrapping as a standard method of constructing the curve. However, bootstraping is not as easy as it seems, especially, when doing it manually in Excel. A lot of details needed to be taken into account:

Day-count conventions
• Interpolation methods
• Calendars
• Compounding methods

These features are realized and available in Quantlib, so I thought it would be nice to use Quantlib to show how bootstrapping can be done.
Our actual results we compare with market quotes. Also, I will provide example of how to derive discounts and forward rates from market instruments by using Quantlib.

Market Data part

A curve we going to deal with represents US-dollar interest rate swaps, YCSW0047 Index in Bloomberg terminal:

• The short end is cash rates with Actual/360 day-count
• In the middle are 6 contiguous 3 month IMM Eurodollar futures
• On the long end of the curve are fixed-rate vs. floating-rate swaps

Fixed rate leg is based on Annual, Act/360 day-count; floating rate leg has quarterly (for instance, the
benchmark floating index for the US swap market is the 3 month LIBOR), Act/360 day-count basis.

The rest of the market data are presented in the “Market Table” below, arranged by the traded instruments into increasing maturity with corresponding conventions.
The quotes in green are derived by using Quantlib and the rest of the article will show how to do this.
As we can see, these quotes can be compared with the column “Spot Rate” to see the difference between the Market and Quantlib.

Setup and Calculation part

1. Interpolation

First of all, we need to take care about an interpolation method, the generation of a complete yield curve from the market data that is available.
In practice, linear and non-linear (regression, spline etc.) are the most common interpolation practices used. We will stay with a simple linear technique.
A list of interpolation types in Quantlib, including two dimensional “bi” types:

• ql/math/interpolations/abcdinterpolation.hpp
• ql/math/interpolations/backwardflatinterpolation.hpp
• ql/math/interpolations/bicubicsplineinterpolation.hpp
• ql/math/interpolations/bilinearinterpolation.hpp
• ql/math/interpolations/convexmonotoneinterpolation.hpp
• ql/math/interpolations/cubicinterpolation.hpp
• ql/math/interpolations/extrapolation.hpp
• ql/math/interpolations/flatextrapolation2d.hpp
• ql/math/interpolations/forwardflatinterpolation.hpp
• ql/math/interpolations/interpolation2d.hpp
• ql/math/interpolations/kernelinterpolation.hpp
• ql/math/interpolations/kernelinterpolation2d.hpp
• ql/math/interpolations/linearinterpolation.hpp
• ql/math/interpolations/mixedinterpolation.hpp
• ql/math/interpolations/multicubicspline.hpp
• ql/math/interpolations/sabrinterpolation.hpp

2. Method of curve construction

We will use Piecewise yield curve constriction, i.e. we build the curve by using different market instrument.
In our case, these instruments are Cash, ED Futures and Swaps.
Quantlib allows building a yield curve as:
• InterpolatedDiscountCurve (discount factors are taken as input)
• InterpolatedZeroCurve (zero coupon bond rates are taken as input)
• FittedBondDiscountCurve (coupon bond prices are taken as input)
• InterpolatedForwardCurve (forward rates are taken as input)
• PiecewiseYieldCurve (a mixture of market instruments are taken as input)

3. Dates
Before wring the code, we need to know more details about specific dates.

3.1. Our calendar is

Calendar calendar = JointCalendar(UnitedKingdom(UnitedKingdom::Exchange),UnitedStates(UnitedStates::Settlement), JoinHolidays);

3.2 The default Spot date, aka Settlement Date is:

Date settlementDate(18, February, 2015);

3.3 Trade date is 2 days prior to the settlement day:

Date todaysDate = calendar.advance(settlementDate, -fixingDays, Days);

3.4 We need to set up our Trade date in the system for the future valuation:

Settings::instance().evaluationDate() = todaysDate;

Otherwise, you will probably have an error on the next day:

QL_FAIL(io::ordinal(iteration+1) << " iteration: failed at " << io::ordinal(i) << " alive instrument, " "maturity " << errors_[i]->helper()->latestDate()<< ", reference date " << ts_->dates_[0] <<": " << e.what());):

3.5 The function «advance» provides methods for determining whether a date is abusiness day or a holiday for a given market, and for incrementing/decrementing a date of a given number of business days:

Date todaysDate = calendar.advance(settlementDate, -fixingDays, Days);

3.6 Date adjustment are used whenever a transaction date falls on a date that is not a
business day: we use ModifiedFollowing roll convention.

Quantlib provides the following business day conventions:

ISDA

• Following, choose the first business day after the given holiday.
• ModifiedFollowing, choose the first business day after the given holiday unless it belongs to a different month, in which case choose the first business day before the holiday.
• Preceding, choose the first business day before the given holiday.
NONE ISDA

• ModifiedPreceding, choose the first business day before the given holiday unless it belongs to a different month, in which case choose the first business day after the holiday.

3.7 Day-count basis

The last but not the least is Day-count basis that specifies the length of year for which the interest accrues. Quantlib offers the following Day-count conventions:

• ql/time/daycounters/actual360.hpp
• ql/time/daycounters/actual365fixed.hpp
• ql/time/daycounters/actual365nl.hpp
• ql/time/daycounters/actualactual.hpp
• ql/time/daycounters/one.hpp
• ql/time/daycounters/simpledaycounter.hpp
• ql/time/daycounters/thirty360.hpp

By using “dayCount” and “yearFraction” functions, it is possible to construct the other conventions in Quantlib.

4. Quotes set up
Now we are ready to input market quotes take from our previous “Market Table” above.

4.1 Cash Rates

Rate d1wQuote = 0.001375;
Rate d1mQuote = 0.001717;
Rate d2mQuote = 0.002112;
Rate d3mQuote = 0.002581;

Every day at 11:00 am London time, British bankers Association polls various dealers for cash deposit rates that are simple (without compounding) quoted on Act/360 basis. 1W, 1M, 2 M and 3M quotes are used in our bootstrapping process.

4.2 Eurodollars are traded in the International Money Market pit with an implied futures rate 100-price.

Real fut1Quote = 99.725; // 100-99.725=0.2750 is the future rate
Real fut2Quote = 99.585; // 0.4150
Real fut3Quote = 99.385; //0.6150
Real fut4Quote = 99.16; // 0.84
Real fut5Quote = 98.93; // 1.07
Real fut6Quote = 98.715; // 1.285

For simplicity purpose and taking into account the maturities of EDs, I skip convexity adjustment for futures; more details can be found in my article Interest rates: Futures and forward convexity adjustment.

If уou would like to play around with adjustment, you may set up the code this way (0.93% is Market rate volatility):

Rate T = depositDayCounter.yearFraction(settlementDate, imm);
Real convexityQuote= 0.5*0.00937*0.00937*3*T;
boost::shared_ptr&ltQuote> convexity(new SimpleQuote(convexityQuote));
boost::shared_ptr&ltRateHelper> fut5(new FuturesRateHelper(Handle&ltQuote>(fut5Price), imm, futMonths, calendar, ModifiedFollowing, true, depositDayCounter, Handle&ltQuote>(convexity)));

A very useful feature of Quantlib is IMM dates identifying the four quarterly dates of each year which ED contracts use as their scheduled maturity date:

Date imm = IMM::nextDate(settlementDate);

4.3 Par swap rates (a par swap is a swap whose market value today is zero and its par swap rate is the fixed rate of this swap) for the following maturities are used:

Rate s2yQuote = 0.0089268;
Rate s3yQuote = 0.0123343;
Rate s4yQuote = 0.0147985;
Rate s5yQuote = 0.0165843;
Rate s6yQuote = 0.0179191;

We also pay attention to the specification of our swaps as specified in the YCSW0047 Index:

Frequency swFixedLegFrequency = Annual;
DayCounter swFixedLegDayCounter = Actual360();
boost::shared_ptr&ltIborIndex> swFloatingLegIndex(new USDLibor(Period(3, Months)));

5. The collection of interest rates for all different terms is the term structure or simply the curve:

DayCounter termStructureDayCounter = Actual360();
boost::shared_ptr&ltYieldTermStructure> depoFutSwapTermStructure(new PiecewiseYieldCurve&ltDiscount,Linear>(settlementDate, depoFutSwapInstruments, termStructureDayCounter, 1.0e-15));

6. Now we are ready to extract the curve.

6.1 Zero rates by using the function:

depoFutSwapTermStructure->zeroRate(matDate1, depositDayCounter, Simple) << std::endl;

6.2 Discount by using the function:

depoFutSwapTermStructure->discount(matDate14) << std::endl;

6.3 Forward rates by using the function:

depoFutSwapTermStructure->forwardRate(matDate13,matDate14,FutDayCounter,Simple)

The output is the window where:

• The first number is taken in the column “Spot rate” from the “Market Table” with market data in the beginning of the article.

• The second number is our zero rate that we compare with the first number to ensure we received more or less the same results as market quoted.

• Discount rate and forward rate are also computed for the matDate13(19, February, 2019) and matDate14(18, February, 2020) dates.

One Picture is Worth a Thousand Words:

Quantlib code is here

ПОСЛЕДНИЕ СТАТЬИ
РУБРИКИ