Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
bond.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2017 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
19#include <boost/make_shared.hpp>
20#include <boost/test/unit_test.hpp>
29#include <oret/toplevelfixture.hpp>
30#include <ql/termstructures/credit/flathazardrate.hpp>
31#include <ql/termstructures/volatility/swaption/swaptionconstantvol.hpp>
32#include <ql/termstructures/yield/flatforward.hpp>
33#include <ql/time/calendars/target.hpp>
34#include <ql/time/daycounters/actualactual.hpp>
35
36using std::string;
37
38using namespace QuantLib;
39using namespace boost::unit_test_framework;
40using namespace std;
41using namespace ore::data;
42
43// Bond test
44
45namespace {
46
47class TestMarket : public MarketImpl {
48public:
49 TestMarket() : MarketImpl(false) {
50 asof_ = Date(3, Feb, 2016);
51 // build discount
52 yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Yield, "BANK_EUR_LEND")] =
53 flatRateYts(0.02);
54 yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Discount, "EUR")] = flatRateYts(0.02);
55 defaultCurves_[make_pair(Market::defaultConfiguration, "CreditCurve_A")] = flatRateDcs(0.0);
56 // recoveryRates_[make_pair(Market::defaultConfiguration, "CreditCurve_A")] =
57 // Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.00));
58 securitySpreads_[make_pair(Market::defaultConfiguration, "Security1")] =
59 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.00));
60 recoveryRates_[make_pair(Market::defaultConfiguration, "Security1")] =
61 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.00));
62 // build ibor index
63 Handle<IborIndex> hEUR(ore::data::parseIborIndex(
64 "EUR-EURIBOR-6M", yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Discount, "EUR")]));
65 iborIndices_[make_pair(Market::defaultConfiguration, "EUR-EURIBOR-6M")] = hEUR;
66
67 // add Eurib 6M fixing
68 hEUR->addFixing(Date(1, Feb, 2016), -0.00191);
69 hEUR->addFixing(Date(1, Feb, 2017), -0.00191);
70 hEUR->addFixing(Date(1, Feb, 2018), -0.00191);
71 hEUR->addFixing(Date(1, Feb, 2019), -0.00191);
72 hEUR->addFixing(Date(31, Jan, 2019), -0.00191);
73 hEUR->addFixing(Date(30, Jan, 2020), -0.00191);
74 }
75
76 TestMarket(Real defaultFlatRate) : MarketImpl(false) {
77 asof_ = Date(3, Feb, 2016);
78 // build discount
79 yieldCurves_[make_tuple(Market::defaultConfiguration, YieldCurveType::Yield, "BANK_EUR_LEND")] =
80 flatRateYts(0.02);
81 defaultCurves_[make_pair(Market::defaultConfiguration, "CreditCurve_A")] = flatRateDcs(defaultFlatRate);
82 // recoveryRates_[make_pair(Market::defaultConfiguration, "CreditCurve_A")] =
83 // Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.00));
84 securitySpreads_[make_pair(Market::defaultConfiguration, "Security1")] =
85 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.00));
86 recoveryRates_[make_pair(Market::defaultConfiguration, "Security1")] =
87 Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(0.00));
88 }
89
90private:
91 Handle<YieldTermStructure> flatRateYts(Real forward) {
92 QuantLib::ext::shared_ptr<YieldTermStructure> yts(new FlatForward(0, NullCalendar(), forward, ActualActual(ActualActual::ISDA)));
93 yts->enableExtrapolation();
94 return Handle<YieldTermStructure>(yts);
95 }
96 Handle<QuantExt::CreditCurve> flatRateDcs(Real forward) {
97 QuantLib::ext::shared_ptr<DefaultProbabilityTermStructure> dcs(
98 new FlatHazardRate(asof_, forward, ActualActual(ActualActual::ISDA)));
99 return Handle<QuantExt::CreditCurve>(
100 QuantLib::ext::make_shared<QuantExt::CreditCurve>(Handle<DefaultProbabilityTermStructure>(dcs)));
101 }
102};
103
104struct CommonVars {
105 // global data
106 string ccy;
107 string securityId;
108 string creditCurveId;
109 string issuerId;
110 string referenceCurveId;
111 bool isPayer;
112 string start;
113 string end;
114 string issue;
115 string fixtenor;
116 Calendar cal;
117 string calStr;
118 string conv;
119 string rule;
120 Size days;
121 string fixDC;
122 Real fixedRate;
123 string settledays;
124 bool isinarrears;
125 Real notional;
126 vector<Real> notionals;
127 vector<Real> spread;
128
129 // utilities
130 QuantLib::ext::shared_ptr<ore::data::Bond> makeBond() {
131 ScheduleData fixedSchedule(ScheduleRules(start, end, fixtenor, calStr, conv, conv, rule));
132
133 // build CMSSwap
134 LegData fixedLegData(QuantLib::ext::make_shared<FixedLegData>(vector<double>(1, fixedRate)), isPayer, ccy,
135 fixedSchedule, fixDC, notionals);
136
137 Envelope env("CP1");
138
139 QuantLib::ext::shared_ptr<ore::data::Bond> bond(
140 new ore::data::Bond(env, BondData(issuerId, creditCurveId, securityId, referenceCurveId, settledays, calStr,
141 issue, fixedLegData)));
142 return bond;
143 }
144
145 QuantLib::ext::shared_ptr<ore::data::Bond> makeAmortizingFixedBond(string amortType, Real value, bool underflow) {
146 ScheduleData fixedSchedule(ScheduleRules(start, end, fixtenor, calStr, conv, conv, rule));
147
148 AmortizationData amortizationData(amortType, value, start, end, fixtenor, underflow);
149 LegData fixedLegData(QuantLib::ext::make_shared<FixedLegData>(vector<double>(1, fixedRate)), isPayer, ccy,
150 fixedSchedule, fixDC, notionals, vector<string>(), conv, false, false, false, true, "", 0,
151 "", {amortizationData});
152
153 Envelope env("CP1");
154
155 QuantLib::ext::shared_ptr<ore::data::Bond> bond(
156 new ore::data::Bond(env, BondData(issuerId, creditCurveId, securityId, referenceCurveId, settledays, calStr,
157 issue, fixedLegData)));
158 return bond;
159 }
160
161 QuantLib::ext::shared_ptr<ore::data::Bond> makeAmortizingFloatingBond(string amortType, Real value, bool underflow) {
162 ScheduleData floatingSchedule(ScheduleRules(start, end, fixtenor, calStr, conv, conv, rule));
163
164 AmortizationData amortizationData(amortType, value, start, end, fixtenor, underflow);
165 LegData floatingLegData(QuantLib::ext::make_shared<FloatingLegData>("EUR-EURIBOR-6M", 2, false, spread), isPayer, ccy,
166 floatingSchedule, fixDC, notionals, vector<string>(), conv, false, false, false, true,
167 "", 0, "", {amortizationData});
168
169 Envelope env("CP1");
170
171 QuantLib::ext::shared_ptr<ore::data::Bond> bond(
172 new ore::data::Bond(env, BondData(issuerId, creditCurveId, securityId, referenceCurveId, settledays, calStr,
173 issue, floatingLegData)));
174 return bond;
175 }
176
177 QuantLib::ext::shared_ptr<ore::data::Bond> makeAmortizingFixedBondWithChangingAmortisation(string amortType1, Real value1,
178 bool underflow1, string end1,
179 string amortType2, Real value2,
180 bool underflow2) {
181 ScheduleData fixedSchedule(ScheduleRules(start, end, fixtenor, calStr, conv, conv, rule));
182
183 AmortizationData amortizationData1(amortType1, value1, start, end1, fixtenor, underflow1);
184 AmortizationData amortizationData2(amortType2, value2, end1, end, fixtenor, underflow2);
185 LegData fixedLegData(QuantLib::ext::make_shared<FixedLegData>(vector<double>(1, fixedRate)), isPayer, ccy,
186 fixedSchedule, fixDC, notionals, vector<string>(), conv, false, false, false, true, "", 0,
187 "", {amortizationData1, amortizationData2});
188
189 Envelope env("CP1");
190
191 QuantLib::ext::shared_ptr<ore::data::Bond> bond(
192 new ore::data::Bond(env, BondData(issuerId, creditCurveId, securityId, referenceCurveId, settledays, calStr,
193 issue, fixedLegData)));
194 return bond;
195 }
196
197 QuantLib::ext::shared_ptr<ore::data::Bond>
198 makeAmortizingFloatingBondWithChangingAmortisation(string amortType1, Real value1, bool underflow1, string end1,
199 string amortType2, Real value2, bool underflow2) {
200 ScheduleData floatingSchedule(ScheduleRules(start, end, fixtenor, calStr, conv, conv, rule));
201
202 FloatingLegData floatingLegRateData;
203 AmortizationData amortizationData1(amortType1, value1, start, end1, fixtenor, underflow1);
204 AmortizationData amortizationData2(amortType2, value2, end1, end, fixtenor, underflow2);
205 LegData floatingLegData(QuantLib::ext::make_shared<FloatingLegData>("EUR-EURIBOR-6M", 2, false, spread), isPayer, ccy,
206 floatingSchedule, fixDC, notionals, vector<string>(), conv, false, false, false, true,
207 "", 0, "", {amortizationData1, amortizationData2});
208
209 Envelope env("CP1");
210
211 QuantLib::ext::shared_ptr<ore::data::Bond> bond(
212 new ore::data::Bond(env, BondData(issuerId, creditCurveId, securityId, referenceCurveId, settledays, calStr,
213 issue, floatingLegData)));
214 return bond;
215 }
216
217 QuantLib::ext::shared_ptr<ore::data::Bond> makeZeroBond() {
218 Envelope env("CP1");
219
220 QuantLib::ext::shared_ptr<ore::data::Bond> bond(
221 new ore::data::Bond(env, BondData(issuerId, creditCurveId, securityId, referenceCurveId, settledays, calStr,
222 notional, end, ccy, issue)));
223 return bond;
224 }
225
226 CommonVars()
227 : ccy("EUR"), securityId("Security1"), creditCurveId("CreditCurve_A"), issuerId("CPTY_A"),
228 referenceCurveId("BANK_EUR_LEND"), isPayer(false), start("20160203"), end("20210203"), issue("20160203"),
229 fixtenor("1Y") {
230 cal = TARGET();
231 calStr = "TARGET";
232 conv = "MF";
233 rule = "Forward";
234 fixDC = "ACT/ACT";
235 fixedRate = 0.05;
236 settledays = "2";
237 isinarrears = false;
238 notional = 10000000;
239 notionals.push_back(10000000);
240 spread.push_back(0.0);
241 }
242};
243
244// print details of bond cashflows
245void printBondSchedule(const QuantLib::ext::shared_ptr<ore::data::Bond>& b) {
246 auto qlInstr = QuantLib::ext::dynamic_pointer_cast<QuantLib::Bond>(b->instrument()->qlInstrument());
247 BOOST_REQUIRE(qlInstr != nullptr);
248 BOOST_TEST_MESSAGE("Bond NPV=" << qlInstr->NPV() << ", Schedule:");
249 Leg l = qlInstr->cashflows();
250 BOOST_TEST_MESSAGE(" StartDate EndDate Nominal Rate Amount");
251 for (auto const& c : l) {
252 auto cpn = QuantLib::ext::dynamic_pointer_cast<Coupon>(c);
253 if (cpn != nullptr) {
254 BOOST_TEST_MESSAGE(QuantLib::io::iso_date(cpn->accrualStartDate())
255 << " " << QuantLib::io::iso_date(cpn->accrualEndDate()) << std::setw(12)
256 << cpn->nominal() << std::setw(12) << cpn->rate() << std::setw(12) << cpn->amount());
257 } else {
258 BOOST_TEST_MESSAGE(" " << QuantLib::io::iso_date(c->date()) << std::setw(12) << " "
259 << std::setw(12) << " " << std::setw(12) << c->amount());
260 }
261 }
262 BOOST_TEST_MESSAGE("");
263}
264
265// check nominal schedule of bond
266void checkNominalSchedule(const QuantLib::ext::shared_ptr<ore::data::Bond>& b, const std::vector<Real> notionals) {
267 auto qlInstr = QuantLib::ext::dynamic_pointer_cast<QuantLib::Bond>(b->instrument()->qlInstrument());
268 BOOST_REQUIRE(qlInstr != nullptr);
269 Leg l = qlInstr->cashflows();
270 std::vector<Real> bondNotionals;
271 for (auto const& c : l) {
272 auto cpn = QuantLib::ext::dynamic_pointer_cast<Coupon>(c);
273 if (cpn != nullptr) {
274 bondNotionals.push_back(cpn->nominal());
275 }
276 }
277 BOOST_REQUIRE_EQUAL(bondNotionals.size(), notionals.size());
278 for (Size i = 0; i < notionals.size(); ++i)
279 BOOST_CHECK_CLOSE(bondNotionals[i], notionals[i], 1E-4);
280}
281
282} // namespace
283
284BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)
285
286BOOST_AUTO_TEST_SUITE(BondTests)
287
288BOOST_AUTO_TEST_CASE(testZeroBond) {
289 BOOST_TEST_MESSAGE("Testing Zero Bond...");
290
291 // build market
292 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>();
293 Settings::instance().evaluationDate() = market->asofDate();
294
295 CommonVars vars;
296 QuantLib::ext::shared_ptr<ore::data::Bond> bond = vars.makeZeroBond();
297
298 // Build and price
299 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
300 engineData->model("Bond") = "DiscountedCashflows";
301 engineData->engine("Bond") = "DiscountingRiskyBondEngine";
302
303 map<string, string> engineparams;
304 engineparams["TimestepPeriod"] = "6M";
305 engineData->engineParameters("Bond") = engineparams;
306
307 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
308
309 bond->build(engineFactory);
310
311 Real npv = bond->instrument()->NPV();
312 Real expectedNpv = 9048374.18;
313
314 BOOST_CHECK_CLOSE(npv, expectedNpv, 1.0);
315}
316
317BOOST_AUTO_TEST_CASE(testAmortizingBond) {
318 BOOST_TEST_MESSAGE("Testing Amortising Bonds...");
319
320 // build market
321 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>();
322 Date today = Date(30, Jan, 2021);
323 Settings::instance().evaluationDate() = today; // market->asofDate();
324
325 CommonVars vars;
326 vector<QuantLib::ext::shared_ptr<ore::data::Bond>> bonds;
327 QuantLib::ext::shared_ptr<ore::data::Bond> bondFixedAmount = vars.makeAmortizingFixedBond("FixedAmount", 2500000, true);
328 bonds.push_back(bondFixedAmount);
329
330 QuantLib::ext::shared_ptr<ore::data::Bond> bondRelativeInitial =
331 vars.makeAmortizingFixedBond("RelativeToInitialNotional", 0.25, true);
332 bonds.push_back(bondRelativeInitial);
333
334 // Build and price
335 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
336 engineData->model("Bond") = "DiscountedCashflows";
337 engineData->engine("Bond") = "DiscountingRiskyBondEngine";
338
339 map<string, string> engineparams;
340 engineparams["TimestepPeriod"] = "6M";
341 engineData->engineParameters("Bond") = engineparams;
342
343 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
344
345 Real npvTol = 0.5;
346
347 for (auto& b : bonds) {
348 b->build(engineFactory);
349
350 // output schedule
351 printBondSchedule(b);
352
353 Real npv = b->instrument()->NPV();
354 Real expectedNpv = 0.0;
355
356 BOOST_CHECK(std::fabs(npv - expectedNpv) < npvTol);
357 }
358
359 QuantLib::ext::shared_ptr<ore::data::Bond> bondRelativePrevious =
360 vars.makeAmortizingFixedBond("RelativeToPreviousNotional", 0.25, true);
361 bondRelativePrevious->build(engineFactory);
362 printBondSchedule(bondRelativePrevious);
363
364 QuantLib::ext::shared_ptr<QuantLib::Instrument> inst1 = bondRelativePrevious->instrument()->qlInstrument();
365 QuantLib::ext::shared_ptr<QuantLib::Bond> qlBond1 = QuantLib::ext::dynamic_pointer_cast<QuantLib::Bond>(inst1);
366 Real expectedNotional = 3164062.5;
367
368 Real notional = qlBond1->notionals()[qlBond1->notionals().size() - 2];
369
370 BOOST_CHECK_CLOSE(notional, expectedNotional, 1);
371
372 QuantLib::ext::shared_ptr<ore::data::Bond> bondFixedAnnuity = vars.makeAmortizingFixedBond("Annuity", 2500000, true);
373 bondFixedAnnuity->build(engineFactory);
374 printBondSchedule(bondFixedAnnuity);
375
376 QuantLib::ext::shared_ptr<QuantLib::Instrument> inst2 = bondFixedAnnuity->instrument()->qlInstrument();
377 QuantLib::ext::shared_ptr<QuantLib::Bond> qlBond2 = QuantLib::ext::dynamic_pointer_cast<QuantLib::Bond>(inst2);
378 expectedNotional = 1380908.447;
379
380 notional = qlBond2->notionals()[qlBond2->notionals().size() - 2];
381
382 BOOST_CHECK(std::fabs(notional - expectedNotional) < npvTol);
383
384 QuantLib::ext::shared_ptr<ore::data::Bond> bondFloatingAnnuity = vars.makeAmortizingFloatingBond("Annuity", 2500000, true);
385 bondFloatingAnnuity->build(engineFactory);
386 printBondSchedule(bondFloatingAnnuity);
387
388 QuantLib::ext::shared_ptr<QuantLib::Instrument> inst3 = bondFloatingAnnuity->instrument()->qlInstrument();
389 QuantLib::ext::shared_ptr<QuantLib::Bond> qlBond3 = QuantLib::ext::dynamic_pointer_cast<QuantLib::Bond>(inst3);
390 Real expectedAmount = 93.41;
391
392 Real amount = qlBond3->cashflows()[qlBond3->cashflows().size() - 2]->amount();
393
394 BOOST_CHECK(std::fabs(amount - expectedAmount) < npvTol);
395}
396
397BOOST_AUTO_TEST_CASE(testAmortizingBondWithChangingAmortisation) {
398 BOOST_TEST_MESSAGE("Testing Amortising Bonds with changing amortisation...");
399
400 // build market
401 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>();
402 Date today = Date(30, Jan, 2021);
403 Settings::instance().evaluationDate() = today; // market->asofDate();
404
405 // build engine factory
406 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
407 engineData->model("Bond") = "DiscountedCashflows";
408 engineData->engine("Bond") = "DiscountingRiskyBondEngine";
409 map<string, string> engineparams;
410 engineparams["TimestepPeriod"] = "6M";
411 engineData->engineParameters("Bond") = engineparams;
412 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
413
414 // test different amortization combinations
415 CommonVars vars;
416
417 // fixed rate bond test cases
418 QuantLib::ext::shared_ptr<ore::data::Bond> bond1 = vars.makeAmortizingFixedBondWithChangingAmortisation(
419 "FixedAmount", 2500000, true, "05-02-2018", "FixedAmount", 1250000, true);
420 bond1->build(engineFactory);
421 printBondSchedule(bond1);
422 checkNominalSchedule(bond1, {1.0E7, 7.5E6, 6.25E6, 5.0E6, 3.75E6});
423
424 QuantLib::ext::shared_ptr<ore::data::Bond> bond2 = vars.makeAmortizingFixedBondWithChangingAmortisation(
425 "FixedAmount", 2500000, true, "05-02-2018", "RelativeToInitialNotional", 0.1, true);
426 bond2->build(engineFactory);
427 printBondSchedule(bond2);
428 checkNominalSchedule(bond2, {1.0E7, 7.5E6, 6.5E6, 5.5E6, 4.5E6});
429
430 QuantLib::ext::shared_ptr<ore::data::Bond> bond3 = vars.makeAmortizingFixedBondWithChangingAmortisation(
431 "RelativeToPreviousNotional", 0.1, true, "05-02-2018", "Annuity", 1E6, true);
432 bond3->build(engineFactory);
433 printBondSchedule(bond3);
434 checkNominalSchedule(bond3, {1.0E7, 9.0E6, 8.45247E6, 7.87393E6, 7.26645E6});
435
436 QuantLib::ext::shared_ptr<ore::data::Bond> bond4 = vars.makeAmortizingFixedBondWithChangingAmortisation(
437 "Annuity", 1E6, true, "05-02-2018", "RelativeToPreviousNotional", 0.1, true);
438 bond4->build(engineFactory);
439 printBondSchedule(bond4);
440 checkNominalSchedule(bond4, {1.0E7, 9.50012E6, 8.55011E6, 7.6951E6, 6.92559E6});
441
442 // floating rate bond test cases
443 QuantLib::ext::shared_ptr<ore::data::Bond> bond5 = vars.makeAmortizingFloatingBondWithChangingAmortisation(
444 "FixedAmount", 2500000, true, "05-02-2018", "FixedAmount", 1250000, true);
445 bond5->build(engineFactory);
446 printBondSchedule(bond5);
447 checkNominalSchedule(bond5, {1.0E7, 7.5E6, 6.25E6, 5.0E6, 3.75E6});
448
449 QuantLib::ext::shared_ptr<ore::data::Bond> bond6 = vars.makeAmortizingFloatingBondWithChangingAmortisation(
450 "FixedAmount", 2500000, true, "05-02-2018", "RelativeToInitialNotional", 0.1, true);
451 bond6->build(engineFactory);
452 printBondSchedule(bond6);
453 checkNominalSchedule(bond6, {1.0E7, 7.5E6, 6.5E6, 5.5E6, 4.5E6});
454
455 // annuity only allowed in single block setup
456 QuantLib::ext::shared_ptr<ore::data::Bond> bond7 = vars.makeAmortizingFloatingBondWithChangingAmortisation(
457 "RelativeToPreviousNotional", 0.1, true, "05-02-2018", "Annuity", 1E6, true);
458 BOOST_CHECK_THROW(bond7->build(engineFactory), QuantLib::Error);
459}
460
461BOOST_AUTO_TEST_CASE(testMultiPhaseBond) {
462 // build market
463 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>();
464 Date today = Date(30, Jan, 2021);
465 Settings::instance().evaluationDate() = today; // market->asofDate();
466
467 // build engine factory
468 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
469 engineData->model("Bond") = "DiscountedCashflows";
470 engineData->engine("Bond") = "DiscountingRiskyBondEngine";
471 map<string, string> engineparams;
472 engineparams["TimestepPeriod"] = "6M";
473 engineData->engineParameters("Bond") = engineparams;
474 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
475
476 // test multi phase bond
477 CommonVars vars;
478 ScheduleData schedule1(ScheduleRules("05-02-2016", "05-02-2018", "1Y", "TARGET", "F", "F", "Forward"));
479 ScheduleData schedule2(ScheduleRules("05-02-2018", "05-02-2020", "6M", "TARGET", "F", "F", "Forward"));
480 auto fixedLegRateData = QuantLib::ext::make_shared<FixedLegData>(vector<double>(1, 0.01));
481 LegData legdata1(fixedLegRateData, vars.isPayer, vars.ccy, schedule1, vars.fixDC, vars.notionals);
482 LegData legdata2(fixedLegRateData, vars.isPayer, vars.ccy, schedule2, vars.fixDC, vars.notionals);
483 Envelope env("CP1");
484 QuantLib::ext::shared_ptr<ore::data::Bond> bond(
485 new ore::data::Bond(env, BondData(vars.issuerId, vars.creditCurveId, vars.securityId, vars.referenceCurveId,
486 vars.settledays, vars.calStr, vars.issue, {legdata1, legdata2})));
487 bond->build(engineFactory);
488 printBondSchedule(bond);
489 auto qlInstr = QuantLib::ext::dynamic_pointer_cast<QuantLib::Bond>(bond->instrument()->qlInstrument());
490 BOOST_REQUIRE(qlInstr != nullptr);
491 // annually
492 BOOST_REQUIRE_EQUAL(qlInstr->cashflows().size(), 7);
493 BOOST_CHECK_EQUAL(qlInstr->cashflows()[0]->date(), Date(6, Feb, 2017));
494 BOOST_CHECK_EQUAL(qlInstr->cashflows()[1]->date(), Date(5, Feb, 2018));
495 // semi annually
496 BOOST_CHECK_EQUAL(qlInstr->cashflows()[2]->date(), Date(6, Aug, 2018));
497 BOOST_CHECK_EQUAL(qlInstr->cashflows()[3]->date(), Date(5, Feb, 2019));
498 BOOST_CHECK_EQUAL(qlInstr->cashflows()[4]->date(), Date(5, Aug, 2019));
499 BOOST_CHECK_EQUAL(qlInstr->cashflows()[5]->date(), Date(5, Feb, 2020));
500 BOOST_CHECK_EQUAL(qlInstr->cashflows()[6]->date(), Date(5, Feb, 2020));
501}
502
503BOOST_AUTO_TEST_CASE(testBondZeroSpreadDefault) {
504 BOOST_TEST_MESSAGE("Testing Bond price...");
505
506 // build market
507 QuantLib::ext::shared_ptr<Market> market = QuantLib::ext::make_shared<TestMarket>();
508 Settings::instance().evaluationDate() = market->asofDate();
509
510 CommonVars vars;
511 QuantLib::ext::shared_ptr<ore::data::Bond> bond = vars.makeBond();
512
513 // Build and price
514 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
515 engineData->model("Bond") = "DiscountedCashflows";
516 engineData->engine("Bond") = "DiscountingRiskyBondEngine";
517
518 map<string, string> engineparams;
519 engineparams["TimestepPeriod"] = "6M";
520 engineData->engineParameters("Bond") = engineparams;
521
522 QuantLib::ext::shared_ptr<EngineFactory> engineFactory = QuantLib::ext::make_shared<EngineFactory>(engineData, market);
523
524 bond->build(engineFactory);
525
526 Real npv = bond->instrument()->NPV();
527 Real expectedNpv = 11403727.39;
528
529 BOOST_CHECK_CLOSE(npv, expectedNpv, 1.0);
530}
531
532BOOST_AUTO_TEST_CASE(testBondCompareDefault) {
533 BOOST_TEST_MESSAGE("Testing Bond price...");
534
535 // build market
536 QuantLib::ext::shared_ptr<Market> market1 = QuantLib::ext::make_shared<TestMarket>(0.0);
537 QuantLib::ext::shared_ptr<Market> market2 = QuantLib::ext::make_shared<TestMarket>(0.5);
538 QuantLib::ext::shared_ptr<Market> market3 = QuantLib::ext::make_shared<TestMarket>(0.99);
539 Settings::instance().evaluationDate() = market1->asofDate();
540
541 CommonVars vars;
542 QuantLib::ext::shared_ptr<ore::data::Bond> bond = vars.makeBond();
543
544 // Build and price
545 QuantLib::ext::shared_ptr<EngineData> engineData = QuantLib::ext::make_shared<EngineData>();
546 engineData->model("Bond") = "DiscountedCashflows";
547 engineData->engine("Bond") = "DiscountingRiskyBondEngine";
548 map<string, string> engineparams;
549 engineparams["TimestepPeriod"] = "6M";
550 engineData->engineParameters("Bond") = engineparams;
551
552 QuantLib::ext::shared_ptr<EngineFactory> engineFactory1 = QuantLib::ext::make_shared<EngineFactory>(engineData, market1);
553 QuantLib::ext::shared_ptr<EngineFactory> engineFactory2 = QuantLib::ext::make_shared<EngineFactory>(engineData, market2);
554 QuantLib::ext::shared_ptr<EngineFactory> engineFactory3 = QuantLib::ext::make_shared<EngineFactory>(engineData, market3);
555
556 bond->build(engineFactory1);
557 Real npv1 = bond->instrument()->NPV();
558 bond->build(engineFactory2);
559 Real npv2 = bond->instrument()->NPV();
560 bond->build(engineFactory3);
561 Real npv3 = bond->instrument()->NPV();
562
563 BOOST_CHECK((npv1 > npv2) && (npv2 > npv3));
564
565 // BOOST_CHECK_CLOSE(npv, expectedNpv, 1.0);
566}
567
568BOOST_AUTO_TEST_SUITE_END()
569
570BOOST_AUTO_TEST_SUITE_END()
Bond trade data model and serialization.
builder that returns an engine to price a bond instrument
Serializable object holding amortization rules.
Definition: legdata.hpp:805
Serializable Bond.
Definition: bond.hpp:153
Serializable object holding generic trade data, reporting dimensions.
Definition: envelope.hpp:51
Serializable Floating Leg Data.
Definition: legdata.hpp:179
Serializable object holding leg data.
Definition: legdata.hpp:844
static const string defaultConfiguration
Default configuration label.
Definition: market.hpp:296
Market Implementation.
Definition: marketimpl.hpp:53
map< tuple< string, YieldCurveType, string >, Handle< YieldTermStructure > > yieldCurves_
Definition: marketimpl.hpp:208
map< pair< string, string >, Handle< QuantExt::CreditCurve > > defaultCurves_
Definition: marketimpl.hpp:215
map< pair< string, string >, Handle< IborIndex > > iborIndices_
Definition: marketimpl.hpp:209
map< pair< string, string >, Handle< Quote > > recoveryRates_
Definition: marketimpl.hpp:218
map< pair< string, string >, Handle< Quote > > securitySpreads_
Definition: marketimpl.hpp:227
Serializable schedule data.
Definition: schedule.hpp:202
Serializable object holding schedule Rules data.
Definition: schedule.hpp:37
SafeStack< ValueType > value
A class to hold pricing engine parameters.
trade envelope data model and serialization
QuantLib::ext::shared_ptr< IborIndex > parseIborIndex(const string &s, const Handle< YieldTermStructure > &h)
Convert std::string to QuantLib::IborIndex.
Map text representations to QuantLib/QuantExt types.
leg data model and serialization
An implementation of the Market class that stores the required objects in maps.
trade schedule data model and serialization
BOOST_AUTO_TEST_CASE(testZeroBond)
Definition: bond.cpp:288