Saturday, August 10, 2019

programming - How to sum interest rate curves in QuantLib


C++ code taken from Bonds.cpp and slightly amended:


#include 
#include
#include
#include

using namespace QuantLib;


int main(int, char* []) {

try {

// Just a couple of parameters

Calendar calendar = TARGET();
Date settlementDate(18, September, 2008);
settlementDate = calendar.adjust(settlementDate);
Integer fixingDays = 3;

Natural settlementDays = 3;
Date todaysDate = calendar.advance(settlementDate, -fixingDays, Days);
Settings::instance().evaluationDate() = todaysDate;

/* Now let's introduce two curves: the first one is a swap curve built from
deposits and IRS, the second one is a credit spread curve which should be
added to the former one to get a proper yield curve */

// Swap curve


DayCounter termStructureDayCounter = ActualActual(ActualActual::ISDA);
double tolerance = 1.0e-5;

// Deposits

Rate d1wQuote = 0.043375;
Rate d1mQuote = 0.031875;
Rate d3mQuote = 0.0320375;
Rate d6mQuote = 0.03385;
Rate d9mQuote = 0.0338125;

Rate d1yQuote = 0.0335125;

boost::shared_ptr d1wRate(new SimpleQuote(d1wQuote));
boost::shared_ptr d1mRate(new SimpleQuote(d1mQuote));
boost::shared_ptr d3mRate(new SimpleQuote(d3mQuote));
boost::shared_ptr d6mRate(new SimpleQuote(d6mQuote));
boost::shared_ptr d9mRate(new SimpleQuote(d9mQuote));
boost::shared_ptr d1yRate(new SimpleQuote(d1yQuote));

// IRS


Rate s2yQuote = 0.0295;
Rate s3yQuote = 0.0323;
Rate s5yQuote = 0.0359;
Rate s10yQuote = 0.0412;
Rate s15yQuote = 0.0433;

boost::shared_ptr s2yRate(new SimpleQuote(s2yQuote));
boost::shared_ptr s3yRate(new SimpleQuote(s3yQuote));
boost::shared_ptr s5yRate(new SimpleQuote(s5yQuote));

boost::shared_ptr s10yRate(new SimpleQuote(s10yQuote));
boost::shared_ptr s15yRate(new SimpleQuote(s15yQuote));

// Rate Helper

// Deposits
DayCounter depositDayCounter = Actual360();

boost::shared_ptr d1w(new DepositRateHelper(
Handle(d1wRate),

1*Weeks, fixingDays,
calendar, ModifiedFollowing,
true, depositDayCounter));
boost::shared_ptr d1m(new DepositRateHelper(
Handle(d1mRate),
1*Months, fixingDays,
calendar, ModifiedFollowing,
true, depositDayCounter));
boost::shared_ptr d3m(new DepositRateHelper(
Handle(d3mRate),

3*Months, fixingDays,
calendar, ModifiedFollowing,
true, depositDayCounter));
boost::shared_ptr d6m(new DepositRateHelper(
Handle(d6mRate),
6*Months, fixingDays,
calendar, ModifiedFollowing,
true, depositDayCounter));
boost::shared_ptr d9m(new DepositRateHelper(
Handle(d9mRate),

9*Months, fixingDays,
calendar, ModifiedFollowing,
true, depositDayCounter));
boost::shared_ptr d1y(new DepositRateHelper(
Handle(d1yRate),
1*Years, fixingDays,
calendar, ModifiedFollowing,
true, depositDayCounter));

// Setup IRS

Frequency swFixedLegFrequency = Annual;
BusinessDayConvention swFixedLegConvention = Unadjusted;
DayCounter swFixedLegDayCounter = Thirty360(Thirty360::European);
boost::shared_ptr swFloatingLegIndex(new Euribor6M);

const Period forwardStart(1*Days);

boost::shared_ptr s2y(new SwapRateHelper(
Handle(s2yRate), 2*Years,
calendar, swFixedLegFrequency,

swFixedLegConvention, swFixedLegDayCounter,
swFloatingLegIndex, Handle(),forwardStart));
boost::shared_ptr s3y(new SwapRateHelper(
Handle(s3yRate), 3*Years,
calendar, swFixedLegFrequency,
swFixedLegConvention, swFixedLegDayCounter,
swFloatingLegIndex, Handle(),forwardStart));
boost::shared_ptr s5y(new SwapRateHelper(
Handle(s5yRate), 5*Years,
calendar, swFixedLegFrequency,

swFixedLegConvention, swFixedLegDayCounter,
swFloatingLegIndex, Handle(),forwardStart));
boost::shared_ptr s10y(new SwapRateHelper(
Handle(s10yRate), 10*Years,
calendar, swFixedLegFrequency,
swFixedLegConvention, swFixedLegDayCounter,
swFloatingLegIndex, Handle(),forwardStart));
boost::shared_ptr s15y(new SwapRateHelper(
Handle(s15yRate), 15*Years,
calendar, swFixedLegFrequency,

swFixedLegConvention, swFixedLegDayCounter,
swFloatingLegIndex, Handle(),forwardStart));


// A depo-swap curve
std::vector > depoSwapInstruments;
depoSwapInstruments.push_back(d1w);
depoSwapInstruments.push_back(d1m);
depoSwapInstruments.push_back(d3m);
depoSwapInstruments.push_back(d6m);

depoSwapInstruments.push_back(d9m);
depoSwapInstruments.push_back(d1y);
depoSwapInstruments.push_back(s2y);
depoSwapInstruments.push_back(s3y);
depoSwapInstruments.push_back(s5y);
depoSwapInstruments.push_back(s10y);
depoSwapInstruments.push_back(s15y);
boost::shared_ptr depoSwapTermStructure(
new PiecewiseYieldCurve(
settlementDate, depoSwapInstruments,

termStructureDayCounter,
tolerance));

return 0;

} catch (std::exception& e) {
std::cerr << e.what() << std::endl;
return 1;
} catch (...) {
std::cerr << "unknown error" << std::endl;

return 1;
}
}

