[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) 2009, 2012 StatPro Italia srl
5
6 This file is part of QuantLib, a free-software/open-source library
7 for financial quantitative analysts and developers - http://quantlib.org/
8
9 QuantLib is free software: you can redistribute it and/or modify it
10 under the terms of the QuantLib license. You should have received a
11 copy of the license along with this program; if not, please email
12 <quantlib-dev@lists.sf.net>. The license is also available online at
13 <http://quantlib.org/license.shtml>.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
20#include "cashflows.hpp"
21#include "utilities.hpp"
22#include <ql/cashflows/cashflows.hpp>
23#include <ql/cashflows/simplecashflow.hpp>
24#include <ql/cashflows/fixedratecoupon.hpp>
25#include <ql/cashflows/floatingratecoupon.hpp>
26#include <ql/cashflows/iborcoupon.hpp>
27#include <ql/cashflows/overnightindexedcoupon.hpp>
28#include <ql/cashflows/couponpricer.hpp>
29#include <ql/termstructures/volatility/optionlet/constantoptionletvol.hpp>
30#include <ql/quotes/simplequote.hpp>
31#include <ql/time/calendars/target.hpp>
32#include <ql/time/daycounters/actualactual.hpp>
33#include <ql/time/schedule.hpp>
34#include <ql/indexes/ibor/euribor.hpp>
35#include <ql/indexes/ibor/usdlibor.hpp>
36#include <ql/indexes/ibor/sofr.hpp>
37#include <ql/optional.hpp>
38#include <ql/settings.hpp>
39#include <iomanip>
40
41using namespace QuantLib;
42using namespace boost::unit_test_framework;
43
44void CashFlowsTest::testSettings() {
45
46 BOOST_TEST_MESSAGE("Testing cash-flow settings...");
47
48 Date today = Date::todaysDate();
49 Settings::instance().evaluationDate() = today;
50
51 // cash flows at T+0, T+1, T+2
52 std::vector<ext::shared_ptr<CashFlow> > leg;
53 leg.reserve(n: 3);
54 for (Integer i = 0; i < 3; ++i)
55 leg.push_back(x: ext::shared_ptr<CashFlow>(new SimpleCashFlow(1.0, today+i)));
56
57
58 #define CHECK_INCLUSION(n, days, expected) \
59 if ((!leg[n]->hasOccurred(today+days)) != expected) { \
60 BOOST_ERROR("cashflow at T+" << n << " " \
61 << (expected ? "not" : "") << "included" \
62 << " at T+" << days); \
63 }
64
65 // case 1: don't include reference-date payments, no override at
66 // today's date
67
68 Settings::instance().includeReferenceDateEvents() = false;
69 Settings::instance().includeTodaysCashFlows() = ext::nullopt;
70
71 CHECK_INCLUSION(0, 0, false);
72 CHECK_INCLUSION(0, 1, false);
73
74 CHECK_INCLUSION(1, 0, true);
75 CHECK_INCLUSION(1, 1, false);
76 CHECK_INCLUSION(1, 2, false);
77
78 CHECK_INCLUSION(2, 1, true);
79 CHECK_INCLUSION(2, 2, false);
80 CHECK_INCLUSION(2, 3, false);
81
82 // case 2: same, but with explicit setting at today's date
83
84 Settings::instance().includeReferenceDateEvents() = false;
85 Settings::instance().includeTodaysCashFlows() = false;
86
87 CHECK_INCLUSION(0, 0, false);
88 CHECK_INCLUSION(0, 1, false);
89
90 CHECK_INCLUSION(1, 0, true);
91 CHECK_INCLUSION(1, 1, false);
92 CHECK_INCLUSION(1, 2, false);
93
94 CHECK_INCLUSION(2, 1, true);
95 CHECK_INCLUSION(2, 2, false);
96 CHECK_INCLUSION(2, 3, false);
97
98 // case 3: do include reference-date payments, no override at
99 // today's date
100
101 Settings::instance().includeReferenceDateEvents() = true;
102 Settings::instance().includeTodaysCashFlows() = ext::nullopt;
103
104 CHECK_INCLUSION(0, 0, true);
105 CHECK_INCLUSION(0, 1, false);
106
107 CHECK_INCLUSION(1, 0, true);
108 CHECK_INCLUSION(1, 1, true);
109 CHECK_INCLUSION(1, 2, false);
110
111 CHECK_INCLUSION(2, 1, true);
112 CHECK_INCLUSION(2, 2, true);
113 CHECK_INCLUSION(2, 3, false);
114
115 // case 4: do include reference-date payments, explicit (and same)
116 // setting at today's date
117
118 Settings::instance().includeReferenceDateEvents() = true;
119 Settings::instance().includeTodaysCashFlows() = true;
120
121 CHECK_INCLUSION(0, 0, true);
122 CHECK_INCLUSION(0, 1, false);
123
124 CHECK_INCLUSION(1, 0, true);
125 CHECK_INCLUSION(1, 1, true);
126 CHECK_INCLUSION(1, 2, false);
127
128 CHECK_INCLUSION(2, 1, true);
129 CHECK_INCLUSION(2, 2, true);
130 CHECK_INCLUSION(2, 3, false);
131
132 // case 5: do include reference-date payments, override at
133 // today's date
134
135 Settings::instance().includeReferenceDateEvents() = true;
136 Settings::instance().includeTodaysCashFlows() = false;
137
138 CHECK_INCLUSION(0, 0, false);
139 CHECK_INCLUSION(0, 1, false);
140
141 CHECK_INCLUSION(1, 0, true);
142 CHECK_INCLUSION(1, 1, true);
143 CHECK_INCLUSION(1, 2, false);
144
145 CHECK_INCLUSION(2, 1, true);
146 CHECK_INCLUSION(2, 2, true);
147 CHECK_INCLUSION(2, 3, false);
148
149
150 // no discount to make calculations easier
151 InterestRate no_discount(0.0, Actual365Fixed(), Continuous, Annual);
152
153 #define CHECK_NPV(includeRef, expected) \
154 do { \
155 Real NPV = CashFlows::npv(leg, no_discount, includeRef, today); \
156 if (std::fabs(NPV - expected) > 1e-6) { \
157 BOOST_ERROR("NPV mismatch:\n" \
158 << " calculated: " << NPV << "\n" \
159 << " expected: " << expected); \
160 } \
161 } while (false);
162
163 // no override
164 Settings::instance().includeTodaysCashFlows() = ext::nullopt;
165
166 CHECK_NPV(false, 2.0);
167 CHECK_NPV(true, 3.0);
168
169 // override
170 Settings::instance().includeTodaysCashFlows() = false;
171
172 CHECK_NPV(false, 2.0);
173 CHECK_NPV(true, 2.0);
174
175}
176
177void CashFlowsTest::testAccessViolation() {
178 BOOST_TEST_MESSAGE("Testing dynamic cast of coupon in Black pricer...");
179
180 Date todaysDate(7, April, 2010);
181 Date settlementDate(9, April, 2010);
182 Settings::instance().evaluationDate() = todaysDate;
183 Calendar calendar = TARGET();
184
185 Handle<YieldTermStructure> rhTermStructure(
186 flatRate(today: settlementDate, forward: 0.04875825, dc: Actual365Fixed()));
187
188 Volatility volatility = 0.10;
189 Handle<OptionletVolatilityStructure> vol;
190 vol = Handle<OptionletVolatilityStructure>(
191 ext::shared_ptr<OptionletVolatilityStructure>(
192 new ConstantOptionletVolatility(
193 2,
194 calendar,
195 ModifiedFollowing,
196 volatility,
197 Actual365Fixed())));
198
199 ext::shared_ptr<IborIndex> index3m (new USDLibor(3*Months,
200 rhTermStructure));
201
202 Date payDate(20, December, 2013);
203 Date startDate(20, September, 2013);
204 Date endDate(20, December, 2013);
205 Rate spread = 0.0115;
206 ext::shared_ptr<IborCouponPricer> pricer(new BlackIborCouponPricer(vol));
207 ext::shared_ptr<FloatingRateCoupon> coupon(
208 new FloatingRateCoupon(payDate,100, startDate, endDate, 2,
209 index3m, 1.0 , spread / 100));
210 coupon->setPricer(pricer);
211
212 try {
213 // this caused an access violation in version 1.0
214 coupon->amount();
215 } catch (Error&) {
216 // ok; proper exception thrown
217 }
218}
219
220void CashFlowsTest::testDefaultSettlementDate() {
221 BOOST_TEST_MESSAGE("Testing default evaluation date in cashflows methods...");
222 Date today = Settings::instance().evaluationDate();
223 Schedule schedule =
224 MakeSchedule()
225 .from(effectiveDate: today-2*Months).to(terminationDate: today+4*Months)
226 .withFrequency(Semiannual)
227 .withCalendar(TARGET())
228 .withConvention(Unadjusted)
229 .backwards();
230
231 Leg leg = FixedRateLeg(schedule)
232 .withNotionals(100.0)
233 .withCouponRates(0.03, paymentDayCounter: Actual360())
234 .withPaymentCalendar(TARGET())
235 .withPaymentAdjustment(Following);
236
237 Time accruedPeriod = CashFlows::accruedPeriod(leg, includeSettlementDateFlows: false);
238 if (accruedPeriod == 0.0)
239 BOOST_ERROR("null accrued period with default settlement date");
240
241 Date::serial_type accruedDays = CashFlows::accruedDays(leg, includeSettlementDateFlows: false);
242 if (accruedDays == 0)
243 BOOST_ERROR("no accrued days with default settlement date");
244
245 Real accruedAmount = CashFlows::accruedAmount(leg, includeSettlementDateFlows: false);
246 if (accruedAmount == 0.0)
247 BOOST_ERROR("null accrued amount with default settlement date");
248}
249
250void CashFlowsTest::testNullFixingDays() {
251 BOOST_TEST_MESSAGE("Testing ibor leg construction with null fixing days...");
252 Date today = Settings::instance().evaluationDate();
253 Schedule schedule =
254 MakeSchedule()
255 .from(effectiveDate: today-2*Months).to(terminationDate: today+4*Months)
256 .withFrequency(Semiannual)
257 .withCalendar(TARGET())
258 .withConvention(Following)
259 .backwards();
260
261 ext::shared_ptr<IborIndex> index(new USDLibor(6*Months));
262 Leg leg = IborLeg(schedule, index)
263 .withNotionals(notional: 100.0)
264 // this can happen with default values, and caused an
265 // exception when the null was not managed properly
266 .withFixingDays(fixingDays: Null<Natural>());
267}
268
269void CashFlowsTest::testExCouponDates() {
270 BOOST_TEST_MESSAGE("Testing ex-coupon date calculation...");
271
272 Date today = Date::todaysDate();
273 Schedule schedule = MakeSchedule()
274 .from(effectiveDate: today)
275 .to(terminationDate: today + 5 * Years)
276 .withFrequency(Monthly)
277 .withCalendar(TARGET())
278 .withConvention(Following);
279
280 // no ex-coupon dates
281 Leg l1 = FixedRateLeg(schedule).withNotionals(100.0).withCouponRates(0.03, paymentDayCounter: Actual360());
282 for (auto& i : l1) {
283 ext::shared_ptr<Coupon> c = ext::dynamic_pointer_cast<Coupon>(r: i);
284 if (c->exCouponDate() != Date()) {
285 BOOST_ERROR("ex-coupon date found (none expected)");
286 }
287 }
288
289 // same for floating legs
290 ext::shared_ptr<IborIndex> index(new Euribor3M);
291 Leg l2 = IborLeg(schedule, index).withNotionals(notional: 100.0);
292 for (auto& i : l2) {
293 ext::shared_ptr<Coupon> c = ext::dynamic_pointer_cast<Coupon>(r: i);
294 if (c->exCouponDate() != Date()) {
295 BOOST_ERROR("ex-coupon date found (none expected)");
296 }
297 }
298
299 // calendar days
300 Leg l5 = FixedRateLeg(schedule)
301 .withNotionals(100.0)
302 .withCouponRates(0.03, paymentDayCounter: Actual360())
303 .withExCouponPeriod(Period(2, Days), NullCalendar(), Unadjusted, endOfMonth: false);
304 for (auto& i : l5) {
305 ext::shared_ptr<Coupon> c = ext::dynamic_pointer_cast<Coupon>(r: i);
306 Date expected = c->accrualEndDate() - 2;
307 if (c->exCouponDate() != expected) {
308 BOOST_ERROR("ex-coupon date = " << c->exCouponDate() << " (" << expected
309 << " expected)");
310 }
311 }
312
313 Leg l6 = IborLeg(schedule, index)
314 .withNotionals(notional: 100.0)
315 .withExCouponPeriod(Period(2, Days), NullCalendar(), Unadjusted, endOfMonth: false);
316 for (auto& i : l6) {
317 ext::shared_ptr<Coupon> c = ext::dynamic_pointer_cast<Coupon>(r: i);
318 Date expected = c->accrualEndDate() - 2;
319 if (c->exCouponDate() != expected) {
320 BOOST_ERROR("ex-coupon date = " << c->exCouponDate() << " (" << expected
321 << " expected)");
322 }
323 }
324
325 // business days
326 Leg l7 = FixedRateLeg(schedule)
327 .withNotionals(100.0)
328 .withCouponRates(0.03, paymentDayCounter: Actual360())
329 .withExCouponPeriod(Period(2, Days), TARGET(), Preceding, endOfMonth: false);
330 for (auto& i : l7) {
331 ext::shared_ptr<Coupon> c = ext::dynamic_pointer_cast<Coupon>(r: i);
332 Date expected = TARGET().advance(c->accrualEndDate(), n: -2, unit: Days);
333 if (c->exCouponDate() != expected) {
334 BOOST_ERROR("ex-coupon date = " << c->exCouponDate() << " (" << expected
335 << " expected)");
336 }
337 }
338
339 Leg l8 = IborLeg(schedule, index)
340 .withNotionals(notional: 100.0)
341 .withExCouponPeriod(Period(2, Days), TARGET(), Preceding, endOfMonth: false);
342 for (auto& i : l8) {
343 ext::shared_ptr<Coupon> c = ext::dynamic_pointer_cast<Coupon>(r: i);
344 Date expected = TARGET().advance(c->accrualEndDate(), n: -2, unit: Days);
345 if (c->exCouponDate() != expected) {
346 BOOST_ERROR("ex-coupon date = " << c->exCouponDate() << " (" << expected
347 << " expected)");
348 }
349 }
350}
351
352void CashFlowsTest::testIrregularFirstCouponReferenceDatesAtEndOfMonth() {
353 BOOST_TEST_MESSAGE("Testing irregular first coupon reference dates with end of month enabled...");
354 Schedule schedule =
355 MakeSchedule()
356 .from(effectiveDate: Date(17, January, 2017)).to(terminationDate: Date(28, February, 2018))
357 .withFrequency(Semiannual)
358 .withConvention(Unadjusted)
359 .endOfMonth()
360 .backwards();
361
362 Leg leg = FixedRateLeg(schedule)
363 .withNotionals(100.0)
364 .withCouponRates(0.01, paymentDayCounter: Actual360());
365
366 ext::shared_ptr<Coupon> firstCoupon =
367 ext::dynamic_pointer_cast<Coupon>(r: leg.front());
368
369 if (firstCoupon->referencePeriodStart() != Date(31, August, 2016))
370 BOOST_ERROR("Expected reference start date at end of month, "
371 "got " << firstCoupon->referencePeriodStart());
372}
373
374void CashFlowsTest::testIrregularLastCouponReferenceDatesAtEndOfMonth() {
375 BOOST_TEST_MESSAGE("Testing irregular last coupon reference dates with end of month enabled...");
376 Schedule schedule =
377 MakeSchedule()
378 .from(effectiveDate: Date(17, January, 2017)).to(terminationDate: Date(15, September, 2018))
379 .withNextToLastDate(d: Date(28, February, 2018))
380 .withFrequency(Semiannual)
381 .withConvention(Unadjusted)
382 .endOfMonth()
383 .backwards();
384
385 Leg leg = FixedRateLeg(schedule)
386 .withNotionals(100.0)
387 .withCouponRates(0.01, paymentDayCounter: Actual360());
388
389 ext::shared_ptr<Coupon> lastCoupon =
390 ext::dynamic_pointer_cast<Coupon>(r: leg.back());
391
392 if (lastCoupon->referencePeriodEnd() != Date(31, August, 2018))
393 BOOST_ERROR("Expected reference end date at end of month, "
394 "got " << lastCoupon->referencePeriodEnd());
395}
396
397void CashFlowsTest::testPartialScheduleLegConstruction() {
398 BOOST_TEST_MESSAGE("Testing leg construction with partial schedule...");
399 // schedule with irregular first and last period
400 Schedule schedule = MakeSchedule()
401 .from(effectiveDate: Date(15, September, 2017))
402 .to(terminationDate: Date(30, September, 2020))
403 .withNextToLastDate(d: Date(25, September, 2020))
404 .withFrequency(Semiannual)
405 .backwards();
406 // same schedule, date based, with metadata
407 Schedule schedule2(schedule.dates(), NullCalendar(), Unadjusted, Unadjusted,
408 6 * Months, ext::nullopt, schedule.endOfMonth(),
409 schedule.isRegular());
410 // same schedule, date based, without metadata
411 Schedule schedule3(schedule.dates());
412
413 // fixed rate legs based on the three schedule
414 Leg leg = FixedRateLeg(schedule).withNotionals(100.0).withCouponRates(
415 0.01, paymentDayCounter: ActualActual(ActualActual::ISMA));
416 Leg leg2 = FixedRateLeg(schedule2).withNotionals(100.0).withCouponRates(
417 0.01, paymentDayCounter: ActualActual(ActualActual::ISMA));
418 Leg leg3 = FixedRateLeg(schedule3).withNotionals(100.0).withCouponRates(
419 0.01, paymentDayCounter: ActualActual(ActualActual::ISMA));
420
421 // check reference period of first and last coupon in all variants
422 // for the first two we expect a 6M reference period, for the
423 // third it can not be constructed, so should be equal to the
424 // respective schedule period
425 ext::shared_ptr<FixedRateCoupon> firstCpn =
426 ext::dynamic_pointer_cast<FixedRateCoupon>(r: leg.front());
427 ext::shared_ptr<FixedRateCoupon> lastCpn =
428 ext::dynamic_pointer_cast<FixedRateCoupon>(r: leg.back());
429 BOOST_REQUIRE(firstCpn != nullptr);
430 BOOST_REQUIRE(lastCpn != nullptr);
431 BOOST_CHECK_EQUAL(firstCpn->referencePeriodStart(), Date(25, Mar, 2017));
432 BOOST_CHECK_EQUAL(firstCpn->referencePeriodEnd(), Date(25, Sep, 2017));
433 BOOST_CHECK_EQUAL(lastCpn->referencePeriodStart(), Date(25, Sep, 2020));
434 BOOST_CHECK_EQUAL(lastCpn->referencePeriodEnd(), Date(25, Mar, 2021));
435
436 ext::shared_ptr<FixedRateCoupon> firstCpn2 =
437 ext::dynamic_pointer_cast<FixedRateCoupon>(r: leg2.front());
438 ext::shared_ptr<FixedRateCoupon> lastCpn2 =
439 ext::dynamic_pointer_cast<FixedRateCoupon>(r: leg2.back());
440 BOOST_REQUIRE(firstCpn2 != nullptr);
441 BOOST_REQUIRE(lastCpn2 != nullptr);
442 BOOST_CHECK_EQUAL(firstCpn2->referencePeriodStart(), Date(25, Mar, 2017));
443 BOOST_CHECK_EQUAL(firstCpn2->referencePeriodEnd(), Date(25, Sep, 2017));
444 BOOST_CHECK_EQUAL(lastCpn2->referencePeriodStart(), Date(25, Sep, 2020));
445 BOOST_CHECK_EQUAL(lastCpn2->referencePeriodEnd(), Date(25, Mar, 2021));
446
447 ext::shared_ptr<FixedRateCoupon> firstCpn3 =
448 ext::dynamic_pointer_cast<FixedRateCoupon>(r: leg3.front());
449 ext::shared_ptr<FixedRateCoupon> lastCpn3 =
450 ext::dynamic_pointer_cast<FixedRateCoupon>(r: leg3.back());
451 BOOST_REQUIRE(firstCpn3 != nullptr);
452 BOOST_REQUIRE(lastCpn3 != nullptr);
453 BOOST_CHECK_EQUAL(firstCpn3->referencePeriodStart(), Date(15, Sep, 2017));
454 BOOST_CHECK_EQUAL(firstCpn3->referencePeriodEnd(), Date(25, Sep, 2017));
455 BOOST_CHECK_EQUAL(lastCpn3->referencePeriodStart(), Date(25, Sep, 2020));
456 BOOST_CHECK_EQUAL(lastCpn3->referencePeriodEnd(), Date(30, Sep, 2020));
457
458 // same check as above for a floating leg
459 ext::shared_ptr<IborIndex> iborIndex =
460 ext::make_shared<USDLibor>(args: 3 * Months);
461 Leg legf = IborLeg(schedule, iborIndex)
462 .withNotionals(notional: 100.0)
463 .withPaymentDayCounter(ActualActual(ActualActual::ISMA));
464 Leg legf2 = IborLeg(schedule2, iborIndex)
465 .withNotionals(notional: 100.0)
466 .withPaymentDayCounter(ActualActual(ActualActual::ISMA));
467 Leg legf3 = IborLeg(schedule3, iborIndex)
468 .withNotionals(notional: 100.0)
469 .withPaymentDayCounter(ActualActual(ActualActual::ISMA));
470
471 ext::shared_ptr<FloatingRateCoupon> firstCpnF =
472 ext::dynamic_pointer_cast<FloatingRateCoupon>(r: legf.front());
473 ext::shared_ptr<FloatingRateCoupon> lastCpnF =
474 ext::dynamic_pointer_cast<FloatingRateCoupon>(r: legf.back());
475 BOOST_REQUIRE(firstCpnF != nullptr);
476 BOOST_REQUIRE(lastCpnF != nullptr);
477 BOOST_CHECK_EQUAL(firstCpnF->referencePeriodStart(), Date(25, Mar, 2017));
478 BOOST_CHECK_EQUAL(firstCpnF->referencePeriodEnd(), Date(25, Sep, 2017));
479 BOOST_CHECK_EQUAL(lastCpnF->referencePeriodStart(), Date(25, Sep, 2020));
480 BOOST_CHECK_EQUAL(lastCpnF->referencePeriodEnd(), Date(25, Mar, 2021));
481
482 ext::shared_ptr<FloatingRateCoupon> firstCpnF2 =
483 ext::dynamic_pointer_cast<FloatingRateCoupon>(r: legf2.front());
484 ext::shared_ptr<FloatingRateCoupon> lastCpnF2 =
485 ext::dynamic_pointer_cast<FloatingRateCoupon>(r: legf2.back());
486 BOOST_REQUIRE(firstCpnF2 != nullptr);
487 BOOST_REQUIRE(lastCpnF2 != nullptr);
488 BOOST_CHECK_EQUAL(firstCpnF2->referencePeriodStart(), Date(25, Mar, 2017));
489 BOOST_CHECK_EQUAL(firstCpnF2->referencePeriodEnd(), Date(25, Sep, 2017));
490 BOOST_CHECK_EQUAL(lastCpnF2->referencePeriodStart(), Date(25, Sep, 2020));
491 BOOST_CHECK_EQUAL(lastCpnF2->referencePeriodEnd(), Date(25, Mar, 2021));
492
493 ext::shared_ptr<FloatingRateCoupon> firstCpnF3 =
494 ext::dynamic_pointer_cast<FloatingRateCoupon>(r: legf3.front());
495 ext::shared_ptr<FloatingRateCoupon> lastCpnF3 =
496 ext::dynamic_pointer_cast<FloatingRateCoupon>(r: legf3.back());
497 BOOST_REQUIRE(firstCpnF3 != nullptr);
498 BOOST_REQUIRE(lastCpnF3 != nullptr);
499 BOOST_CHECK_EQUAL(firstCpnF3->referencePeriodStart(), Date(15, Sep, 2017));
500 BOOST_CHECK_EQUAL(firstCpnF3->referencePeriodEnd(), Date(25, Sep, 2017));
501 BOOST_CHECK_EQUAL(lastCpnF3->referencePeriodStart(), Date(25, Sep, 2020));
502 BOOST_CHECK_EQUAL(lastCpnF3->referencePeriodEnd(), Date(30, Sep, 2020));
503}
504
505void CashFlowsTest::testFixedIborCouponWithoutForecastCurve() {
506 BOOST_TEST_MESSAGE("Testing past ibor coupon without forecast curve...");
507
508 Date today = Settings::instance().evaluationDate();
509
510 auto index = ext::make_shared<USDLibor>(args: 6*Months);
511 auto calendar = index->fixingCalendar();
512
513 Date fixingDate = calendar.advance(today, n: -2, unit: Months);
514 Rate pastFixing = 0.01;
515 index->addFixing(fixingDate, fixing: pastFixing);
516
517 Date startDate = index->valueDate(fixingDate);
518 Date endDate = index->maturityDate(valueDate: fixingDate);
519
520 IborCoupon coupon(endDate, 100.0, startDate, endDate, index->fixingDays(), index);
521 coupon.setPricer(ext::make_shared<BlackIborCouponPricer>());
522
523 BOOST_CHECK_NO_THROW(coupon.amount());
524
525 // the main check is the one above, but let's check for consistency too:
526 Real amount = coupon.amount();
527 Real expected = pastFixing * coupon.nominal() * coupon.accrualPeriod();
528 if (std::fabs(x: amount - expected) > 1e-8) {
529 BOOST_ERROR("amount mismatch:"
530 << "\n calculated: " << amount
531 << "\n expected: " << expected);
532 }
533}
534
535test_suite* CashFlowsTest::suite() {
536 auto* suite = BOOST_TEST_SUITE("Cash flows tests");
537 suite->add(QUANTLIB_TEST_CASE(&CashFlowsTest::testSettings));
538 suite->add(QUANTLIB_TEST_CASE(&CashFlowsTest::testAccessViolation));
539 suite->add(QUANTLIB_TEST_CASE(&CashFlowsTest::testDefaultSettlementDate));
540 suite->add(QUANTLIB_TEST_CASE(&CashFlowsTest::testExCouponDates));
541
542 if (IborCoupon::Settings::instance().usingAtParCoupons())
543 suite->add(QUANTLIB_TEST_CASE(&CashFlowsTest::testNullFixingDays));
544
545 suite->add(QUANTLIB_TEST_CASE(&CashFlowsTest::testIrregularFirstCouponReferenceDatesAtEndOfMonth));
546 suite->add(QUANTLIB_TEST_CASE(&CashFlowsTest::testIrregularLastCouponReferenceDatesAtEndOfMonth));
547 suite->add(QUANTLIB_TEST_CASE(&CashFlowsTest::testPartialScheduleLegConstruction));
548 suite->add(QUANTLIB_TEST_CASE(&CashFlowsTest::testFixedIborCouponWithoutForecastCurve));
549
550 return suite;
551}
552

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