20#include <boost/make_shared.hpp>
23#include <boost/test/unit_test.hpp>
24#include <boost/test/data/test_case.hpp>
26#include <ql/cashflows/cashflows.hpp>
27#include <ql/cashflows/fixedratecoupon.hpp>
28#include <ql/cashflows/iborcoupon.hpp>
29#include <ql/indexes/ibor/euribor.hpp>
30#include <ql/instruments/bond.hpp>
31#include <ql/quotes/simplequote.hpp>
32#include <ql/termstructures/credit/flathazardrate.hpp>
33#include <ql/termstructures/volatility/swaption/swaptionconstantvol.hpp>
34#include <ql/termstructures/yield/flatforward.hpp>
35#include <ql/time/calendars/target.hpp>
36#include <ql/time/schedule.hpp>
47 std::string testLabel;
48 Real benchmarkRate, defaultSpread, securitySpread, oisRate, euriborRate, bondFixing;
52std::ostream&
operator<<(std::ostream& o,
const TestDatum& d) {
53 return o <<
"[" << d.testLabel <<
": benchmarkRate=" << d.benchmarkRate <<
", defaultSpread=" << d.defaultSpread
54 <<
", securitySpread=" << d.securitySpread <<
", oisRate=" << d.oisRate
55 <<
", euriborRate=" << d.euriborRate <<
", bondFixing=" << d.bondFixing <<
", seasoned=" << std::boolalpha
59TestDatum testData[] = {{
"singleCurve, zero credit spread, no sec spread", 0.03, 0.00, 0.00, 0.02, 0.02, 1.10,
false},
60 {
"multiCurve, zero credit spread, no sec spread", 0.02, 0.00, 0.01, 0.02, 0.02, 1.10,
false},
61 {
"singleCurve, pos credit spread, no sec spread", 0.02, 0.01, 0.00, 0.02, 0.02, 1.07,
false},
62 {
"multiCurve, pos credit spread, no sec spread", 0.02, 0.01, 0.00, 0.01, 0.02, 1.07,
false},
63 {
"singleCurve, pos credit spread, pos sec spread", 0.02, 0.01, 0.01, 0.02, 0.02, 1.025,
false},
64 {
"multiCurve, pos credit spread, pos sec spread", 0.02, 0.01, 0.01, 0.01, 0.02, 1.025,
false},
65 {
"singleCurve, zero credit spread, no sec spread", 0.02, 0.00, 0.00, 0.02, 0.02, 1.10,
true},
66 {
"multiCurve, zero credit spread, no sec spread", 0.02, 0.00, 0.00, 0.01, 0.02, 1.10,
true},
67 {
"singleCurve, pos credit spread, no sec spread", 0.02, 0.01, 0.00, 0.02, 0.02, 1.07,
true},
68 {
"multiCurve, pos credit spread, no sec spread", 0.02, 0.01, 0.00, 0.01, 0.02, 1.07,
true},
69 {
"singleCurve, pos credit spread, pos sec spread", 0.02, 0.01, 0.01, 0.02, 0.02, 1.025,
true},
70 {
"multiCurve, pos credit spread, pos sec spread", 0.02, 0.01, 0.01, 0.01, 0.02, 1.025,
true}};
76BOOST_AUTO_TEST_SUITE(BondTRSTest)
80 BOOST_TEST_MESSAGE(
"Testing QuantExt Bond TRS pricing.");
82 Settings::instance().evaluationDate() = Date(5, Feb, 2016);
83 Date today = Settings::instance().evaluationDate();
84 Calendar calendar = TARGET();
87 Handle<Quote> rateQuote(QuantLib::ext::make_shared<SimpleQuote>(data.benchmarkRate));
88 Handle<Quote> issuerSpreadQuote(QuantLib::ext::make_shared<SimpleQuote>(data.defaultSpread));
89 DayCounter dc = Actual365Fixed();
90 Handle<YieldTermStructure> yieldCurve(QuantLib::ext::make_shared<FlatForward>(today, rateQuote, dc, Compounded, Annual));
91 Handle<DefaultProbabilityTermStructure> defaultCurve(
92 QuantLib::ext::make_shared<FlatHazardRate>(today, issuerSpreadQuote, dc));
93 Handle<Quote> bondSpecificSpread(QuantLib::ext::make_shared<SimpleQuote>(data.securitySpread));
94 Handle<Quote> recoveryRateQuote(QuantLib::ext::make_shared<SimpleQuote>(0.4));
97 Handle<Quote> oisQuote(QuantLib::ext::make_shared<SimpleQuote>(data.oisRate));
98 Handle<YieldTermStructure> oisCurve(QuantLib::ext::make_shared<FlatForward>(today, oisQuote, dc, Compounded, Annual));
99 Handle<Quote> iborQuote(QuantLib::ext::make_shared<SimpleQuote>(data.euriborRate));
100 Handle<YieldTermStructure> iborCurve(QuantLib::ext::make_shared<FlatForward>(today, iborQuote, dc, Compounded, Annual));
103 Date startDate = data.seasoned ? today - 3 * Months : TARGET().advance(today, 2 * Days);
104 Date endDate = startDate + Period(5, Years);
105 BusinessDayConvention bdc = Following;
106 BusinessDayConvention bdcEnd = bdc;
107 DateGeneration::Rule rule = DateGeneration::Forward;
108 bool endOfMonth =
false;
109 Date firstDate, lastDate;
110 Schedule schedule(startDate, endDate, 1 * Years, calendar, bdc, bdcEnd, rule, endOfMonth, firstDate, lastDate);
111 Real redemption = 100.0;
112 Real couponRate = 0.04;
114 FixedRateLeg(schedule).withNotionals(redemption).withCouponRates(couponRate, dc).withPaymentAdjustment(bdc);
116 QuantLib::ext::shared_ptr<QuantLib::Bond> bond(QuantLib::ext::make_shared<QuantLib::Bond>(0, calendar, startDate, leg));
119 std::string secId =
"SECURITY";
120 QuantLib::ext::shared_ptr<QuantExt::BondIndex> bondIndex = QuantLib::ext::make_shared<QuantExt::BondIndex>(
121 secId,
false,
false, NullCalendar(), bond, yieldCurve, defaultCurve, recoveryRateQuote, bondSpecificSpread,
122 Handle<YieldTermStructure>(),
false);
123 Date bondFixingDate(5, Nov, 2015);
124 bondIndex->addFixing(bondFixingDate, data.bondFixing);
127 Period timeStep = 1 * Months;
128 QuantLib::ext::shared_ptr<PricingEngine> bondEngine(QuantLib::ext::make_shared<QuantExt::DiscountingRiskyBondEngine>(
129 yieldCurve, defaultCurve, recoveryRateQuote, bondSpecificSpread, timeStep));
130 bond->setPricingEngine(bondEngine);
133 Schedule floatingSchedule(startDate, endDate, 6 * Months, calendar, bdc, bdcEnd, rule, endOfMonth, firstDate,
135 QuantLib::ext::shared_ptr<IborIndex> iborIndex = QuantLib::ext::make_shared<Euribor>(6 * Months, iborCurve);
136 Leg fundingLeg = IborLeg(schedule, iborIndex).withNotionals(redemption);
137 Date iborFixingDate(3, Nov, 2015);
138 iborIndex->addFixing(iborFixingDate, 0.03);
139 Leg fundingNotionalLeg;
142 std::vector<Date> valuationDates, paymentDates;
143 for (
const auto& d : floatingSchedule.dates()) {
144 valuationDates.push_back(d);
145 if (d != floatingSchedule.dates().front())
146 paymentDates.push_back(d);
150 QuantLib::ext::shared_ptr<BondTRS> trs = QuantLib::ext::make_shared<BondTRS>(
151 bondIndex, 1.0, Null<Real>(), std::vector<QuantLib::Leg>{fundingLeg, fundingNotionalLeg},
152 true, valuationDates, paymentDates);
154 trs->setPricingEngine(trsEngine);
157 QuantLib::ext::shared_ptr<QuantLib::Bond> floater(QuantLib::ext::make_shared<QuantLib::Bond>(0, calendar, startDate, fundingLeg));
158 Handle<Quote> floaterIssuerSpreadQuote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
159 Handle<DefaultProbabilityTermStructure> floaterDefaultCurve(
160 QuantLib::ext::make_shared<FlatHazardRate>(today, floaterIssuerSpreadQuote, dc));
161 Handle<Quote> floaterSpecificSpread(QuantLib::ext::make_shared<SimpleQuote>(data.securitySpread));
162 Handle<Quote> floaterRecoveryRateQuote(QuantLib::ext::make_shared<SimpleQuote>(0.0));
163 QuantLib::ext::shared_ptr<PricingEngine> floaterEngine(QuantLib::ext::make_shared<QuantExt::DiscountingRiskyBondEngine>(
164 yieldCurve, floaterDefaultCurve, floaterRecoveryRateQuote, floaterSpecificSpread, timeStep));
165 floater->setPricingEngine(floaterEngine);
167 std::ostringstream output;
170 Real bondNpv = bond->NPV();
172 output <<
"Bond NPV = " << bondNpv <<
"\n";
173 output <<
"Floater NPV = " << floater->NPV() <<
"\n";
174 output <<
"TRS NPV = " << trs->NPV() <<
"\n";
175 output <<
"Bond + TRS - Floater = " << bondNpv + trs->NPV() - floater->NPV() <<
"\n";
177 BOOST_TEST_MESSAGE(output.str());
184 BOOST_CHECK_SMALL((bondNpv + trs->NPV() - floater->NPV()) - (bondNpv - redemption), 1.0);
187BOOST_AUTO_TEST_SUITE_END()
188BOOST_AUTO_TEST_SUITE_END()
bond index class representing historical and forward bond prices
BOOST_DATA_TEST_CASE(testBondTRS, boost::unit_test::data::make(testData), data)
Discounting Bond TRS Engine.
Engine to value a Bond TRS.
std::ostream & operator<<(std::ostream &out, EquityReturnType t)
Fixture that can be used at top level.