[go: up one dir, main page]

1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2004, 2005 StatPro Italia srl
5 Copyright (C) 2007, 2012 Ferdinando Ametrano
6 Copyright (C) 2007, 2009 Piter Dias
7
8 This file is part of QuantLib, a free-software/open-source library
9 for financial quantitative analysts and developers - http://quantlib.org/
10
11 QuantLib is free software: you can redistribute it and/or modify it
12 under the terms of the QuantLib license. You should have received a
13 copy of the license along with this program; if not, please email
14 <quantlib-dev@lists.sf.net>. The license is also available online at
15 <http://quantlib.org/license.shtml>.
16
17 This program is distributed in the hope that it will be useful, but WITHOUT
18 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 FOR A PARTICULAR PURPOSE. See the license for more details.
20*/
21
22#include "bonds.hpp"
23#include "utilities.hpp"
24#include <ql/cashflows/iborcoupon.hpp>
25#include <ql/instruments/bonds/fixedratebond.hpp>
26#include <ql/instruments/bonds/floatingratebond.hpp>
27#include <ql/instruments/bonds/zerocouponbond.hpp>
28#include <ql/time/calendars/target.hpp>
29#include <ql/time/calendars/unitedstates.hpp>
30#include <ql/time/calendars/unitedkingdom.hpp>
31#include <ql/time/calendars/australia.hpp>
32#include <ql/time/calendars/brazil.hpp>
33#include <ql/time/calendars/southafrica.hpp>
34#include <ql/time/calendars/nullcalendar.hpp>
35#include <ql/time/daycounters/thirty360.hpp>
36#include <ql/time/daycounters/actual360.hpp>
37#include <ql/time/daycounters/actualactual.hpp>
38#include <ql/time/daycounters/business252.hpp>
39#include <ql/indexes/ibor/usdlibor.hpp>
40#include <ql/quotes/simplequote.hpp>
41#include <ql/utilities/dataformatters.hpp>
42#include <ql/time/schedule.hpp>
43#include <ql/cashflows/fixedratecoupon.hpp>
44#include <ql/cashflows/simplecashflow.hpp>
45#include <ql/cashflows/couponpricer.hpp>
46#include <ql/cashflows/cashflows.hpp>
47#include <ql/pricingengines/bond/discountingbondengine.hpp>
48#include <ql/pricingengines/bond/bondfunctions.hpp>
49#include <ql/termstructures/credit/flathazardrate.hpp>
50#include <ql/termstructures/yield/flatforward.hpp>
51#include <ql/currencies/europe.hpp>
52#include <ql/pricingengines/bond/riskybondengine.hpp>
53
54using namespace QuantLib;
55using namespace boost::unit_test_framework;
56
57#define ASSERT_CLOSE(name, settlement, calculated, expected, tolerance) \
58 if (std::fabs(calculated-expected) > tolerance) { \
59 BOOST_ERROR("Failed to reproduce " << name << " at " << settlement \
60 << "\n calculated: " << std::setprecision(8) << calculated \
61 << "\n expected: " << std::setprecision(8) << expected); \
62 }
63
64namespace bonds_test {
65
66 struct CommonVars {
67 // common data
68 Calendar calendar;
69 Date today;
70 Real faceAmount;
71
72 // setup
73 CommonVars() {
74 calendar = TARGET();
75 today = calendar.adjust(Date::todaysDate());
76 Settings::instance().evaluationDate() = today;
77 faceAmount = 1000000.0;
78 }
79 };
80
81 void checkValue(Real value, Real expectedValue, Real tolerance, const std::string& msg) {
82 if (std::fabs(x: value - expectedValue) > tolerance) {
83 BOOST_ERROR(msg
84 << std::fixed
85 << "\n calculated: " << value
86 << "\n expected: " << expectedValue
87 << "\n tolerance: " << tolerance
88 << "\n error: " << value - expectedValue);
89 }
90 }
91}
92
93
94void BondTest::testYield() {
95
96 BOOST_TEST_MESSAGE("Testing consistency of bond price/yield calculation...");
97
98 using namespace bonds_test;
99
100 CommonVars vars;
101
102 Real tolerance = 1.0e-7;
103 Size maxEvaluations = 100;
104
105 Integer issueMonths[] = { -24, -18, -12, -6, 0, 6, 12, 18, 24 };
106 Integer lengths[] = { 3, 5, 10, 15, 20 };
107 Natural settlementDays = 3;
108 Real coupons[] = { 0.02, 0.05, 0.08 };
109 Frequency frequencies[] = { Semiannual, Annual };
110 DayCounter bondDayCount = Thirty360(Thirty360::BondBasis);
111 BusinessDayConvention accrualConvention = Unadjusted;
112 BusinessDayConvention paymentConvention = ModifiedFollowing;
113 Real redemption = 100.0;
114
115 Rate yields[] = { 0.03, 0.04, 0.05, 0.06, 0.07 };
116 Compounding compounding[] = { Compounded, Continuous };
117
118 for (int issueMonth : issueMonths) {
119 for (int length : lengths) {
120 for (Real& coupon : coupons) {
121 for (auto& frequencie : frequencies) {
122 for (auto& n : compounding) {
123
124 Date dated = vars.calendar.advance(vars.today, n: issueMonth, unit: Months);
125 Date issue = dated;
126 Date maturity = vars.calendar.advance(issue, n: length, unit: Years);
127
128 Schedule sch(dated, maturity, Period(frequencie), vars.calendar,
129 accrualConvention, accrualConvention, DateGeneration::Backward,
130 false);
131
132 FixedRateBond bond(settlementDays, vars.faceAmount, sch,
133 std::vector<Rate>(1, coupon), bondDayCount,
134 paymentConvention, redemption, issue);
135
136 for (Real m : yields) {
137
138 Real price =
139 BondFunctions::cleanPrice(bond, yield: m, dayCounter: bondDayCount, compounding: n, frequency: frequencie);
140
141 Rate calculated = BondFunctions::yield(
142 bond, price, dayCounter: bondDayCount, compounding: n, frequency: frequencie, settlementDate: Date(), accuracy: tolerance,
143 maxIterations: maxEvaluations, guess: 0.05, priceType: Bond::Price::Clean);
144
145 if (std::fabs(x: m - calculated) > tolerance) {
146 // the difference might not matter
147 Real price2 = BondFunctions::cleanPrice(
148 bond, yield: calculated, dayCounter: bondDayCount, compounding: n, frequency: frequencie);
149 if (std::fabs(x: price - price2) / price > tolerance) {
150 BOOST_ERROR("\nyield recalculation failed:"
151 "\n issue: "
152 << issue << "\n maturity: " << maturity
153 << "\n coupon: " << io::rate(coupon)
154 << "\n frequency: " << frequencie
155 << "\n yield: " << io::rate(m)
156 << (n == Compounded ? " compounded" : " continuous")
157 << std::setprecision(7) << "\n clean price: "
158 << price << "\n yield': " << io::rate(calculated)
159 << "\n clean price': " << price2);
160 }
161 }
162
163 price = BondFunctions::dirtyPrice(bond, yield: m, dayCounter: bondDayCount, compounding: n, frequency: frequencie);
164
165 calculated = BondFunctions::yield(
166 bond, price, dayCounter: bondDayCount, compounding: n, frequency: frequencie, settlementDate: Date(), accuracy: tolerance,
167 maxIterations: maxEvaluations, guess: 0.05, priceType: Bond::Price::Dirty);
168
169 if (std::fabs(x: m - calculated) > tolerance) {
170 // the difference might not matter
171 Real price2 = BondFunctions::dirtyPrice(
172 bond, yield: calculated, dayCounter: bondDayCount, compounding: n, frequency: frequencie);
173 if (std::fabs(x: price - price2) / price > tolerance) {
174 BOOST_ERROR("\nyield recalculation failed:"
175 "\n issue: "
176 << issue << "\n maturity: " << maturity
177 << "\n coupon: " << io::rate(coupon)
178 << "\n frequency: " << frequencie
179 << "\n yield: " << io::rate(m)
180 << (n == Compounded ? " compounded" : " continuous")
181 << std::setprecision(7) << "\n dirty price: "
182 << price << "\n yield': " << io::rate(calculated)
183 << "\n dirty price': " << price2);
184 }
185 }
186 }
187 }
188 }
189 }
190 }
191 }
192}
193
194void BondTest::testAtmRate() {
195
196 BOOST_TEST_MESSAGE("Testing consistency of bond price/ATM rate calculation...");
197
198 using namespace bonds_test;
199
200 CommonVars vars;
201
202 Real tolerance = 1.0e-7;
203
204 Integer issueMonths[] = { -24, -18, -12, -6, 0, 6, 12, 18, 24 };
205 Integer lengths[] = { 3, 5, 10, 15, 20 };
206 Natural settlementDays = 3;
207 Real coupons[] = { 0.02, 0.05, 0.08 };
208 Frequency frequencies[] = { Semiannual, Annual };
209 DayCounter bondDayCount = Thirty360(Thirty360::BondBasis);
210 BusinessDayConvention accrualConvention = Unadjusted;
211 BusinessDayConvention paymentConvention = ModifiedFollowing;
212 Real redemption = 100.0;
213 Handle<YieldTermStructure> disc(flatRate(today: vars.today,forward: 0.03,dc: Actual360()));
214 ext::shared_ptr<PricingEngine> bondEngine(new DiscountingBondEngine(disc));
215
216 for (int issueMonth : issueMonths) {
217 for (int length : lengths) {
218 for (Real& coupon : coupons) {
219 for (auto& frequencie : frequencies) {
220 Date dated = vars.calendar.advance(vars.today, n: issueMonth, unit: Months);
221 Date issue = dated;
222 Date maturity = vars.calendar.advance(issue, n: length, unit: Years);
223
224 Schedule sch(dated, maturity, Period(frequencie), vars.calendar,
225 accrualConvention, accrualConvention, DateGeneration::Backward,
226 false);
227
228 FixedRateBond bond(settlementDays, vars.faceAmount, sch,
229 std::vector<Rate>(1, coupon), bondDayCount,
230 paymentConvention, redemption, issue);
231
232 bond.setPricingEngine(bondEngine);
233 Real price = bond.cleanPrice();
234 Rate calculated =
235 BondFunctions::atmRate(bond, discountCurve: **disc, settlementDate: bond.settlementDate(), cleanPrice: price);
236
237 if (std::fabs(x: coupon - calculated) > tolerance) {
238 BOOST_ERROR("\natm rate recalculation failed:"
239 "\n today: "
240 << vars.today << "\n settlement date: " << bond.settlementDate()
241 << "\n issue: " << issue << "\n maturity: "
242 << maturity << "\n coupon: " << io::rate(coupon)
243 << "\n frequency: " << frequencie
244 << "\n clean price: " << price
245 << "\n dirty price: " << price + bond.accruedAmount()
246 << "\n atm rate: " << io::rate(calculated));
247 }
248 }
249 }
250 }
251 }
252}
253
254void BondTest::testZspread() {
255
256 BOOST_TEST_MESSAGE("Testing consistency of bond price/z-spread calculation...");
257
258 using namespace bonds_test;
259
260 CommonVars vars;
261
262 Real tolerance = 1.0e-7;
263 Size maxEvaluations = 100;
264
265 Handle<YieldTermStructure> discountCurve(
266 flatRate(today: vars.today,forward: 0.03,dc: Actual360()));
267
268 Integer issueMonths[] = { -24, -18, -12, -6, 0, 6, 12, 18, 24 };
269 Integer lengths[] = { 3, 5, 10, 15, 20 };
270 Natural settlementDays = 3;
271 Real coupons[] = { 0.02, 0.05, 0.08 };
272 Frequency frequencies[] = { Semiannual, Annual };
273 DayCounter bondDayCount = Thirty360(Thirty360::BondBasis);
274 BusinessDayConvention accrualConvention = Unadjusted;
275 BusinessDayConvention paymentConvention = ModifiedFollowing;
276 Real redemption = 100.0;
277
278 Spread spreads[] = { -0.01, -0.005, 0.0, 0.005, 0.01 };
279 Compounding compounding[] = { Compounded, Continuous };
280
281 for (int issueMonth : issueMonths) {
282 for (int length : lengths) {
283 for (Real& coupon : coupons) {
284 for (auto& frequencie : frequencies) {
285 for (auto& n : compounding) {
286
287 Date dated = vars.calendar.advance(vars.today, n: issueMonth, unit: Months);
288 Date issue = dated;
289 Date maturity = vars.calendar.advance(issue, n: length, unit: Years);
290
291 Schedule sch(dated, maturity, Period(frequencie), vars.calendar,
292 accrualConvention, accrualConvention, DateGeneration::Backward,
293 false);
294
295 FixedRateBond bond(settlementDays, vars.faceAmount, sch,
296 std::vector<Rate>(1, coupon), bondDayCount,
297 paymentConvention, redemption, issue);
298
299 for (Real spread : spreads) {
300
301 Real price = BondFunctions::cleanPrice(bond, discount: *discountCurve, zSpread: spread,
302 dayCounter: bondDayCount, compounding: n, frequency: frequencie);
303 Spread calculated = BondFunctions::zSpread(
304 bond, cleanPrice: price, *discountCurve, dayCounter: bondDayCount, compounding: n, frequency: frequencie, settlementDate: Date(),
305 accuracy: tolerance, maxIterations: maxEvaluations);
306
307 if (std::fabs(x: spread - calculated) > tolerance) {
308 // the difference might not matter
309 Real price2 = BondFunctions::cleanPrice(
310 bond, discount: *discountCurve, zSpread: calculated, dayCounter: bondDayCount, compounding: n, frequency: frequencie);
311 if (std::fabs(x: price - price2) / price > tolerance) {
312 BOOST_ERROR("\nZ-spread recalculation failed:"
313 "\n issue: "
314 << issue << "\n maturity: " << maturity
315 << "\n coupon: " << io::rate(coupon)
316 << "\n frequency: " << frequencie
317 << "\n Z-spread: " << io::rate(spread)
318 << (n == Compounded ? " compounded" : " continuous")
319 << std::setprecision(7)
320 << "\n price: " << price
321 << "\n Z-spread': " << io::rate(calculated)
322 << "\n price': " << price2);
323 }
324 }
325 }
326 }
327 }
328 }
329 }
330 }
331}
332
333
334
335void BondTest::testTheoretical() {
336
337 BOOST_TEST_MESSAGE("Testing theoretical bond price/yield calculation...");
338
339 using namespace bonds_test;
340
341 CommonVars vars;
342
343 Real tolerance = 1.0e-7;
344 Size maxEvaluations = 100;
345
346 Size lengths[] = { 3, 5, 10, 15, 20 };
347 Natural settlementDays = 3;
348 Real coupons[] = { 0.02, 0.05, 0.08 };
349 Frequency frequencies[] = { Semiannual, Annual };
350 DayCounter bondDayCount = Actual360();
351 BusinessDayConvention accrualConvention = Unadjusted;
352 BusinessDayConvention paymentConvention = ModifiedFollowing;
353 Real redemption = 100.0;
354
355 Rate yields[] = { 0.03, 0.04, 0.05, 0.06, 0.07 };
356
357 for (unsigned long length : lengths) {
358 for (Real& coupon : coupons) {
359 for (auto& frequencie : frequencies) {
360
361 Date dated = vars.today;
362 Date issue = dated;
363 Date maturity = vars.calendar.advance(issue, n: length, unit: Years);
364
365 ext::shared_ptr<SimpleQuote> rate(new SimpleQuote(0.0));
366 Handle<YieldTermStructure> discountCurve(flatRate(today: vars.today, forward: rate, dc: bondDayCount));
367
368 Schedule sch(dated, maturity, Period(frequencie), vars.calendar, accrualConvention,
369 accrualConvention, DateGeneration::Backward, false);
370
371 FixedRateBond bond(settlementDays, vars.faceAmount, sch,
372 std::vector<Rate>(1, coupon), bondDayCount, paymentConvention,
373 redemption, issue);
374
375 ext::shared_ptr<PricingEngine> bondEngine(new DiscountingBondEngine(discountCurve));
376 bond.setPricingEngine(bondEngine);
377
378 for (Real m : yields) {
379
380 rate->setValue(m);
381
382 Real price =
383 BondFunctions::cleanPrice(bond, yield: m, dayCounter: bondDayCount, compounding: Continuous, frequency: frequencie);
384 Real calculatedPrice = bond.cleanPrice();
385
386 if (std::fabs(x: price - calculatedPrice) > tolerance) {
387 BOOST_ERROR("price calculation failed:"
388 << "\n issue: " << issue << "\n maturity: "
389 << maturity << "\n coupon: " << io::rate(coupon)
390 << "\n frequency: " << frequencie
391 << "\n yield: " << io::rate(m) << std::setprecision(7)
392 << "\n expected: " << price
393 << "\n calculated': " << calculatedPrice
394 << "\n error': " << price - calculatedPrice);
395 }
396
397 Rate calculatedYield = BondFunctions::yield(
398 bond, price: calculatedPrice, dayCounter: bondDayCount, compounding: Continuous, frequency: frequencie,
399 settlementDate: bond.settlementDate(), accuracy: tolerance, maxIterations: maxEvaluations);
400 if (std::fabs(x: m - calculatedYield) > tolerance) {
401 BOOST_ERROR("yield calculation failed:"
402 << "\n issue: " << issue << "\n maturity: "
403 << maturity << "\n coupon: " << io::rate(coupon)
404 << "\n frequency: " << frequencie
405 << "\n yield: " << io::rate(m) << std::setprecision(7)
406 << "\n price: " << price
407 << "\n yield': " << io::rate(calculatedYield));
408 }
409 }
410 }
411 }
412 }
413}
414
415
416void BondTest::testCached() {
417
418 BOOST_TEST_MESSAGE(
419 "Testing bond price/yield calculation against cached values...");
420
421 using namespace bonds_test;
422
423 CommonVars vars;
424
425 // with implicit settlement calculation:
426
427 Date today(22, November, 2004);
428 Settings::instance().evaluationDate() = today;
429
430 Calendar bondCalendar = NullCalendar();
431
432 Natural settlementDays = 1;
433
434 Handle<YieldTermStructure> discountCurve(flatRate(today,forward: 0.03,dc: Actual360()));
435
436 // actual market values from the evaluation date
437
438 Frequency freq = Semiannual;
439 // This means that this bond has a short first coupon, as the
440 // first coupon payment is april 30th and therefore the notional
441 // first coupon is on October 30th 2004. Changing the EOM
442 // convention to true will correct this so that the coupon starts
443 // on October 31st and the first coupon is complete. This is
444 // effectively assumed by the no-schedule daycounter.
445 Schedule sch1(Date(31, October, 2004),
446 Date(31, October, 2006), Period(freq), bondCalendar,
447 Unadjusted, Unadjusted, DateGeneration::Backward, true);
448 DayCounter bondDayCount1 = ActualActual(ActualActual::ISMA, sch1);
449 DayCounter bondDayCount1NoSchedule = ActualActual(ActualActual::ISMA);
450
451 FixedRateBond bond1(settlementDays, vars.faceAmount, sch1,
452 std::vector<Rate>(1, 0.025),
453 bondDayCount1, ModifiedFollowing,
454 100.0, Date(1, November, 2004));
455 FixedRateBond bond1NoSchedule(
456 settlementDays, vars.faceAmount, sch1,
457 std::vector<Rate>(1, 0.025),
458 bondDayCount1NoSchedule, ModifiedFollowing,
459 100.0, Date(1, November, 2004)
460 );
461
462 ext::shared_ptr<PricingEngine> bondEngine(
463 new DiscountingBondEngine(discountCurve));
464 bond1.setPricingEngine(bondEngine);
465 bond1NoSchedule.setPricingEngine(bondEngine);
466
467 Real marketPrice1 = 99.203125;
468 Rate marketYield1 = 0.02925;
469
470 Schedule sch2(Date(15, November, 2004),
471 Date(15, November, 2009), Period(freq), bondCalendar,
472 Unadjusted, Unadjusted, DateGeneration::Backward, false);
473 DayCounter bondDayCount2 = ActualActual(ActualActual::ISMA, sch2);
474 DayCounter bondDayCount2NoSchedule = ActualActual(ActualActual::ISMA);
475
476 FixedRateBond bond2(settlementDays, vars.faceAmount, sch2,
477 std::vector<Rate>(1, 0.035),
478 bondDayCount2, ModifiedFollowing,
479 100.0, Date(15, November, 2004));
480 FixedRateBond bond2NoSchedule(settlementDays, vars.faceAmount, sch2,
481 std::vector<Rate>(1, 0.035),
482 bondDayCount2NoSchedule, ModifiedFollowing,
483 100.0, Date(15, November, 2004)
484 );
485
486 bond2.setPricingEngine(bondEngine);
487 bond2NoSchedule.setPricingEngine(bondEngine);
488
489 Real marketPrice2 = 99.6875;
490 Rate marketYield2 = 0.03569;
491
492 // calculated values
493
494 Real cachedPrice1a = 99.204505, cachedPrice2a = 99.687192;
495 Real cachedPrice1b = 98.943393, cachedPrice2b = 101.986794;
496 Rate cachedYield1a = 0.029257, cachedYield2a = 0.035689;
497 Rate cachedYield1b = 0.029045, cachedYield2b = 0.035375;
498 Rate cachedYield1c = 0.030423, cachedYield2c = 0.030432;
499
500 // check
501 Real tolerance = 1.0e-6;
502
503 checkValue(
504 value: BondFunctions::cleanPrice(bond: bond1, yield: marketYield1, dayCounter: bondDayCount1, compounding: Compounded, frequency: freq),
505 expectedValue: cachedPrice1a,
506 tolerance,
507 msg: "failed to reproduce cached price with schedule for bond 1:"
508 );
509 checkValue(
510 value: BondFunctions::cleanPrice(bond: bond1NoSchedule, yield: marketYield1, dayCounter: bondDayCount1NoSchedule, compounding: Compounded, frequency: freq),
511 expectedValue: cachedPrice1a,
512 tolerance,
513 msg: "failed to reproduce cached price with no schedule for bond 1:"
514 );
515 checkValue(
516 value: bond1.cleanPrice(),
517 expectedValue: cachedPrice1b,
518 tolerance,
519 msg: "failed to reproduce cached clean price with schedule for bond 1:"
520 );
521 checkValue(
522 value: bond1NoSchedule.cleanPrice(),
523 expectedValue: cachedPrice1b,
524 tolerance,
525 msg: "failed to reproduce cached clean price with no schdule for bond 1:"
526 );
527 checkValue(
528 value: BondFunctions::yield(bond: bond1, price: marketPrice1, dayCounter: bondDayCount1, compounding: Compounded, frequency: freq),
529 expectedValue: cachedYield1a,
530 tolerance,
531 msg: "failed to reproduce cached compounded yield with schedule for bond 1:"
532 );
533 checkValue(
534 value: BondFunctions::yield(bond: bond1NoSchedule, price: marketPrice1, dayCounter: bondDayCount1NoSchedule, compounding: Compounded, frequency: freq),
535 expectedValue: cachedYield1a,
536 tolerance,
537 msg: "failed to reproduce cached compounded yield with no schedule for bond 1:"
538 );
539 checkValue(
540 value: BondFunctions::yield(bond: bond1, price: marketPrice1, dayCounter: bondDayCount1, compounding: Continuous, frequency: freq),
541 expectedValue: cachedYield1b,
542 tolerance,
543 msg: "failed to reproduce cached continuous yield with schedule for bond 1:"
544 );
545 checkValue(
546 value: BondFunctions::yield(bond: bond1NoSchedule, price: marketPrice1, dayCounter: bondDayCount1NoSchedule, compounding: Continuous, frequency: freq),
547 expectedValue: cachedYield1b,
548 tolerance,
549 msg: "failed to reproduce cached continuous yield with no schedule for bond 1:"
550 );
551 checkValue(
552 value: BondFunctions::yield(bond: bond1, price: bond1.cleanPrice(), dayCounter: bondDayCount1, compounding: Continuous, frequency: freq, settlementDate: bond1.settlementDate()),
553 expectedValue: cachedYield1c,
554 tolerance,
555 msg: "failed to reproduce cached continuous yield with schedule for bond 1:"
556 );
557 checkValue(
558 value: BondFunctions::yield(bond: bond1NoSchedule, price: bond1NoSchedule.cleanPrice(), dayCounter: bondDayCount1NoSchedule, compounding: Continuous, frequency: freq, settlementDate: bond1.settlementDate()),
559 expectedValue: cachedYield1c,
560 tolerance,
561 msg: "failed to reproduce cached continuous yield with no schedule for bond 1:"
562 );
563
564
565 //Now bond 2
566 checkValue(
567 value: BondFunctions::cleanPrice(bond: bond2, yield: marketYield2, dayCounter: bondDayCount2, compounding: Compounded, frequency: freq),
568 expectedValue: cachedPrice2a,
569 tolerance,
570 msg: "failed to reproduce cached price with schedule for bond 2"
571 );
572 checkValue(
573 value: BondFunctions::cleanPrice(bond: bond2NoSchedule, yield: marketYield2, dayCounter: bondDayCount2NoSchedule, compounding: Compounded, frequency: freq),
574 expectedValue: cachedPrice2a,
575 tolerance,
576 msg: "failed to reproduce cached price with no schedule for bond 2:"
577 );
578 checkValue(
579 value: bond2.cleanPrice(),
580 expectedValue: cachedPrice2b,
581 tolerance,
582 msg: "failed to reproduce cached clean price with schedule for bond 2:"
583 );
584 checkValue(
585 value: bond2NoSchedule.cleanPrice(),
586 expectedValue: cachedPrice2b,
587 tolerance,
588 msg: "failed to reproduce cached clean price with no schedule for bond 2:"
589 );
590 checkValue(
591 value: BondFunctions::yield(bond: bond2, price: marketPrice2, dayCounter: bondDayCount2, compounding: Compounded, frequency: freq),
592 expectedValue: cachedYield2a,
593 tolerance,
594 msg: "failed to reproduce cached compounded yield with schedule for bond 2:"
595 );
596 checkValue(
597 value: BondFunctions::yield(bond: bond2NoSchedule, price: marketPrice2, dayCounter: bondDayCount2NoSchedule, compounding: Compounded, frequency: freq),
598 expectedValue: cachedYield2a,
599 tolerance,
600 msg: "failed to reproduce cached compounded yield with no schedule for bond 2:"
601 );
602 checkValue(
603 value: BondFunctions::yield(bond: bond2, price: marketPrice2, dayCounter: bondDayCount2, compounding: Continuous, frequency: freq),
604 expectedValue: cachedYield2b,
605 tolerance,
606 msg: "failed to reproduce chached continuous yield with schedule for bond 2:"
607 );
608 checkValue(
609 value: BondFunctions::yield(bond: bond2NoSchedule, price: marketPrice2, dayCounter: bondDayCount2NoSchedule, compounding: Continuous, frequency: freq),
610 expectedValue: cachedYield2b,
611 tolerance,
612 msg: "failed to reproduce cached continuous yield with schedule for bond 2:"
613 );
614 checkValue(
615 value: BondFunctions::yield(bond: bond2, price: bond2.cleanPrice(), dayCounter: bondDayCount2, compounding: Continuous, frequency: freq, settlementDate: bond2.settlementDate()),
616 expectedValue: cachedYield2c,
617 tolerance,
618 msg: "failed to reproduce cached continuous yield for bond 2 with schedule:"
619 );
620 checkValue(
621 value: BondFunctions::yield(bond: bond2NoSchedule, price: bond2NoSchedule.cleanPrice(), dayCounter: bondDayCount2NoSchedule, compounding: Continuous, frequency: freq, settlementDate: bond2NoSchedule.settlementDate()),
622 expectedValue: cachedYield2c,
623 tolerance,
624 msg: "failed to reproduce cached continuous yield for bond 2 with no schedule:"
625 );
626
627
628
629 // with explicit settlement date:
630
631 Schedule sch3(Date(30,November,2004),
632 Date(30,November,2006), Period(freq),
633 UnitedStates(UnitedStates::GovernmentBond),
634 Unadjusted, Unadjusted, DateGeneration::Backward, false);
635 DayCounter bondDayCount3 = ActualActual(ActualActual::ISMA, sch3);
636 DayCounter bondDayCount3NoSchedule = ActualActual(ActualActual::ISMA);
637
638 FixedRateBond bond3(settlementDays, vars.faceAmount, sch3,
639 std::vector<Rate>(1, 0.02875),
640 bondDayCount3,
641 ModifiedFollowing,
642 100.0, Date(30,November,2004));
643 FixedRateBond bond3NoSchedule(settlementDays, vars.faceAmount, sch3,
644 std::vector<Rate>(1, 0.02875),
645 bondDayCount3NoSchedule,
646 ModifiedFollowing,
647 100.0, Date(30, November, 2004));
648
649 bond3.setPricingEngine(bondEngine);
650 bond3NoSchedule.setPricingEngine(bondEngine);
651
652 Rate marketYield3 = 0.02997;
653
654 Date settlementDate = Date(30,November,2004);
655 Real cachedPrice3 = 99.764759;
656
657 checkValue(
658 value: BondFunctions::cleanPrice(bond: bond3, yield: marketYield3, dayCounter: bondDayCount3, compounding: Compounded, frequency: freq, settlementDate),
659 expectedValue: cachedPrice3,
660 tolerance,
661 msg: "Failed to reproduce cached price for bond 3 with schedule"
662 );
663 checkValue(
664 value: BondFunctions::cleanPrice(bond: bond3NoSchedule, yield: marketYield3, dayCounter: bondDayCount3NoSchedule, compounding: Compounded, frequency: freq, settlementDate),
665 expectedValue: cachedPrice3,
666 tolerance,
667 msg: "Failed to reproduce cached price for bond 3 with no schedule"
668 );
669
670 // this should give the same result since the issue date is the
671 // earliest possible settlement date
672
673 Settings::instance().evaluationDate() = Date(22,November,2004);
674 checkValue(
675 value: BondFunctions::cleanPrice(bond: bond3, yield: marketYield3, dayCounter: bondDayCount3, compounding: Compounded, frequency: freq),
676 expectedValue: cachedPrice3,
677 tolerance,
678 msg: "Failed to reproduce the cached price for bond 3 with schedule and the earlierst possible settlment date"
679 );
680 checkValue(
681 value: BondFunctions::cleanPrice(bond: bond3NoSchedule, yield: marketYield3, dayCounter: bondDayCount3NoSchedule, compounding: Compounded, frequency: freq),
682 expectedValue: cachedPrice3,
683 tolerance,
684 msg: "Failed to reproduce the cached price for bond 3 with no schedule and the earlierst possible settlment date"
685 );
686}
687
688
689
690void BondTest::testCachedZero() {
691
692 BOOST_TEST_MESSAGE("Testing zero-coupon bond prices against cached values...");
693
694 using namespace bonds_test;
695
696 CommonVars vars;
697
698 Date today(22,November,2004);
699 Settings::instance().evaluationDate() = today;
700
701 Natural settlementDays = 1;
702
703 Handle<YieldTermStructure> discountCurve(flatRate(today,forward: 0.03,dc: Actual360()));
704
705 Real tolerance = 1.0e-6;
706
707 // plain
708
709 ZeroCouponBond bond1(settlementDays,
710 UnitedStates(UnitedStates::GovernmentBond),
711 vars.faceAmount,
712 Date(30,November,2008),
713 ModifiedFollowing,
714 100.0, Date(30,November,2004));
715
716 ext::shared_ptr<PricingEngine> bondEngine(
717 new DiscountingBondEngine(discountCurve));
718 bond1.setPricingEngine(bondEngine);
719
720 Real cachedPrice1 = 88.551726;
721
722 Real price = bond1.cleanPrice();
723 if (std::fabs(x: price-cachedPrice1) > tolerance) {
724 BOOST_FAIL("failed to reproduce cached price:\n"
725 << std::fixed
726 << " calculated: " << price << "\n"
727 << " expected: " << cachedPrice1 << "\n"
728 << " error: " << price-cachedPrice1);
729 }
730
731 ZeroCouponBond bond2(settlementDays,
732 UnitedStates(UnitedStates::GovernmentBond),
733 vars.faceAmount,
734 Date(30,November,2007),
735 ModifiedFollowing,
736 100.0, Date(30,November,2004));
737
738 bond2.setPricingEngine(bondEngine);
739
740 Real cachedPrice2 = 91.278949;
741
742 price = bond2.cleanPrice();
743 if (std::fabs(x: price-cachedPrice2) > tolerance) {
744 BOOST_FAIL("failed to reproduce cached price:\n"
745 << std::fixed
746 << " calculated: " << price << "\n"
747 << " expected: " << cachedPrice2 << "\n"
748 << " error: " << price-cachedPrice2);
749 }
750
751 ZeroCouponBond bond3(settlementDays,
752 UnitedStates(UnitedStates::GovernmentBond),
753 vars.faceAmount,
754 Date(30,November,2006),
755 ModifiedFollowing,
756 100.0, Date(30,November,2004));
757
758 bond3.setPricingEngine(bondEngine);
759
760 Real cachedPrice3 = 94.098006;
761
762 price = bond3.cleanPrice();
763 if (std::fabs(x: price-cachedPrice3) > tolerance) {
764 BOOST_FAIL("failed to reproduce cached price:\n"
765 << std::fixed
766 << " calculated: " << price << "\n"
767 << " expected: " << cachedPrice3 << "\n"
768 << " error: " << price-cachedPrice3);
769 }
770}
771
772
773void BondTest::testCachedFixed() {
774
775 BOOST_TEST_MESSAGE("Testing fixed-coupon bond prices against cached values...");
776
777 using namespace bonds_test;
778
779 CommonVars vars;
780
781 Date today(22,November,2004);
782 Settings::instance().evaluationDate() = today;
783
784 Natural settlementDays = 1;
785
786 Handle<YieldTermStructure> discountCurve(flatRate(today,forward: 0.03,dc: Actual360()));
787
788 Real tolerance = 1.0e-6;
789
790 // plain
791
792 Schedule sch(Date(30,November,2004),
793 Date(30,November,2008), Period(Semiannual),
794 UnitedStates(UnitedStates::GovernmentBond),
795 Unadjusted, Unadjusted, DateGeneration::Backward, false);
796
797 FixedRateBond bond1(settlementDays, vars.faceAmount, sch,
798 std::vector<Rate>(1, 0.02875),
799 ActualActual(ActualActual::ISMA),
800 ModifiedFollowing,
801 100.0, Date(30,November,2004));
802
803 ext::shared_ptr<PricingEngine> bondEngine(
804 new DiscountingBondEngine(discountCurve));
805 bond1.setPricingEngine(bondEngine);
806
807 Real cachedPrice1 = 99.298100;
808
809 Real price = bond1.cleanPrice();
810 if (std::fabs(x: price-cachedPrice1) > tolerance) {
811 BOOST_FAIL("failed to reproduce cached price:\n"
812 << std::fixed
813 << " calculated: " << price << "\n"
814 << " expected: " << cachedPrice1 << "\n"
815 << " error: " << price-cachedPrice1);
816 }
817
818 // varying coupons
819
820 std::vector<Rate> couponRates(4);
821 couponRates[0] = 0.02875;
822 couponRates[1] = 0.03;
823 couponRates[2] = 0.03125;
824 couponRates[3] = 0.0325;
825
826 FixedRateBond bond2(settlementDays, vars.faceAmount, sch,
827 couponRates,
828 ActualActual(ActualActual::ISMA),
829 ModifiedFollowing,
830 100.0, Date(30,November,2004));
831
832 bond2.setPricingEngine(bondEngine);
833
834 Real cachedPrice2 = 100.334149;
835
836 price = bond2.cleanPrice();
837 if (std::fabs(x: price-cachedPrice2) > tolerance) {
838 BOOST_FAIL("failed to reproduce cached price:\n"
839 << std::fixed
840 << " calculated: " << price << "\n"
841 << " expected: " << cachedPrice2 << "\n"
842 << " error: " << price-cachedPrice2);
843 }
844
845 // stub date
846
847 Schedule sch3(Date(30,November,2004),
848 Date(30,March,2009), Period(Semiannual),
849 UnitedStates(UnitedStates::GovernmentBond),
850 Unadjusted, Unadjusted, DateGeneration::Backward, false,
851 Date(), Date(30,November,2008));
852
853 FixedRateBond bond3(settlementDays, vars.faceAmount, sch3,
854 couponRates, ActualActual(ActualActual::ISMA),
855 ModifiedFollowing,
856 100.0, Date(30,November,2004));
857
858 bond3.setPricingEngine(bondEngine);
859
860 Real cachedPrice3 = 100.382794;
861
862 price = bond3.cleanPrice();
863 if (std::fabs(x: price-cachedPrice3) > tolerance) {
864 BOOST_FAIL("failed to reproduce cached price:\n"
865 << std::fixed
866 << " calculated: " << price << "\n"
867 << " expected: " << cachedPrice3 << "\n"
868 << " error: " << price-cachedPrice3);
869 }
870}
871
872
873void BondTest::testCachedFloating() {
874
875 BOOST_TEST_MESSAGE("Testing floating-rate bond prices against cached values...");
876
877 using namespace bonds_test;
878
879 bool usingAtParCoupons = IborCoupon::Settings::instance().usingAtParCoupons();
880
881 CommonVars vars;
882
883 Date today(22,November,2004);
884 Settings::instance().evaluationDate() = today;
885
886 Natural settlementDays = 1;
887
888 Handle<YieldTermStructure> riskFreeRate(flatRate(today,forward: 0.025,dc: Actual360()));
889 Handle<YieldTermStructure> discountCurve(flatRate(today,forward: 0.03,dc: Actual360()));
890
891 ext::shared_ptr<IborIndex> index(new USDLibor(6*Months, riskFreeRate));
892 Natural fixingDays = 1;
893
894 Real tolerance = 1.0e-6;
895
896 ext::shared_ptr<IborCouponPricer> pricer(new
897 BlackIborCouponPricer(Handle<OptionletVolatilityStructure>()));
898
899 // plain
900
901 Schedule sch(Date(30,November,2004),
902 Date(30,November,2008),
903 Period(Semiannual),
904 UnitedStates(UnitedStates::GovernmentBond),
905 ModifiedFollowing, ModifiedFollowing,
906 DateGeneration::Backward, false);
907
908 FloatingRateBond bond1(settlementDays, vars.faceAmount, sch,
909 index, ActualActual(ActualActual::ISMA),
910 ModifiedFollowing, fixingDays,
911 std::vector<Real>(), std::vector<Spread>(),
912 std::vector<Rate>(), std::vector<Rate>(),
913 false,
914 100.0, Date(30,November,2004));
915
916 ext::shared_ptr<PricingEngine> bondEngine(
917 new DiscountingBondEngine(riskFreeRate));
918 bond1.setPricingEngine(bondEngine);
919
920 setCouponPricer(leg: bond1.cashflows(),pricer);
921
922 Real cachedPrice1 = usingAtParCoupons ? 99.874646 : 99.874645;
923
924 Real price = bond1.cleanPrice();
925 if (std::fabs(x: price-cachedPrice1) > tolerance) {
926 BOOST_FAIL("failed to reproduce cached price:\n"
927 << std::fixed
928 << " calculated: " << price << "\n"
929 << " expected: " << cachedPrice1 << "\n"
930 << " error: " << price-cachedPrice1);
931 }
932
933 // different risk-free and discount curve
934
935 FloatingRateBond bond2(settlementDays, vars.faceAmount, sch,
936 index, ActualActual(ActualActual::ISMA),
937 ModifiedFollowing, fixingDays,
938 std::vector<Rate>(), std::vector<Spread>(),
939 std::vector<Rate>(), std::vector<Rate>(),
940 false,
941 100.0, Date(30,November,2004));
942
943 ext::shared_ptr<PricingEngine> bondEngine2(
944 new DiscountingBondEngine(discountCurve));
945 bond2.setPricingEngine(bondEngine2);
946
947 setCouponPricer(leg: bond2.cashflows(),pricer);
948
949 Real cachedPrice2 = 97.955904;
950
951 price = bond2.cleanPrice();
952 if (std::fabs(x: price-cachedPrice2) > tolerance) {
953 BOOST_FAIL("failed to reproduce cached price:\n"
954 << std::fixed
955 << " calculated: " << price << "\n"
956 << " expected: " << cachedPrice2 << "\n"
957 << " error: " << price-cachedPrice2);
958 }
959
960 // varying spread
961
962 std::vector<Rate> spreads(4);
963 spreads[0] = 0.001;
964 spreads[1] = 0.0012;
965 spreads[2] = 0.0014;
966 spreads[3] = 0.0016;
967
968 FloatingRateBond bond3(settlementDays, vars.faceAmount, sch,
969 index, ActualActual(ActualActual::ISMA),
970 ModifiedFollowing, fixingDays,
971 std::vector<Real>(), spreads,
972 std::vector<Rate>(), std::vector<Rate>(),
973 false,
974 100.0, Date(30,November,2004));
975
976 bond3.setPricingEngine(bondEngine2);
977
978 setCouponPricer(leg: bond3.cashflows(),pricer);
979
980 Real cachedPrice3 = usingAtParCoupons ? 98.495459 : 98.495458;
981
982 price = bond3.cleanPrice();
983 if (std::fabs(x: price-cachedPrice3) > tolerance) {
984 BOOST_FAIL("failed to reproduce cached price:\n"
985 << std::fixed
986 << " calculated: " << price << "\n"
987 << " expected: " << cachedPrice3 << "\n"
988 << " error: " << price-cachedPrice3);
989 }
990
991 Schedule sch2(Date(26, November, 2003), Date(26, November, 2007), Period(Semiannual),
992 UnitedStates(UnitedStates::GovernmentBond), ModifiedFollowing, ModifiedFollowing,
993 DateGeneration::Backward, false);
994 FloatingRateBond bond4(settlementDays, vars.faceAmount, sch2, index,
995 ActualActual(ActualActual::ISMA), ModifiedFollowing, fixingDays,
996 std::vector<Real>(), spreads, std::vector<Rate>(), std::vector<Rate>(), false, 100.0, Date(29, October, 2004), Period(6*Days));
997
998 index->addFixing(fixingDate: Date(25, May, 2004), fixing: 0.0402);
999 bond4.setPricingEngine(bondEngine2);
1000
1001 setCouponPricer(leg: bond4.cashflows(), pricer);
1002
1003 Real cachedPrice4 = usingAtParCoupons ? 98.892055 : 98.892346;
1004
1005 price = bond4.cleanPrice();
1006 if (std::fabs(x: price - cachedPrice4) > tolerance) {
1007 BOOST_FAIL("failed to reproduce cached price:\n"
1008 << std::fixed << " calculated: " << price << "\n"
1009 << " expected: " << cachedPrice4 << "\n"
1010 << " error: " << price - cachedPrice4);
1011 }
1012}
1013
1014void BondTest::testBrazilianCached() {
1015
1016 BOOST_TEST_MESSAGE(
1017 "Testing Brazilian public bond prices against Andima cached values...");
1018
1019 using namespace bonds_test;
1020
1021 CommonVars vars;
1022
1023 Natural settlementDays = 1;
1024 Real faceAmount = 1000.0;
1025 Date today(6,June,2007);
1026 Date issueDate(1,January,2007);
1027
1028 // The tolerance is high because Andima truncate yields
1029 Real tolerance = 1.0e-4;
1030
1031 // Reset evaluation date
1032 Settings::instance().evaluationDate() = today;
1033
1034 // NTN-F maturity dates
1035 std::vector<Date> maturityDates(6);
1036 maturityDates[0] = Date(1,January,2008);
1037 maturityDates[1] = Date(1,January,2010);
1038 maturityDates[2] = Date(1,July,2010);
1039 maturityDates[3] = Date(1,January,2012);
1040 maturityDates[4] = Date(1,January,2014);
1041 maturityDates[5] = Date(1,January,2017);
1042
1043 // Andima NTN-F yields
1044 std::vector<Rate> yields(6);
1045 yields[0] = 0.114614;
1046 yields[1] = 0.105726;
1047 yields[2] = 0.105328;
1048 yields[3] = 0.104283;
1049 yields[4] = 0.103218;
1050 yields[5] = 0.102948;
1051
1052 // Andima NTN-F prices
1053 std::vector<Rate> prices(6);
1054 prices[0] = 1034.63031372;
1055 prices[1] = 1030.09919487;
1056 prices[2] = 1029.98307160;
1057 prices[3] = 1028.13585068;
1058 prices[4] = 1028.33383817;
1059 prices[5] = 1026.19716497;
1060
1061 std::vector<InterestRate> couponRates(1);
1062 couponRates[0] = InterestRate(0.1, Thirty360(Thirty360::BondBasis), Compounded,Annual);
1063
1064 for (Size bondIndex = 0; bondIndex < maturityDates.size(); bondIndex++) {
1065
1066 InterestRate yield(yields[bondIndex],
1067 Business252(Brazil()),
1068 Compounded, Annual);
1069
1070 Schedule schedule(Date(1,January,2007),
1071 maturityDates[bondIndex], Period(Semiannual),
1072 Brazil(Brazil::Settlement),
1073 Unadjusted, Unadjusted,
1074 DateGeneration::Backward, false);
1075
1076 Leg coupons = FixedRateLeg(schedule)
1077 .withNotionals(faceAmount)
1078 .withCouponRates(couponRates)
1079 .withPaymentAdjustment(Following);
1080
1081 Bond bond(settlementDays,
1082 schedule.calendar(),
1083 issueDate,
1084 coupons);
1085
1086 Real cachedPrice = prices[bondIndex];
1087 Real price = faceAmount *
1088 (BondFunctions::cleanPrice(bond, yield: yield.rate(), dayCounter: yield.dayCounter(),
1089 compounding: yield.compounding(), frequency: yield.frequency(),
1090 settlementDate: today) + bond.accruedAmount(d: today)) / 100.0;
1091
1092 if (std::fabs(x: price-cachedPrice) > tolerance) {
1093 BOOST_ERROR("failed to reproduce Andima cached price:\n"
1094 << std::fixed
1095 << " calculated: " << price << "\n"
1096 << " expected: " << cachedPrice << "\n"
1097 << " error: " << price-cachedPrice << "\n"
1098 );
1099 }
1100 }
1101}
1102
1103void BondTest::testExCouponGilt() {
1104 BOOST_TEST_MESSAGE(
1105 "Testing ex-coupon UK Gilt price against market values...");
1106 /* UK Gilts have an exCouponDate 7 business days before the coupon
1107 is due (see <http://www.dmo.gov.uk/index.aspx?page=Gilts/Gilt_Faq>).
1108 On the exCouponDate the bond still trades cum-coupon so we use
1109 6 days below and UK calendar
1110
1111 Output verified with Bloomberg:
1112
1113 ISIN: GB0009997999
1114 Issue Date: February 29th, 1996
1115 Interest Accrue: February 29th, 1996
1116 First Coupon: June 7th, 1996
1117 Maturity: June 7th, 2021
1118 coupon: 8
1119 period: 6M
1120
1121 Settlement date: May 29th, 2013
1122 Test Price : 103
1123 Accrued : 38021.97802
1124 NPV : 106.8021978
1125 Yield : 7.495180593
1126 Yield->NPV : 106.8021978
1127 Yield->NPV->Price : 103
1128 Mod duration : 5.676044458
1129 Convexity : 0.4215314859
1130 PV 0.01 : 0.0606214023
1131
1132 Settlement date: May 30th, 2013
1133 Test Price : 103
1134 Accrued : -1758.241758
1135 NPV : 102.8241758
1136 Yield : 7.496183543
1137 Yield->NPV : 102.8241758
1138 Yield->NPV->Price : 103
1139 Mod duration : 5.892816328
1140 Convexity : 0.4375621862
1141 PV 0.01 : 0.06059239822
1142
1143 Settlement date: May 31st, 2013
1144 Test Price : 103
1145 Accrued : -1538.461538
1146 NPV : 102.8461538
1147 Yield : 7.495987492
1148 Yield->NPV : 102.8461539
1149 Yield->NPV->Price : 103
1150 Mod duration : 5.890186028
1151 Convexity : 0.4372394381
1152 PV 0.01 : 0.06057829784
1153 */
1154 struct test_case {
1155 Date settlementDate;
1156 Real testPrice;
1157 Real accruedAmount;
1158 Real NPV;
1159 Rate yield;
1160 Real duration;
1161 Real convexity;
1162 };
1163
1164 Calendar calendar = UnitedKingdom();
1165
1166 Natural settlementDays = 3;
1167
1168 Date issueDate(29, February, 1996);
1169 Date startDate(29, February, 1996);
1170 Date firstCouponDate(07, June, 1996);
1171 Date maturityDate(07, June, 2021);
1172
1173 Rate coupon = 0.08;
1174
1175 Period tenor = 6*Months;
1176 Period exCouponPeriod = 6*Days;
1177
1178 Compounding comp = Compounded;
1179 Frequency freq = Semiannual;
1180
1181 Schedule schedule = Schedule(startDate, maturityDate, tenor,
1182 NullCalendar(), Unadjusted, Unadjusted,
1183 DateGeneration::Forward, true, firstCouponDate);
1184 DayCounter dc = ActualActual(ActualActual::ISMA, schedule);
1185
1186 FixedRateBond bond(settlementDays, 100.0,
1187 schedule,
1188 std::vector<Rate>(1, coupon),
1189 dc, Unadjusted, 100.0,
1190 issueDate, calendar, exCouponPeriod, calendar);
1191
1192 const Leg& leg = bond.cashflows();
1193
1194 test_case cases[] = {
1195 { .settlementDate: Date(29,May,2013), .testPrice: 103.0,
1196 .accruedAmount: 3.8021978, .NPV: 106.8021978, .yield: 0.0749518,
1197 .duration: 5.6760445, .convexity: 42.1531486 },
1198 { .settlementDate: Date(30,May,2013), .testPrice: 103.0,
1199 .accruedAmount: -0.1758242, .NPV: 102.8241758, .yield: 0.0749618,
1200 .duration: 5.8928163, .convexity: 43.7562186 },
1201 { .settlementDate: Date(31,May,2013), .testPrice: 103.0,
1202 .accruedAmount: -0.1538462, .NPV: 102.8461538, .yield: 0.0749599,
1203 .duration: 5.8901860, .convexity: 43.7239438 }
1204 };
1205
1206 for (auto& i : cases) {
1207 Real accrued = bond.accruedAmount(d: i.settlementDate);
1208 ASSERT_CLOSE("accrued amount", i.settlementDate, accrued, i.accruedAmount, 1e-6);
1209
1210 Real npv = i.testPrice + accrued;
1211 ASSERT_CLOSE("NPV", i.settlementDate, npv, i.NPV, 1e-6);
1212
1213 Rate yield = CashFlows::yield(leg, npv, dayCounter: dc, compounding: comp, frequency: freq, includeSettlementDateFlows: false, settlementDate: i.settlementDate);
1214 ASSERT_CLOSE("yield", i.settlementDate, yield, i.yield, 1e-6);
1215
1216 Time duration = CashFlows::duration(leg, yield, dayCounter: dc, compounding: comp, frequency: freq, type: Duration::Modified, includeSettlementDateFlows: false,
1217 settlementDate: i.settlementDate);
1218 ASSERT_CLOSE("duration", i.settlementDate, duration, i.duration, 1e-6);
1219
1220 Real convexity = CashFlows::convexity(leg, yield, dayCounter: dc, compounding: comp, frequency: freq, includeSettlementDateFlows: false, settlementDate: i.settlementDate);
1221 ASSERT_CLOSE("convexity", i.settlementDate, convexity, i.convexity, 1e-6);
1222
1223 Real calcnpv = CashFlows::npv(leg, yield, dayCounter: dc, compounding: comp, frequency: freq, includeSettlementDateFlows: false, settlementDate: i.settlementDate);
1224 ASSERT_CLOSE("NPV from yield", i.settlementDate, calcnpv, i.NPV, 1e-6);
1225
1226 Real calcprice = calcnpv - accrued;
1227 ASSERT_CLOSE("price from yield", i.settlementDate, calcprice, i.testPrice, 1e-6);
1228 }
1229}
1230
1231
1232void BondTest::testExCouponAustralianBond() {
1233 BOOST_TEST_MESSAGE(
1234 "Testing ex-coupon Australian bond price against market values...");
1235 /* Australian Government Bonds have an exCouponDate 7 calendar
1236 days before the coupon is due. On the exCouponDate the bond
1237 trades ex-coupon so we use 7 days below and NullCalendar.
1238 AGB accrued interest is rounded to 3dp.
1239
1240 Output verified with Bloomberg:
1241
1242 ISIN: AU300TB01208
1243 Issue Date: June 10th, 2004
1244 Interest Accrue: February 15th, 2004
1245 First Coupon: August 15th, 2004
1246 Maturity: February 15th, 2017
1247 coupon: 6
1248 period: 6M
1249
1250 Settlement date: August 7th, 2014
1251 Test Price : 103
1252 Accrued : 28670
1253 NPV : 105.867
1254 Yield : 4.723814867
1255 Yield->NPV : 105.867
1256 Yield->NPV->Price : 103
1257 Mod duration : 2.262763296
1258 Convexity : 0.0654870275
1259 PV 0.01 : 0.02395519619
1260
1261 Settlement date: August 8th, 2014
1262 Test Price : 103
1263 Accrued : -1160
1264 NPV : 102.884
1265 Yield : 4.72354833
1266 Yield->NPV : 102.884
1267 Yield->NPV->Price : 103
1268 Mod duration : 2.325360055
1269 Convexity : 0.06725307785
1270 PV 0.01 : 0.02392423439
1271
1272 Settlement date: August 11th, 2014
1273 Test Price : 103
1274 Accrued : -660
1275 NPV : 102.934
1276 Yield : 4.719277687
1277 Yield->NPV : 102.934
1278 Yield->NPV->Price : 103
1279 Mod duration : 2.317320093
1280 Convexity : 0.06684074058
1281 PV 0.01 : 0.02385310264
1282 */
1283 struct test_case {
1284 Date settlementDate;
1285 Real testPrice;
1286 Real accruedAmount;
1287 Real NPV;
1288 Rate yield;
1289 Real duration;
1290 Real convexity;
1291 };
1292
1293 Calendar calendar = Australia();
1294
1295 Natural settlementDays = 3;
1296
1297 Date issueDate(10, June, 2004);
1298 Date startDate(15, February, 2004);
1299 Date firstCouponDate(15, August, 2004);
1300 Date maturityDate(15, February, 2017);
1301
1302 Rate coupon = 0.06;
1303
1304 Period tenor = 6*Months;
1305 Period exCouponPeriod = 7*Days;
1306
1307 Compounding comp = Compounded;
1308 Frequency freq = Semiannual;
1309
1310 Schedule schedule = Schedule(startDate, maturityDate, tenor,
1311 NullCalendar(), Unadjusted, Unadjusted,
1312 DateGeneration::Forward, true, firstCouponDate);
1313 DayCounter dc = ActualActual(ActualActual::ISMA, schedule);
1314
1315 FixedRateBond bond(settlementDays, 100.0,
1316 schedule,
1317 std::vector<Rate>(1, coupon),
1318 dc, Unadjusted, 100.0,
1319 issueDate, calendar, exCouponPeriod, NullCalendar());
1320
1321 const Leg& leg = bond.cashflows();
1322
1323 test_case cases[] = {
1324 { .settlementDate: Date(7,August,2014), .testPrice: 103.0,
1325 .accruedAmount: 2.8670, .NPV: 105.867, .yield: 0.04723,
1326 .duration: 2.26276, .convexity: 6.54870 },
1327 { .settlementDate: Date(8,August,2014), .testPrice: 103.0,
1328 .accruedAmount: -0.1160, .NPV: 102.884, .yield: 0.047235,
1329 .duration: 2.32536, .convexity: 6.72531 },
1330 { .settlementDate: Date(11,August,2014), .testPrice: 103.0,
1331 .accruedAmount: -0.0660, .NPV: 102.934, .yield: 0.04719,
1332 .duration: 2.31732, .convexity: 6.68407 }
1333 };
1334
1335 for (auto& i : cases) {
1336 Real accrued = bond.accruedAmount(d: i.settlementDate);
1337 ASSERT_CLOSE("accrued amount", i.settlementDate, accrued, i.accruedAmount, 1e-3);
1338
1339 Real npv = i.testPrice + accrued;
1340 ASSERT_CLOSE("NPV", i.settlementDate, npv, i.NPV, 1e-3);
1341
1342 Rate yield = CashFlows::yield(leg, npv, dayCounter: dc, compounding: comp, frequency: freq, includeSettlementDateFlows: false, settlementDate: i.settlementDate);
1343 ASSERT_CLOSE("yield", i.settlementDate, yield, i.yield, 1e-5);
1344
1345 Time duration = CashFlows::duration(leg, yield, dayCounter: dc, compounding: comp, frequency: freq, type: Duration::Modified, includeSettlementDateFlows: false,
1346 settlementDate: i.settlementDate);
1347 ASSERT_CLOSE("duration", i.settlementDate, duration, i.duration, 1e-5);
1348
1349 Real convexity = CashFlows::convexity(leg, yield, dayCounter: dc, compounding: comp, frequency: freq, includeSettlementDateFlows: false, settlementDate: i.settlementDate);
1350 ASSERT_CLOSE("convexity", i.settlementDate, convexity, i.convexity, 1e-4);
1351
1352 Real calcnpv = CashFlows::npv(leg, yield, dayCounter: dc, compounding: comp, frequency: freq, includeSettlementDateFlows: false, settlementDate: i.settlementDate);
1353 ASSERT_CLOSE("NPV from yield", i.settlementDate, calcnpv, i.NPV, 1e-3);
1354
1355 Real calcprice = calcnpv - accrued;
1356 ASSERT_CLOSE("price from yield", i.settlementDate, calcprice, i.testPrice, 1e-3);
1357 }
1358}
1359
1360/// <summary>
1361/// Test calculation of South African R2048 bond
1362/// This requires the use of the Schedule to be constructed
1363/// with a custom date vector
1364/// </summary>
1365void BondTest::testBondFromScheduleWithDateVector()
1366{
1367 BOOST_TEST_MESSAGE("Testing South African R2048 bond price using Schedule constructor with Date vector...");
1368 //When pricing bond from Yield To Maturity, use NullCalendar()
1369 Calendar calendar = NullCalendar();
1370
1371 Natural settlementDays = 3;
1372
1373 Date issueDate(29, June, 2012);
1374 Date today(7, September, 2015);
1375 Date evaluationDate = calendar.adjust(today);
1376 Date settlementDate = calendar.advance(date: evaluationDate, period: settlementDays * Days);
1377 Settings::instance().evaluationDate() = evaluationDate;
1378
1379 // For the schedule to generate correctly for Feb-28's, make maturity date on Feb 29
1380 Date maturityDate(29, February, 2048);
1381
1382 Rate coupon = 0.0875;
1383 Compounding comp = Compounded;
1384 Frequency freq = Semiannual;
1385
1386
1387
1388
1389 Period tenor = 6 * Months;
1390 Period exCouponPeriod = 10 * Days;
1391
1392 // Generate coupon dates for 31 Aug and end of Feb each year
1393 // For leap years, this will generate 29 Feb, but the bond
1394 // actually pays coupons on 28 Feb, regardsless of whether
1395 // it is a leap year or not.
1396 Schedule schedule(issueDate, maturityDate, tenor,
1397 NullCalendar(), Unadjusted, Unadjusted,
1398 DateGeneration::Backward, true);
1399
1400 // Adjust the 29 Feb's to 28 Feb
1401 std::vector<Date> dates;
1402 for (Size i = 0; i < schedule.size(); ++i) {
1403 Date d = schedule.date(i);
1404 if (d.month() == February && d.dayOfMonth() == 29)
1405 dates.emplace_back(args: 28, args: February, args: d.year());
1406 else
1407 dates.push_back(x: d);
1408 }
1409
1410 schedule = Schedule(dates,
1411 schedule.calendar(),
1412 schedule.businessDayConvention(),
1413 schedule.terminationDateBusinessDayConvention(),
1414 schedule.tenor(),
1415 schedule.rule(),
1416 schedule.endOfMonth(),
1417 schedule.isRegular());
1418 DayCounter dc = ActualActual(ActualActual::Bond, schedule);
1419 FixedRateBond bond(
1420 0,
1421 100.0,
1422 schedule,
1423 std::vector<Rate>(1, coupon),
1424 dc, Following, 100.0,
1425 issueDate, calendar,
1426 exCouponPeriod, calendar, Unadjusted, false);
1427
1428 // Yield as quoted in market
1429 InterestRate yield(0.09185, dc, comp, freq);
1430
1431 Real calculatedPrice = BondFunctions::dirtyPrice(bond, yield, settlementDate);
1432 Real expectedPrice = 95.75706;
1433 Real tolerance = 1e-5;
1434 if (std::fabs(x: calculatedPrice - expectedPrice) > tolerance) {
1435 BOOST_FAIL("failed to reproduce R2048 dirty price"
1436 << std::fixed << std::setprecision(5)
1437 << "\n expected: " << expectedPrice
1438 << "\n calculated: " << calculatedPrice);
1439 }
1440}
1441
1442void BondTest::testFixedBondWithGivenDates() {
1443
1444 BOOST_TEST_MESSAGE("Testing fixed-coupon bond built on schedule with given dates...");
1445
1446 using namespace bonds_test;
1447
1448 CommonVars vars;
1449
1450 Date today(22,November,2004);
1451 Settings::instance().evaluationDate() = today;
1452
1453 Natural settlementDays = 1;
1454
1455 Handle<YieldTermStructure> discountCurve(flatRate(today,forward: 0.03,dc: Actual360()));
1456
1457 Real tolerance = 1.0e-6;
1458
1459 ext::shared_ptr<PricingEngine> bondEngine(
1460 new DiscountingBondEngine(discountCurve));
1461 // plain
1462
1463 Schedule sch1(Date(30,November,2004),
1464 Date(30,November,2008), Period(Semiannual),
1465 UnitedStates(UnitedStates::GovernmentBond),
1466 Unadjusted, Unadjusted, DateGeneration::Backward, false);
1467 FixedRateBond bond1(settlementDays, vars.faceAmount, sch1,
1468 std::vector<Rate>(1, 0.02875),
1469 ActualActual(ActualActual::ISMA),
1470 ModifiedFollowing,
1471 100.0, Date(30,November,2004));
1472 bond1.setPricingEngine(bondEngine);
1473
1474 Schedule sch1_copy(sch1.dates(), UnitedStates(UnitedStates::GovernmentBond),
1475 Unadjusted, Unadjusted, Period(Semiannual),
1476 DateGeneration::Backward,
1477 false, std::vector<bool>(sch1.size()-1, true));
1478 FixedRateBond bond1_copy(settlementDays, vars.faceAmount, sch1_copy,
1479 std::vector<Rate>(1, 0.02875),
1480 ActualActual(ActualActual::ISMA),
1481 ModifiedFollowing,
1482 100.0, Date(30,November,2004));
1483 bond1_copy.setPricingEngine(bondEngine);
1484
1485 Real expected = bond1.cleanPrice();
1486 Real calculated = bond1_copy.cleanPrice();
1487 if (std::fabs(x: expected-calculated) > tolerance) {
1488 BOOST_FAIL("failed to reproduce cached price:\n"
1489 << std::fixed
1490 << " calculated: " << calculated << "\n"
1491 << " expected: " << expected << "\n"
1492 << " error: " << expected-calculated);
1493 }
1494
1495 // varying coupons
1496
1497 std::vector<Rate> couponRates(4);
1498 couponRates[0] = 0.02875;
1499 couponRates[1] = 0.03;
1500 couponRates[2] = 0.03125;
1501 couponRates[3] = 0.0325;
1502
1503 FixedRateBond bond2(settlementDays, vars.faceAmount, sch1,
1504 couponRates,
1505 ActualActual(ActualActual::ISMA),
1506 ModifiedFollowing,
1507 100.0, Date(30,November,2004));
1508 bond2.setPricingEngine(bondEngine);
1509
1510 FixedRateBond bond2_copy(settlementDays, vars.faceAmount, sch1_copy,
1511 couponRates,
1512 ActualActual(ActualActual::ISMA),
1513 ModifiedFollowing,
1514 100.0, Date(30,November,2004));
1515 bond2_copy.setPricingEngine(bondEngine);
1516
1517 expected = bond2.cleanPrice();
1518 calculated = bond2_copy.cleanPrice();
1519 if (std::fabs(x: expected-calculated) > tolerance) {
1520 BOOST_FAIL("failed to reproduce cached price:\n"
1521 << std::fixed
1522 << " calculated: " << calculated << "\n"
1523 << " expected: " << expected << "\n"
1524 << " error: " << expected-calculated);
1525 }
1526
1527
1528 // stub date
1529
1530 Schedule sch3(Date(30,November,2004),
1531 Date(30,March,2009), Period(Semiannual),
1532 UnitedStates(UnitedStates::GovernmentBond),
1533 Unadjusted, Unadjusted, DateGeneration::Backward, false,
1534 Date(), Date(30,November,2008));
1535 FixedRateBond bond3(settlementDays, vars.faceAmount, sch3,
1536 couponRates,
1537 Actual360(),
1538 ModifiedFollowing,
1539 100.0, Date(30,November,2004));
1540 bond3.setPricingEngine(bondEngine);
1541
1542 Schedule sch3_copy(sch3.dates(), UnitedStates(UnitedStates::GovernmentBond),
1543 Unadjusted, Unadjusted, Period(Semiannual),
1544 DateGeneration::Backward,
1545 false, std::vector<bool>(sch3.size()-1, true));
1546 FixedRateBond bond3_copy(settlementDays, vars.faceAmount, sch3_copy,
1547 couponRates,
1548 Actual360(),
1549 ModifiedFollowing,
1550 100.0, Date(30,November,2004));
1551 bond3_copy.setPricingEngine(bondEngine);
1552
1553 expected = bond3.cleanPrice();
1554 calculated = bond3_copy.cleanPrice();
1555 if (std::fabs(x: expected-calculated) > tolerance) {
1556 BOOST_FAIL("failed to reproduce cached price:\n"
1557 << std::fixed
1558 << " calculated: " << calculated << "\n"
1559 << " expected: " << expected << "\n"
1560 << " error: " << expected-calculated);
1561 }
1562}
1563
1564void BondTest::testRiskyBondWithGivenDates() {
1565
1566 BOOST_TEST_MESSAGE("Testing risky bond engine...");
1567
1568 using namespace bonds_test;
1569
1570 CommonVars vars;
1571
1572 Date today(22, November, 2005);
1573 Settings::instance().evaluationDate() = today;
1574
1575 // Probability Structure
1576 Handle<Quote> hazardRate(ext::shared_ptr<Quote>(new SimpleQuote(0.1)));
1577 Handle<DefaultProbabilityTermStructure> defaultProbability(
1578 ext::shared_ptr<DefaultProbabilityTermStructure>(
1579 new FlatHazardRate(0, TARGET(), hazardRate, Actual360())));
1580
1581 // Yield term structure
1582 RelinkableHandle<YieldTermStructure> riskFree;
1583 riskFree.linkTo(h: ext::shared_ptr<YieldTermStructure>(new FlatForward(today, 0.02, Actual360())));
1584 Schedule sch1(Date(30, November, 2004), Date(30, November, 2008), Period(Semiannual),
1585 UnitedStates(UnitedStates::GovernmentBond), Unadjusted, Unadjusted,
1586 DateGeneration::Backward, false);
1587
1588 // Create Bond
1589 Natural settlementDays = 1;
1590 std::vector<Real> notionals = {0.0167, 0.023, 0.03234, 0.034, 0.038, 0.042, 0.047, 0.053};
1591
1592 std::vector<Rate> couponRates(4);
1593 couponRates[0] = 0.02875;
1594 couponRates[1] = 0.03;
1595 couponRates[2] = 0.03125;
1596 couponRates[3] = 0.0325;
1597 Real recoveryRate = 0.4;
1598
1599 FixedRateBond bond(settlementDays, vars.faceAmount, sch1, couponRates,
1600 ActualActual(ActualActual::ISMA), ModifiedFollowing, 100.0,
1601 Date(20, November, 2004));
1602
1603 // Create Engine
1604 ext::shared_ptr<PricingEngine> bondEngine(new RiskyBondEngine(defaultProbability, recoveryRate, riskFree));
1605 bond.setPricingEngine(bondEngine);
1606
1607 // Calculate and validate NPV and price
1608 Real expected = 888458.819055;
1609 Real calculated = bond.NPV();
1610 Real tolerance = 1.0e-6;
1611 if (std::fabs(x: expected - calculated) > tolerance) {
1612 BOOST_FAIL("Failed to reproduce risky bond NPV:\n"
1613 << std::fixed
1614 << " calculated: " << calculated << "\n"
1615 << " expected: " << expected << "\n"
1616 << " error: " << expected - calculated);
1617 }
1618
1619 expected = 87.407883;
1620 calculated = bond.cleanPrice();
1621 if (std::fabs(x: expected - calculated) > tolerance) {
1622 BOOST_FAIL("Failed to reproduce risky bond price:\n"
1623 << std::fixed
1624 << " calculated: " << calculated << "\n"
1625 << " expected: " << expected << "\n"
1626 << " error: " << expected - calculated);
1627 }
1628}
1629
1630
1631void BondTest::testFixedRateBondWithArbitrarySchedule() {
1632 BOOST_TEST_MESSAGE("Testing fixed-rate bond with arbitrary schedule...");
1633 Calendar calendar = NullCalendar();
1634
1635 Natural settlementDays = 3;
1636
1637 Date today(1, January, 2019);
1638 Settings::instance().evaluationDate() = today;
1639
1640 // For the schedule to generate correctly for Feb-28's, make maturity date on Feb 29
1641 std::vector<Date> dates(4);
1642 dates[0] = Date(1, February, 2019);
1643 dates[1] = Date(7, February, 2019);
1644 dates[2] = Date(1, April, 2019);
1645 dates[3] = Date(27, May, 2019);
1646
1647 Schedule schedule(dates, calendar, Unadjusted);
1648
1649 Rate coupon = 0.01;
1650 DayCounter dc = Actual365Fixed();
1651
1652 FixedRateBond bond(
1653 settlementDays,
1654 100.0,
1655 schedule,
1656 std::vector<Rate>(1, coupon),
1657 dc, Following, 100.0);
1658
1659 if (bond.frequency() != NoFrequency) {
1660 BOOST_ERROR("unexpected frequency: " << bond.frequency());
1661 }
1662
1663 Handle<YieldTermStructure> discountCurve(flatRate(today, forward: 0.03, dc: Actual360()));
1664 bond.setPricingEngine(ext::shared_ptr<PricingEngine>(new DiscountingBondEngine(discountCurve)));
1665
1666 BOOST_CHECK_NO_THROW(bond.cleanPrice());
1667}
1668
1669
1670void BondTest::testThirty360BondWithSettlementOn31st(){
1671 BOOST_TEST_MESSAGE(
1672 "Testing Thirty/360 bond with settlement on 31st of the month...");
1673
1674 // cusip 3130A0X70, data is from Bloomberg
1675 Settings::instance().evaluationDate() = Date(28, July, 2017);
1676
1677 Date datedDate(13, February, 2014);
1678 Date settlement(31, July, 2017);
1679 Date maturity(13, August, 2018);
1680
1681 DayCounter dayCounter = Thirty360(Thirty360::USA);
1682 Compounding compounding = Compounded;
1683
1684 Schedule fixedBondSchedule(datedDate,
1685 maturity,
1686 Period(Semiannual),
1687 UnitedStates(UnitedStates::GovernmentBond),
1688 Unadjusted, Unadjusted, DateGeneration::Forward, false);
1689
1690 FixedRateBond fixedRateBond(
1691 1,
1692 100,
1693 fixedBondSchedule,
1694 std::vector<Rate>(1, 0.015),
1695 dayCounter,
1696 Unadjusted,
1697 100.0);
1698
1699 Real cleanPrice = 100.0;
1700
1701 Real yield = BondFunctions::yield(bond: fixedRateBond, price: cleanPrice, dayCounter, compounding, frequency: Semiannual, settlementDate: settlement);
1702 ASSERT_CLOSE("yield", settlement, yield, 0.015, 1e-4);
1703
1704 Real duration = BondFunctions::duration(bond: fixedRateBond, yield: InterestRate(yield, dayCounter, compounding, Semiannual), type: Duration::Macaulay, settlementDate: settlement);
1705 ASSERT_CLOSE("duration", settlement, duration, 1.022, 1e-3);
1706
1707 Real convexity = BondFunctions::convexity(bond: fixedRateBond, yield: InterestRate(yield, dayCounter, compounding, Semiannual), settlementDate: settlement)/100;
1708 ASSERT_CLOSE("convexity", settlement, convexity, 0.015, 1e-3);
1709
1710 Real accrued = BondFunctions::accruedAmount(bond: fixedRateBond, settlementDate: settlement);
1711 ASSERT_CLOSE("accrued", settlement, accrued, 0.7, 1e-6);
1712}
1713
1714test_suite* BondTest::suite() {
1715 auto* suite = BOOST_TEST_SUITE("Bond tests");
1716
1717 suite->add(QUANTLIB_TEST_CASE(&BondTest::testYield));
1718 suite->add(QUANTLIB_TEST_CASE(&BondTest::testAtmRate));
1719 suite->add(QUANTLIB_TEST_CASE(&BondTest::testZspread));
1720 suite->add(QUANTLIB_TEST_CASE(&BondTest::testTheoretical));
1721 suite->add(QUANTLIB_TEST_CASE(&BondTest::testCached));
1722 suite->add(QUANTLIB_TEST_CASE(&BondTest::testCachedZero));
1723 suite->add(QUANTLIB_TEST_CASE(&BondTest::testCachedFixed));
1724 suite->add(QUANTLIB_TEST_CASE(&BondTest::testCachedFloating));
1725 suite->add(QUANTLIB_TEST_CASE(&BondTest::testBrazilianCached));
1726 suite->add(QUANTLIB_TEST_CASE(&BondTest::testFixedBondWithGivenDates));
1727 suite->add(QUANTLIB_TEST_CASE(&BondTest::testRiskyBondWithGivenDates));
1728 suite->add(QUANTLIB_TEST_CASE(&BondTest::testExCouponGilt));
1729 suite->add(QUANTLIB_TEST_CASE(&BondTest::testExCouponAustralianBond));
1730 suite->add(QUANTLIB_TEST_CASE(&BondTest::testBondFromScheduleWithDateVector));
1731 suite->add(QUANTLIB_TEST_CASE(&BondTest::testFixedRateBondWithArbitrarySchedule));
1732 suite->add(QUANTLIB_TEST_CASE(&BondTest::testThirty360BondWithSettlementOn31st));
1733 return suite;
1734}
1735
1736

source code of quantlib/test-suite/bonds.cpp