If I'm not wrong, so far we've been obtaining a kind of risk free discount curve.


Now let me to introduce an additional credit spread curve in the form of an object handled by RateHelper (kinda snippet to be inserted after the depo-swap curve of the code above):


        ...
Rate d1ySpread = 0.013;
Rate d2ySpread = 0.011;
Rate d3ySpread = 0.022;

Rate d6ySpread = 0.0238;
Rate d9ySpread = 0.0238;
Rate d10ySpread = 0.0239;

boost::shared_ptr d1ySpreadRate(new SimpleQuote(d1ySpread));
boost::shared_ptr d2ySpreadRate(new SimpleQuote(d2ySpread));
boost::shared_ptr d3ySpreadRate(new SimpleQuote(d3ySpread));
boost::shared_ptr d6ySpreadRate(new SimpleQuote(d6ySpread));
boost::shared_ptr d9ySpreadRate(new SimpleQuote(d9ySpread));
boost::shared_ptr d10ySpreadRate(new SimpleQuote(d10ySpread));


boost::shared_ptr credit1y(new SwapRateHelper(
Handle(d1ySpreadRate), 1*Years,
calendar, swFixedLegFrequency,
swFixedLegConvention, swFixedLegDayCounter,
swFloatingLegIndex, Handle(),forwardStart));
boost::shared_ptr credit2y(new SwapRateHelper(
Handle(d2ySpreadRate), 2*Years,
calendar, swFixedLegFrequency,
swFixedLegConvention, swFixedLegDayCounter,

swFloatingLegIndex, Handle(),forwardStart));
boost::shared_ptr credit3y(new SwapRateHelper(
Handle(d3ySpreadRate), 3*Years,
calendar, swFixedLegFrequency,
swFixedLegConvention, swFixedLegDayCounter,
swFloatingLegIndex, Handle(),forwardStart));
boost::shared_ptr credit6y(new SwapRateHelper(
Handle(d6ySpreadRate), 6*Years,
calendar, swFixedLegFrequency,
swFixedLegConvention, swFixedLegDayCounter,

swFloatingLegIndex, Handle(),forwardStart));
boost::shared_ptr credit9y(new SwapRateHelper(
Handle(d9ySpreadRate), 9*Years,
calendar, swFixedLegFrequency,
swFixedLegConvention, swFixedLegDayCounter,
swFloatingLegIndex, Handle(),forwardStart));
boost::shared_ptr credit10y(new SwapRateHelper(
Handle(d10ySpreadRate), 10*Years,
calendar, swFixedLegFrequency,
swFixedLegConvention, swFixedLegDayCounter,

swFloatingLegIndex, Handle(),forwardStart));

std::vector > creditInstruments;
creditInstruments.push_back(credit1y);
creditInstruments.push_back(credit2y);
creditInstruments.push_back(credit3y);
creditInstruments.push_back(credit6y);
creditInstruments.push_back(credit9y);
creditInstruments.push_back(credit10y);
boost::shared_ptr creditTermStructure(

new PiecewiseYieldCurve(
settlementDate, creditInstruments,
termStructureDayCounter,
tolerance));
...

The object creditTermStructure could represent, as instance, a CDS spread curve, or such a term structure.


Having both the depoSwapTermStructure and the creditTermStructure built by the code above, I would like to produce a new object of class YieldTermStructure by summing depoSwapTermStructure and creditTermStructure taking into account the misaligned time knots, that is, the code must sum the rates by the appropriate tenors (1Y depo-swap + 1Y credit spread, 2Y depo-swap + 2Y credit spread... and so on. As of the lacking credit spread maturities, such as 18Y, it should be interpolate known knots).


How is it possible to do such a thing using QuantLib?



Answer




There's no class at this time to add two curves as you want, but it won't be much difficult to write it.


The closest you'll get in the library is the ZeroSpreadedTermStructure class, that shows the general idea: it inherits from YieldTermStructure (by way of ZeroYieldStructure) takes a YieldTermStructure and a spread (constant, in this case) and override its own methods so that they return the sum of the two: for instance,


Rate ZeroSpreadedTermStructure::forwardImpl(Time t) const {
return originalCurve_->forwardRate(t, t, comp_, freq_, true)
+ spread_->value();
}

In your case, you'll have to write a similar class that takes two Handles to YieldTermStructure instead. Somewhat surprisingly, this will make your job easier. You can still take ZeroSpreadedTermStructure as a model for other tasks such as registering with the needed observers, but all you have to do in this case is inherit from YieldTermStructure directly and override the single discountImpl method:


DiscountFactor YourClass::discountImpl(Time t) const {
return baseCurve_->discount(t, true)

* spreadCurve_->discount(t, true);
}

since the sum of the rates implies the product of the corresponding discount factors.


If you need more information, a description of the YieldTermStructure hierarchy and the methods it implements (which is kind of longish to include here) is available at http://implementingquantlib.blogspot.com/2013/09/chapter-3-part-2-of-n-yield-term.html and later posts.


And of course, once you have your curve working, you're welcome to contribute it to QuantLib. The easier way would be to get a GitHub account and follow the instructions in the readme at https://github.com/lballabio/quantlib.


No comments:

Post a Comment

technique - How credible is wikipedia?

I understand that this question relates more to wikipedia than it does writing but... If I was going to use wikipedia for a source for a res...