Coverage Report

Created: 2026-06-08 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/cashflows/cashflows.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2005, 2006 StatPro Italia srl
5
 Copyright (C) 2005 Charles Whitmore
6
 Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Ferdinando Ametrano
7
 Copyright (C) 2008 Toyin Akin
8
9
 This file is part of QuantLib, a free-software/open-source library
10
 for financial quantitative analysts and developers - http://quantlib.org/
11
12
 QuantLib is free software: you can redistribute it and/or modify it
13
 under the terms of the QuantLib license.  You should have received a
14
 copy of the license along with this program; if not, please email
15
 <quantlib-dev@lists.sf.net>. The license is also available online at
16
 <https://www.quantlib.org/license.shtml>.
17
18
 This program is distributed in the hope that it will be useful, but WITHOUT
19
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20
 FOR A PARTICULAR PURPOSE.  See the license for more details.
21
*/
22
23
#include <ql/cashflows/cashflows.hpp>
24
#include <ql/cashflows/coupon.hpp>
25
#include <ql/cashflows/couponpricer.hpp>
26
#include <ql/math/solvers1d/brent.hpp>
27
#include <ql/math/solvers1d/newtonsafe.hpp>
28
#include <ql/patterns/visitor.hpp>
29
#include <ql/quotes/simplequote.hpp>
30
#include <ql/termstructures/yield/flatforward.hpp>
31
#include <ql/termstructures/yield/zerospreadedtermstructure.hpp>
32
#include <utility>
33
34
namespace QuantLib {
35
36
    // Date inspectors
37
38
971
    Date CashFlows::startDate(const Leg& leg) {
39
971
        QL_REQUIRE(!leg.empty(), "empty leg");
40
41
971
        Date d = Date::maxDate();
42
6.33k
        for (const auto& i : leg) {
43
6.33k
            ext::shared_ptr<Coupon> c = ext::dynamic_pointer_cast<Coupon>(i);
44
6.33k
            if (c != nullptr)
45
3.05k
                d = std::min(d, c->accrualStartDate());
46
3.28k
            else
47
3.28k
                d = std::min(d, i->date());
48
6.33k
        }
49
971
        return d;
50
971
    }
51
52
971
    Date CashFlows::maturityDate(const Leg& leg) {
53
971
        QL_REQUIRE(!leg.empty(), "empty leg");
54
55
971
        Date d = Date::minDate();
56
6.33k
        for (const auto& i : leg) {
57
6.33k
            ext::shared_ptr<Coupon> c = ext::dynamic_pointer_cast<Coupon>(i);
58
6.33k
            if (c != nullptr)
59
3.05k
                d = std::max(d, c->accrualEndDate());
60
3.28k
            else
61
3.28k
                d = std::max(d, i->date());
62
6.33k
        }
63
971
        return d;
64
971
    }
65
66
    bool CashFlows::isExpired(const Leg& leg,
67
                              const ext::optional<bool>& includeSettlementDateFlows,
68
                              Date settlementDate)
69
971
    {
70
971
        if (leg.empty())
71
0
            return true;
72
73
971
        if (settlementDate == Date())
74
971
            settlementDate = Settings::instance().evaluationDate();
75
76
971
        for (Size i=leg.size(); i>0; --i)
77
971
            if (!leg[i-1]->hasOccurred(settlementDate,
78
971
                                       includeSettlementDateFlows))
79
971
                return false;
80
0
        return true;
81
971
    }
82
83
    Leg::const_reverse_iterator
84
    CashFlows::previousCashFlow(const Leg& leg,
85
                                const ext::optional<bool>& includeSettlementDateFlows,
86
0
                                Date settlementDate) {
87
0
        if (leg.empty())
88
0
            return leg.rend();
89
90
0
        if (settlementDate == Date())
91
0
            settlementDate = Settings::instance().evaluationDate();
92
93
0
        Leg::const_reverse_iterator i;
94
0
        for (i = leg.rbegin(); i<leg.rend(); ++i) {
95
0
            if ( (*i)->hasOccurred(settlementDate, includeSettlementDateFlows) )
96
0
                return i;
97
0
        }
98
0
        return leg.rend();
99
0
    }
100
101
    Leg::const_iterator
102
    CashFlows::nextCashFlow(const Leg& leg,
103
                            const ext::optional<bool>& includeSettlementDateFlows,
104
0
                            Date settlementDate) {
105
0
        if (leg.empty())
106
0
            return leg.end();
107
108
0
        if (settlementDate == Date())
109
0
            settlementDate = Settings::instance().evaluationDate();
110
111
0
        Leg::const_iterator i;
112
0
        for (i = leg.begin(); i<leg.end(); ++i) {
113
0
            if ( ! (*i)->hasOccurred(settlementDate, includeSettlementDateFlows) )
114
0
                return i;
115
0
        }
116
0
        return leg.end();
117
0
    }
118
119
    Date CashFlows::previousCashFlowDate(const Leg& leg,
120
                                         const ext::optional<bool>& includeSettlementDateFlows,
121
0
                                         Date settlementDate) {
122
0
        Leg::const_reverse_iterator cf;
123
0
        cf = previousCashFlow(leg, includeSettlementDateFlows, settlementDate);
124
125
0
        if (cf==leg.rend())
126
0
            return {};
127
128
0
        return (*cf)->date();
129
0
    }
130
131
    Date CashFlows::nextCashFlowDate(const Leg& leg,
132
                                     const ext::optional<bool>& includeSettlementDateFlows,
133
0
                                     Date settlementDate) {
134
0
        Leg::const_iterator cf;
135
0
        cf = nextCashFlow(leg, includeSettlementDateFlows, settlementDate);
136
137
0
        if (cf==leg.end())
138
0
            return {};
139
140
0
        return (*cf)->date();
141
0
    }
142
143
    Real CashFlows::previousCashFlowAmount(const Leg& leg,
144
                                           const ext::optional<bool>& includeSettlementDateFlows,
145
0
                                           Date settlementDate) {
146
0
        Leg::const_reverse_iterator cf;
147
0
        cf = previousCashFlow(leg, includeSettlementDateFlows, settlementDate);
148
149
0
        if (cf==leg.rend())
150
0
            return Real();
151
152
0
        Date paymentDate = (*cf)->date();
153
0
        Real result = 0.0;
154
0
        for (; cf<leg.rend() && (*cf)->date()==paymentDate; ++cf)
155
0
            result += (*cf)->amount();
156
0
        return result;
157
0
    }
158
159
    Real CashFlows::nextCashFlowAmount(const Leg& leg,
160
                                       const ext::optional<bool>& includeSettlementDateFlows,
161
0
                                       Date settlementDate) {
162
0
        Leg::const_iterator cf;
163
0
        cf = nextCashFlow(leg, includeSettlementDateFlows, settlementDate);
164
165
0
        if (cf==leg.end())
166
0
            return Real();
167
168
0
        Date paymentDate = (*cf)->date();
169
0
        Real result = 0.0;
170
0
        for (; cf<leg.end() && (*cf)->date()==paymentDate; ++cf)
171
0
            result += (*cf)->amount();
172
0
        return result;
173
0
    }
174
175
    // Coupon utility functions
176
    namespace {
177
178
        template<typename Iter>
179
        Rate aggregateRate(Iter first,
180
0
                           const Iter& last) {
181
0
            if (first==last) return 0.0;
182
183
0
            Date paymentDate = (*first)->date();
184
0
            bool firstCouponFound = false;
185
0
            Real nominal = 0.0;
186
0
            Time accrualPeriod = 0.0;
187
0
            DayCounter dc;
188
0
            Rate result = 0.0;
189
0
            for (; first<last && (*first)->date()==paymentDate; ++first) {
190
0
                ext::shared_ptr<Coupon> cp = ext::dynamic_pointer_cast<Coupon>(*first);
191
0
                if (cp) {
192
0
                    if (firstCouponFound) {
193
0
                        QL_REQUIRE(nominal       == cp->nominal() &&
194
0
                                   accrualPeriod == cp->accrualPeriod() &&
195
0
                                   dc            == cp->dayCounter(),
196
0
                                   "cannot aggregate two different coupons on "
197
0
                                   << paymentDate);
198
0
                    } else {
199
0
                        firstCouponFound = true;
200
0
                        nominal = cp->nominal();
201
0
                        accrualPeriod = cp->accrualPeriod();
202
0
                        dc = cp->dayCounter();
203
0
                    }
204
0
                    result += cp->rate();
205
0
                }
206
0
            }
207
0
            QL_ENSURE(firstCouponFound,
208
0
                      "no coupon paid at cashflow date " << paymentDate);
209
0
            return result;
210
0
        }
Unexecuted instantiation: cashflows.cpp:double QuantLib::(anonymous namespace)::aggregateRate<std::__1::reverse_iterator<std::__1::__wrap_iter<boost::shared_ptr<QuantLib::CashFlow> const*> > >(std::__1::reverse_iterator<std::__1::__wrap_iter<boost::shared_ptr<QuantLib::CashFlow> const*> >, std::__1::reverse_iterator<std::__1::__wrap_iter<boost::shared_ptr<QuantLib::CashFlow> const*> > const&)
Unexecuted instantiation: cashflows.cpp:double QuantLib::(anonymous namespace)::aggregateRate<std::__1::__wrap_iter<boost::shared_ptr<QuantLib::CashFlow> const*> >(std::__1::__wrap_iter<boost::shared_ptr<QuantLib::CashFlow> const*>, std::__1::__wrap_iter<boost::shared_ptr<QuantLib::CashFlow> const*> const&)
211
212
    } // anonymous namespace ends here
213
214
    Rate CashFlows::previousCouponRate(const Leg& leg,
215
                                       const ext::optional<bool>& includeSettlementDateFlows,
216
0
                                       Date settlementDate) {
217
0
        Leg::const_reverse_iterator cf;
218
0
        cf = previousCashFlow(leg, includeSettlementDateFlows, settlementDate);
219
220
0
        return aggregateRate(cf, leg.rend());
221
0
    }
222
223
    Rate CashFlows::nextCouponRate(const Leg& leg,
224
                                   const ext::optional<bool>& includeSettlementDateFlows,
225
0
                                   Date settlementDate) {
226
0
        Leg::const_iterator cf;
227
0
        cf = nextCashFlow(leg, includeSettlementDateFlows, settlementDate);
228
0
        return aggregateRate(cf, leg.end());
229
0
    }
230
231
    Real CashFlows::nominal(const Leg& leg,
232
                            const ext::optional<bool>& includeSettlementDateFlows,
233
0
                            Date settlementDate) {
234
0
        auto cf = nextCashFlow(leg, includeSettlementDateFlows, settlementDate);
235
0
        if (cf==leg.end()) return 0.0;
236
237
0
        Date paymentDate = (*cf)->date();
238
0
        for (; cf<leg.end() && (*cf)->date()==paymentDate; ++cf) {
239
0
            ext::shared_ptr<Coupon> cp = ext::dynamic_pointer_cast<Coupon>(*cf);
240
0
            if (cp != nullptr)
241
0
                return cp->nominal();
242
0
        }
243
0
        return 0.0;
244
0
    }
245
246
    Date CashFlows::accrualStartDate(const Leg& leg,
247
                                     const ext::optional<bool>& includeSettlementDateFlows,
248
0
                                     Date settlementDate) {
249
0
        auto cf = nextCashFlow(leg, includeSettlementDateFlows, settlementDate);
250
0
        if (cf==leg.end())
251
0
            return {};
252
253
0
        Date paymentDate = (*cf)->date();
254
0
        for (; cf<leg.end() && (*cf)->date()==paymentDate; ++cf) {
255
0
            ext::shared_ptr<Coupon> cp = ext::dynamic_pointer_cast<Coupon>(*cf);
256
0
            if (cp != nullptr)
257
0
                return cp->accrualStartDate();
258
0
        }
259
0
        return {};
260
0
    }
261
262
    Date CashFlows::accrualEndDate(const Leg& leg,
263
                                   const ext::optional<bool>& includeSettlementDateFlows,
264
0
                                   Date settlementDate) {
265
0
        auto cf = nextCashFlow(leg, includeSettlementDateFlows, settlementDate);
266
0
        if (cf==leg.end())
267
0
            return {};
268
269
0
        Date paymentDate = (*cf)->date();
270
0
        for (; cf<leg.end() && (*cf)->date()==paymentDate; ++cf) {
271
0
            ext::shared_ptr<Coupon> cp = ext::dynamic_pointer_cast<Coupon>(*cf);
272
0
            if (cp != nullptr)
273
0
                return cp->accrualEndDate();
274
0
        }
275
0
        return {};
276
0
    }
277
278
    Date CashFlows::referencePeriodStart(const Leg& leg,
279
                                         const ext::optional<bool>& includeSettlementDateFlows,
280
0
                                         Date settlementDate) {
281
0
        auto cf = nextCashFlow(leg, includeSettlementDateFlows, settlementDate);
282
0
        if (cf==leg.end())
283
0
            return {};
284
285
0
        Date paymentDate = (*cf)->date();
286
0
        for (; cf<leg.end() && (*cf)->date()==paymentDate; ++cf) {
287
0
            ext::shared_ptr<Coupon> cp = ext::dynamic_pointer_cast<Coupon>(*cf);
288
0
            if (cp != nullptr)
289
0
                return cp->referencePeriodStart();
290
0
        }
291
0
        return {};
292
0
    }
293
294
    Date CashFlows::referencePeriodEnd(const Leg& leg,
295
                                       const ext::optional<bool>& includeSettlementDateFlows,
296
0
                                       Date settlementDate) {
297
0
        auto cf = nextCashFlow(leg, includeSettlementDateFlows, settlementDate);
298
0
        if (cf==leg.end())
299
0
            return {};
300
301
0
        Date paymentDate = (*cf)->date();
302
0
        for (; cf<leg.end() && (*cf)->date()==paymentDate; ++cf) {
303
0
            ext::shared_ptr<Coupon> cp = ext::dynamic_pointer_cast<Coupon>(*cf);
304
0
            if (cp != nullptr)
305
0
                return cp->referencePeriodEnd();
306
0
        }
307
0
        return {};
308
0
    }
309
310
    Time CashFlows::accrualPeriod(const Leg& leg,
311
                                  const ext::optional<bool>& includeSettlementDateFlows,
312
0
                                  Date settlementDate) {
313
0
        auto cf = nextCashFlow(leg, includeSettlementDateFlows, settlementDate);
314
0
        if (cf==leg.end()) return 0;
315
316
0
        Date paymentDate = (*cf)->date();
317
0
        for (; cf<leg.end() && (*cf)->date()==paymentDate; ++cf) {
318
0
            ext::shared_ptr<Coupon> cp = ext::dynamic_pointer_cast<Coupon>(*cf);
319
0
            if (cp != nullptr)
320
0
                return cp->accrualPeriod();
321
0
        }
322
0
        return 0;
323
0
    }
324
325
    Date::serial_type CashFlows::accrualDays(const Leg& leg,
326
                                             const ext::optional<bool>& includeSettlementDateFlows,
327
0
                                             Date settlementDate) {
328
0
        auto cf = nextCashFlow(leg, includeSettlementDateFlows, settlementDate);
329
0
        if (cf==leg.end()) return 0;
330
331
0
        Date paymentDate = (*cf)->date();
332
0
        for (; cf<leg.end() && (*cf)->date()==paymentDate; ++cf) {
333
0
            ext::shared_ptr<Coupon> cp = ext::dynamic_pointer_cast<Coupon>(*cf);
334
0
            if (cp != nullptr)
335
0
                return cp->accrualDays();
336
0
        }
337
0
        return 0;
338
0
    }
339
340
    Time CashFlows::accruedPeriod(const Leg& leg,
341
                                  const ext::optional<bool>& includeSettlementDateFlows,
342
0
                                  Date settlementDate) {
343
0
        if (settlementDate == Date())
344
0
            settlementDate = Settings::instance().evaluationDate();
345
346
0
        auto cf = nextCashFlow(leg, includeSettlementDateFlows, settlementDate);
347
0
        if (cf==leg.end()) return 0;
348
349
0
        Date paymentDate = (*cf)->date();
350
0
        for (; cf<leg.end() && (*cf)->date()==paymentDate; ++cf) {
351
0
            ext::shared_ptr<Coupon> cp = ext::dynamic_pointer_cast<Coupon>(*cf);
352
0
            if (cp != nullptr)
353
0
                return cp->accruedPeriod(settlementDate);
354
0
        }
355
0
        return 0;
356
0
    }
357
358
    Date::serial_type CashFlows::accruedDays(const Leg& leg,
359
                                             const ext::optional<bool>& includeSettlementDateFlows,
360
0
                                             Date settlementDate) {
361
0
        if (settlementDate == Date())
362
0
            settlementDate = Settings::instance().evaluationDate();
363
364
0
        auto cf = nextCashFlow(leg, includeSettlementDateFlows, settlementDate);
365
0
        if (cf==leg.end()) return 0;
366
367
0
        Date paymentDate = (*cf)->date();
368
0
        for (; cf<leg.end() && (*cf)->date()==paymentDate; ++cf) {
369
0
            ext::shared_ptr<Coupon> cp = ext::dynamic_pointer_cast<Coupon>(*cf);
370
0
            if (cp != nullptr)
371
0
                return cp->accruedDays(settlementDate);
372
0
        }
373
0
        return 0;
374
0
    }
375
376
    Real CashFlows::accruedAmount(const Leg& leg,
377
                                  const ext::optional<bool>& includeSettlementDateFlows,
378
0
                                  Date settlementDate) {
379
0
        if (settlementDate == Date())
380
0
            settlementDate = Settings::instance().evaluationDate();
381
382
0
        auto cf = nextCashFlow(leg, includeSettlementDateFlows, settlementDate);
383
0
        if (cf==leg.end()) return 0.0;
384
385
0
        Date paymentDate = (*cf)->date();
386
0
        Real result = 0.0;
387
0
        for (; cf<leg.end() && (*cf)->date()==paymentDate; ++cf) {
388
0
            ext::shared_ptr<Coupon> cp = ext::dynamic_pointer_cast<Coupon>(*cf);
389
0
            if (cp != nullptr)
390
0
                result += cp->accruedAmount(settlementDate);
391
0
        }
392
0
        return result;
393
0
    }
394
395
    // YieldTermStructure utility functions
396
    namespace {
397
398
        class BPSCalculator : public AcyclicVisitor,
399
                              public Visitor<CashFlow>,
400
                              public Visitor<Coupon> {
401
          public:
402
            explicit BPSCalculator(const YieldTermStructure& discountCurve)
403
697
            : discountCurve_(discountCurve) {}
404
1.72k
            void visit(Coupon& c) override {
405
1.72k
                Real bps = c.nominal() *
406
1.72k
                           c.accrualPeriod() *
407
1.72k
                           discountCurve_.discount(c.date());
408
1.72k
                bps_ += bps;
409
1.72k
            }
410
1.13k
            void visit(CashFlow& cf) override {
411
1.13k
                nonSensNPV_ += cf.amount() * 
412
1.13k
                               discountCurve_.discount(cf.date());
413
1.13k
            }
414
697
            Real bps() const { return bps_; }
415
0
            Real nonSensNPV() const { return nonSensNPV_; }
416
          private:
417
            const YieldTermStructure& discountCurve_;
418
            Real bps_ = 0.0, nonSensNPV_ = 0.0;
419
        };
420
421
        const Spread basisPoint_ = 1.0e-4;
422
    } // anonymous namespace ends here
423
424
    Real CashFlows::npv(const Leg& leg,
425
                        const YieldTermStructure& discountCurve,
426
                        const ext::optional<bool>& includeSettlementDateFlows,
427
                        Date settlementDate,
428
0
                        Date npvDate) {
429
430
0
        if (leg.empty())
431
0
            return 0.0;
432
433
0
        if (settlementDate == Date())
434
0
            settlementDate = Settings::instance().evaluationDate();
435
436
0
        if (npvDate == Date())
437
0
            npvDate = settlementDate;
438
439
0
        Real totalNPV = 0.0;
440
0
        for (const auto& i : leg) {
441
0
            if (!i->hasOccurred(settlementDate, includeSettlementDateFlows) &&
442
0
                !i->tradingExCoupon(settlementDate))
443
0
                totalNPV += i->amount() * discountCurve.discount(i->date());
444
0
        }
445
446
0
        return totalNPV/discountCurve.discount(npvDate);
447
0
    }
448
449
    Real CashFlows::bps(const Leg& leg,
450
                        const YieldTermStructure& discountCurve,
451
                        const ext::optional<bool>& includeSettlementDateFlows,
452
                        Date settlementDate,
453
697
                        Date npvDate) {
454
697
        if (leg.empty())
455
0
            return 0.0;
456
457
697
        if (settlementDate == Date())
458
0
            settlementDate = Settings::instance().evaluationDate();
459
460
697
        if (npvDate == Date())
461
0
            npvDate = settlementDate;
462
463
697
        BPSCalculator calc(discountCurve);
464
2.85k
        for (const auto& i : leg) {
465
2.85k
            if (!i->hasOccurred(settlementDate, includeSettlementDateFlows) &&
466
2.85k
                !i->tradingExCoupon(settlementDate))
467
2.85k
                i->accept(calc);
468
2.85k
        }
469
697
        return basisPoint_*calc.bps()/discountCurve.discount(npvDate);
470
697
    }
471
472
    std::pair<Real, Real> CashFlows::npvbps(const Leg& leg,
473
                                            const YieldTermStructure& discountCurve,
474
                                            const ext::optional<bool>& includeSettlementDateFlows,
475
                                            Date settlementDate,
476
0
                                            Date npvDate) {
477
0
        Real npv = 0.0;
478
0
        Real bps = 0.0;
479
480
0
        if (leg.empty()) {
481
0
            return { npv, bps };
482
0
        }
483
484
0
        if (settlementDate == Date())
485
0
            settlementDate = Settings::instance().evaluationDate();
486
487
0
        if (npvDate == Date())
488
0
            npvDate = settlementDate;
489
490
0
        for (const auto& i : leg) {
491
0
            CashFlow& cf = *i;
492
0
            if (!cf.hasOccurred(settlementDate,
493
0
                                includeSettlementDateFlows) &&
494
0
                !cf.tradingExCoupon(settlementDate)) {
495
0
                ext::shared_ptr<Coupon> cp = ext::dynamic_pointer_cast<Coupon>(i);
496
0
                Real df = discountCurve.discount(cf.date());
497
0
                npv += cf.amount() * df;
498
0
                if (cp != nullptr)
499
0
                    bps += cp->nominal() * cp->accrualPeriod() * df;
500
0
            }
501
0
        }
502
0
        DiscountFactor d = discountCurve.discount(npvDate);
503
0
        npv /= d;
504
0
        bps = basisPoint_ * bps / d;
505
506
0
        return { npv, bps };
507
0
    }
508
509
    Rate CashFlows::atmRate(const Leg& leg,
510
                            const YieldTermStructure& discountCurve,
511
                            const ext::optional<bool>& includeSettlementDateFlows,
512
                            Date settlementDate,
513
                            Date npvDate,
514
0
                            Real targetNpv) {
515
0
        if (leg.empty())
516
0
            return 0.0;
517
518
0
        if (settlementDate == Date())
519
0
            settlementDate = Settings::instance().evaluationDate();
520
521
0
        if (npvDate == Date())
522
0
            npvDate = settlementDate;
523
524
0
        Real npv = 0.0;
525
0
        BPSCalculator calc(discountCurve);
526
0
        for (const auto& i : leg) {
527
0
            CashFlow& cf = *i;
528
0
            if (!cf.hasOccurred(settlementDate,
529
0
                                includeSettlementDateFlows) &&
530
0
                !cf.tradingExCoupon(settlementDate)) {
531
0
                npv += cf.amount() *
532
0
                       discountCurve.discount(cf.date());
533
0
                cf.accept(calc);
534
0
            }
535
0
        }
536
537
0
        if (targetNpv==Null<Real>())
538
0
            targetNpv = npv - calc.nonSensNPV();
539
0
        else {
540
0
            targetNpv *= discountCurve.discount(npvDate);
541
0
            targetNpv -= calc.nonSensNPV();
542
0
        }
543
544
0
        if (targetNpv==0.0)
545
0
            return 0.0;
546
547
0
        Real bps = calc.bps();
548
0
        QL_REQUIRE(bps!=0.0, "null bps: impossible atm rate");
549
550
0
        return targetNpv/bps;
551
0
    }
552
553
    // IRR utility functions
554
    namespace {
555
556
        template <class T>
557
2.09k
        Integer sign(T x) {
558
2.09k
            static T zero = T();
559
2.09k
            if (x == zero)
560
111
                return 0;
561
1.98k
            else if (x > zero)
562
1.51k
                return 1;
563
471
            else
564
471
                return -1;
565
2.09k
        }
566
567
        // helper fucntion used to calculate Time-To-Discount for each stage when calculating discount factor stepwisely
568
        Time getStepwiseDiscountTime(const ext::shared_ptr<QuantLib::CashFlow>& cashFlow,
569
                                     const DayCounter& dc,
570
                                     Date npvDate,
571
154k
                                     Date lastDate) {
572
154k
            Date cashFlowDate = cashFlow->date();
573
154k
            Date refStartDate, refEndDate;
574
154k
            ext::shared_ptr<Coupon> coupon =
575
154k
                    ext::dynamic_pointer_cast<Coupon>(cashFlow);
576
154k
            if (coupon != nullptr) {
577
102k
                refStartDate = coupon->referencePeriodStart();
578
102k
                refEndDate = coupon->referencePeriodEnd();
579
102k
            } else {
580
51.5k
                if (lastDate == npvDate) {
581
                    // we don't have a previous coupon date,
582
                    // so we fake it
583
36.5k
                    refStartDate = cashFlowDate - 1*Years;
584
36.5k
                } else  {
585
14.9k
                    refStartDate = lastDate;
586
14.9k
                }
587
51.5k
                refEndDate = cashFlowDate;
588
51.5k
            }
589
590
154k
            if ((coupon != nullptr) && lastDate != coupon->accrualStartDate()) {
591
10.3k
                Time couponPeriod = dc.yearFraction(coupon->accrualStartDate(),
592
10.3k
                                                cashFlowDate, refStartDate, refEndDate);
593
10.3k
                Time accruedPeriod = dc.yearFraction(coupon->accrualStartDate(),
594
10.3k
                                                lastDate, refStartDate, refEndDate);
595
10.3k
                return couponPeriod - accruedPeriod;
596
144k
            } else {
597
144k
                return dc.yearFraction(lastDate, cashFlowDate,
598
144k
                                       refStartDate, refEndDate);
599
144k
            }
600
154k
        }
601
602
        Real simpleDuration(const Leg& leg,
603
                            const InterestRate& y,
604
                            const ext::optional<bool>& includeSettlementDateFlows,
605
                            Date settlementDate,
606
503
                            Date npvDate) {
607
503
            if (leg.empty())
608
0
                return 0.0;
609
610
503
            if (settlementDate == Date())
611
0
                settlementDate = Settings::instance().evaluationDate();
612
613
503
            if (npvDate == Date())
614
0
                npvDate = settlementDate;
615
616
503
            Real P = 0.0;
617
503
            Real dPdy = 0.0;
618
503
            Time t = 0.0;
619
503
            Date lastDate = npvDate;
620
503
            const DayCounter& dc = y.dayCounter();
621
1.86k
            for (const auto& i : leg) {
622
1.86k
                if (i->hasOccurred(settlementDate, includeSettlementDateFlows))
623
0
                    continue;
624
625
1.86k
                Real c = i->amount();
626
1.86k
                if (i->tradingExCoupon(settlementDate)) {
627
0
                    c = 0.0;
628
0
                }
629
630
1.86k
                t += getStepwiseDiscountTime(i, dc, npvDate, lastDate);
631
1.86k
                DiscountFactor B = y.discountFactor(t);
632
1.86k
                P += c * B;
633
1.86k
                dPdy += t * c * B;
634
635
1.86k
                lastDate = i->date();
636
1.86k
            }
637
503
            if (P == 0.0) // no cashflows
638
74
                return 0.0;
639
429
            return dPdy/P;
640
503
        }
641
642
        Real modifiedDuration(const Leg& leg,
643
                              const InterestRate& y,
644
                              const ext::optional<bool>& includeSettlementDateFlows,
645
                              Date settlementDate,
646
7.59k
                              Date npvDate) {
647
7.59k
            if (leg.empty())
648
0
                return 0.0;
649
650
7.59k
            if (settlementDate == Date())
651
0
                settlementDate = Settings::instance().evaluationDate();
652
653
7.59k
            if (npvDate == Date())
654
0
                npvDate = settlementDate;
655
656
7.59k
            Real P = 0.0;
657
7.59k
            Time t = 0.0;
658
7.59k
            Real dPdy = 0.0;
659
7.59k
            Rate r = y.rate();
660
7.59k
            Natural N = y.frequency();
661
7.59k
            Date lastDate = npvDate;
662
7.59k
            const DayCounter& dc = y.dayCounter();
663
37.7k
            for (const auto& i : leg) {
664
37.7k
                if (i->hasOccurred(settlementDate, includeSettlementDateFlows))
665
0
                    continue;
666
667
37.7k
                Real c = i->amount();
668
37.7k
                if (i->tradingExCoupon(settlementDate)) {
669
0
                    c = 0.0;
670
0
                }
671
672
37.7k
                t += getStepwiseDiscountTime(i, dc, npvDate, lastDate);
673
37.7k
                DiscountFactor B = y.discountFactor(t);
674
37.7k
                P += c * B;
675
37.7k
                switch (y.compounding()) {
676
18.1k
                  case Simple:
677
18.1k
                    dPdy -= c * B*B * t;
678
18.1k
                    break;
679
9.77k
                  case Compounded:
680
9.77k
                    dPdy -= c * t * B/(1+r/N);
681
9.77k
                    break;
682
1.48k
                  case Continuous:
683
1.48k
                    dPdy -= c * B * t;
684
1.48k
                    break;
685
8.34k
                  case SimpleThenCompounded:
686
8.34k
                    if (t<=1.0/N)
687
1.60k
                        dPdy -= c * B*B * t;
688
6.74k
                    else
689
6.74k
                        dPdy -= c * t * B/(1+r/N);
690
8.34k
                    break;
691
0
                  case CompoundedThenSimple:
692
0
                    if (t>1.0/N)
693
0
                        dPdy -= c * B*B * t;
694
0
                    else
695
0
                        dPdy -= c * t * B/(1+r/N);
696
0
                    break;
697
0
                  default:
698
0
                    QL_FAIL("unknown compounding convention (" <<
699
37.7k
                            Integer(y.compounding()) << ")");
700
37.7k
                }
701
37.7k
                lastDate = i->date();
702
37.7k
            }
703
704
7.59k
            if (P == 0.0) // no cashflows
705
172
                return 0.0;
706
7.42k
            return -dPdy/P; // reverse derivative sign
707
7.59k
        }
708
709
        Real macaulayDuration(const Leg& leg,
710
                              const InterestRate& y,
711
                              const ext::optional<bool>& includeSettlementDateFlows,
712
                              Date settlementDate,
713
63
                              Date npvDate) {
714
715
63
            QL_REQUIRE(y.compounding() == Compounded,
716
32
                       "compounded rate required");
717
718
32
            return (1.0+y.rate()/Integer(y.frequency())) *
719
32
                modifiedDuration(leg, y,
720
32
                                 includeSettlementDateFlows,
721
32
                                 settlementDate, npvDate);
722
63
        }
723
724
        struct CashFlowLater {
725
            bool operator()(const ext::shared_ptr<CashFlow> &c,
726
0
                            const ext::shared_ptr<CashFlow> &d) {
727
0
                return c->date() > d->date();
728
0
            }
729
        };
730
731
    } // anonymous namespace ends here
732
733
    CashFlows::IrrFinder::IrrFinder(const Leg& leg,
734
                                    Real npv,
735
                                    DayCounter dayCounter,
736
                                    Compounding comp,
737
                                    Frequency freq,
738
                                    const ext::optional<bool>& includeSettlementDateFlows,
739
                                    Date settlementDate,
740
                                    Date npvDate)
741
471
    : leg_(leg), npv_(npv), dayCounter_(std::move(dayCounter)), compounding_(comp),
742
471
      frequency_(freq), includeSettlementDateFlows_(includeSettlementDateFlows),
743
471
      settlementDate_(settlementDate), npvDate_(npvDate) {
744
745
471
        if (settlementDate_ == Date())
746
471
            settlementDate_ = Settings::instance().evaluationDate();
747
748
471
        if (npvDate_ == Date())
749
471
            npvDate_ = settlementDate_;
750
751
471
        checkSign();
752
471
    }
753
754
18.7k
    Real CashFlows::IrrFinder::operator()(Rate y) const {
755
18.7k
        InterestRate yield(y, dayCounter_, compounding_, frequency_);
756
18.7k
        Real NPV = CashFlows::npv(leg_, yield,
757
18.7k
                                  includeSettlementDateFlows_,
758
18.7k
                                  settlementDate_, npvDate_);
759
18.7k
        return NPV - npv_;
760
18.7k
    }
761
762
6.10k
    Real CashFlows::IrrFinder::derivative(Rate y) const {
763
6.10k
        InterestRate yield(y, dayCounter_, compounding_, frequency_);
764
6.10k
        Real p = CashFlows::npv(leg_, yield, includeSettlementDateFlows_, 
765
6.10k
                                settlementDate_, npvDate_);
766
6.10k
        return -modifiedDuration(leg_, yield,
767
6.10k
                                includeSettlementDateFlows_,
768
6.10k
                                settlementDate_, npvDate_) * p;
769
6.10k
    }
770
771
471
    void CashFlows::IrrFinder::checkSign() const {
772
        // depending on the sign of the market price, check that cash
773
        // flows of the opposite sign have been specified (otherwise
774
        // IRR is nonsensical.)
775
776
471
        Integer lastSign = sign(Real(-npv_)),
777
471
                signChanges = 0;
778
1.62k
        for (const auto& i : leg_) {
779
1.62k
            if (!i->hasOccurred(settlementDate_, includeSettlementDateFlows_) &&
780
1.62k
                !i->tradingExCoupon(settlementDate_)) {
781
1.62k
                Integer thisSign = sign(i->amount());
782
1.62k
                if (lastSign * thisSign < 0) // sign change
783
448
                    signChanges++;
784
785
1.62k
                if (thisSign != 0)
786
1.51k
                    lastSign = thisSign;
787
1.62k
            }
788
1.62k
        }
789
471
        QL_REQUIRE(signChanges > 0,
790
471
                   "the given cash flows cannot result in the given market "
791
471
                   "price due to their sign");
792
793
        /* The following is commented out due to the lack of a QL_WARN macro
794
        if (signChanges > 1) {    // Danger of non-unique solution
795
                                  // Check the aggregate cash flows (Norstrom)
796
            Real aggregateCashFlow = npv;
797
            signChanges = 0;
798
            for (Size i = 0; i < leg.size(); ++i) {
799
                Real nextAggregateCashFlow =
800
                    aggregateCashFlow + leg[i]->amount();
801
802
                if (aggregateCashFlow * nextAggregateCashFlow < 0.0)
803
                    signChanges++;
804
805
                aggregateCashFlow = nextAggregateCashFlow;
806
            }
807
            if (signChanges > 1)
808
                QL_WARN( "danger of non-unique solution");
809
        };
810
        */
811
471
    }
812
813
    Real CashFlows::npv(const Leg& leg,
814
                        const InterestRate& y,
815
                        const ext::optional<bool>& includeSettlementDateFlows,
816
                        Date settlementDate,
817
27.1k
                        Date npvDate) {
818
819
27.1k
        if (leg.empty())
820
0
            return 0.0;
821
822
27.1k
        if (settlementDate == Date())
823
956
            settlementDate = Settings::instance().evaluationDate();
824
825
27.1k
        if (npvDate == Date())
826
956
            npvDate = settlementDate;
827
828
#if defined(QL_EXTRA_SAFETY_CHECKS)
829
        QL_REQUIRE(std::adjacent_find(leg.begin(), leg.end(),
830
                                      CashFlowLater()) == leg.end(),
831
                   "cashflows must be sorted in ascending order w.r.t. their payment dates");
832
#endif
833
834
27.1k
        Real npv = 0.0;
835
27.1k
        DiscountFactor discount = 1.0;
836
27.1k
        Date lastDate = npvDate;
837
27.1k
        const DayCounter& dc = y.dayCounter();
838
109k
        for (const auto& i : leg) {
839
109k
            if (i->hasOccurred(settlementDate, includeSettlementDateFlows))
840
0
                continue;
841
842
109k
            Real amount = i->amount();
843
109k
            if (i->tradingExCoupon(settlementDate)) {
844
0
                amount = 0.0;
845
0
            }
846
847
109k
            DiscountFactor b = y.discountFactor(getStepwiseDiscountTime(i, dc, npvDate, lastDate));
848
109k
            discount *= b;
849
109k
            lastDate = i->date();
850
851
109k
            npv += amount * discount;
852
109k
        }
853
854
27.1k
        return npv;
855
27.1k
    }
856
857
    Real CashFlows::npv(const Leg& leg,
858
                        Rate yield,
859
                        const DayCounter& dc,
860
                        Compounding comp,
861
                        Frequency freq,
862
                        const ext::optional<bool>& includeSettlementDateFlows,
863
                        Date settlementDate,
864
971
                        Date npvDate) {
865
971
        return npv(leg, InterestRate(yield, dc, comp, freq),
866
971
                   includeSettlementDateFlows,
867
971
                   settlementDate, npvDate);
868
971
    }
869
870
    Real CashFlows::bps(const Leg& leg,
871
                        const InterestRate& yield,
872
                        const ext::optional<bool>& includeSettlementDateFlows,
873
                        Date settlementDate,
874
697
                        Date npvDate) {
875
876
697
        if (leg.empty())
877
0
            return 0.0;
878
879
697
        if (settlementDate == Date())
880
697
            settlementDate = Settings::instance().evaluationDate();
881
882
697
        if (npvDate == Date())
883
697
            npvDate = settlementDate;
884
885
697
        FlatForward flatRate(settlementDate, yield.rate(), yield.dayCounter(),
886
697
                             yield.compounding(), yield.frequency());
887
697
        return bps(leg, flatRate,
888
697
                   includeSettlementDateFlows,
889
697
                   settlementDate, npvDate);
890
697
    }
891
892
    Real CashFlows::bps(const Leg& leg,
893
                        Rate yield,
894
                        const DayCounter& dc,
895
                        Compounding comp,
896
                        Frequency freq,
897
                        const ext::optional<bool>& includeSettlementDateFlows,
898
                        Date settlementDate,
899
697
                        Date npvDate) {
900
697
        return bps(leg, InterestRate(yield, dc, comp, freq),
901
697
                   includeSettlementDateFlows,
902
697
                   settlementDate, npvDate);
903
697
    }
904
905
    Rate CashFlows::yield(const Leg& leg,
906
                          Real npv,
907
                          const DayCounter& dayCounter,
908
                          Compounding compounding,
909
                          Frequency frequency,
910
                          const ext::optional<bool>& includeSettlementDateFlows,
911
                          Date settlementDate,
912
                          Date npvDate,
913
                          Real accuracy,
914
                          Size maxIterations,
915
471
                          Rate guess) {
916
471
        NewtonSafe solver;
917
471
        solver.setMaxEvaluations(maxIterations);
918
471
        return CashFlows::yield<NewtonSafe>(solver, leg, npv, dayCounter,
919
471
                                            compounding, frequency,
920
471
                                            includeSettlementDateFlows,
921
471
                                            settlementDate, npvDate,
922
471
                                            accuracy, guess);
923
471
    }
924
925
926
    Time CashFlows::duration(const Leg& leg,
927
                             const InterestRate& rate,
928
                             Duration::Type type,
929
                             const ext::optional<bool>& includeSettlementDateFlows,
930
                             Date settlementDate,
931
2.02k
                             Date npvDate) {
932
933
2.02k
        if (leg.empty())
934
0
            return 0.0;
935
936
2.02k
        if (settlementDate == Date())
937
697
            settlementDate = Settings::instance().evaluationDate();
938
939
2.02k
        if (npvDate == Date())
940
697
            npvDate = settlementDate;
941
942
2.02k
        switch (type) {
943
503
          case Duration::Simple:
944
503
            return simpleDuration(leg, rate,
945
503
                                  includeSettlementDateFlows,
946
503
                                  settlementDate, npvDate);
947
1.46k
          case Duration::Modified:
948
1.46k
            return modifiedDuration(leg, rate,
949
1.46k
                                    includeSettlementDateFlows,
950
1.46k
                                    settlementDate, npvDate);
951
63
          case Duration::Macaulay:
952
63
            return macaulayDuration(leg, rate,
953
63
                                    includeSettlementDateFlows,
954
63
                                    settlementDate, npvDate);
955
0
          default:
956
0
            QL_FAIL("unknown duration type");
957
2.02k
        }
958
2.02k
    }
959
960
    Time CashFlows::duration(const Leg& leg,
961
                             Rate yield,
962
                             const DayCounter& dc,
963
                             Compounding comp,
964
                             Frequency freq,
965
                             Duration::Type type,
966
                             const ext::optional<bool>& includeSettlementDateFlows,
967
                             Date settlementDate,
968
697
                             Date npvDate) {
969
697
        return duration(leg, InterestRate(yield, dc, comp, freq),
970
697
                        type,
971
697
                        includeSettlementDateFlows,
972
697
                        settlementDate, npvDate);
973
697
    }
974
975
    Real CashFlows::convexity(const Leg& leg,
976
                              const InterestRate& y,
977
                              const ext::optional<bool>& includeSettlementDateFlows,
978
                              Date settlementDate,
979
1.33k
                              Date npvDate) {
980
1.33k
        if (leg.empty())
981
0
            return 0.0;
982
983
1.33k
        if (settlementDate == Date())
984
666
            settlementDate = Settings::instance().evaluationDate();
985
986
1.33k
        if (npvDate == Date())
987
666
            npvDate = settlementDate;
988
989
1.33k
        const DayCounter& dc = y.dayCounter();
990
991
1.33k
        Real P = 0.0;
992
1.33k
        Time t = 0.0;
993
1.33k
        Real d2Pdy2 = 0.0;
994
1.33k
        Rate r = y.rate();
995
1.33k
        Natural N = y.frequency();
996
1.33k
        Date lastDate = npvDate;
997
5.42k
        for (const auto& i : leg) {
998
5.42k
            if (i->hasOccurred(settlementDate, includeSettlementDateFlows))
999
0
                continue;
1000
1001
5.42k
            Real c = i->amount();
1002
5.42k
            if (i->tradingExCoupon(settlementDate)) {
1003
0
                c = 0.0;
1004
0
            }
1005
1006
5.42k
            t += getStepwiseDiscountTime(i, dc, npvDate, lastDate);
1007
5.42k
            DiscountFactor B = y.discountFactor(t);
1008
5.42k
            P += c * B;
1009
5.42k
            switch (y.compounding()) {
1010
2.14k
              case Simple:
1011
2.14k
                d2Pdy2 += c * 2.0*B*B*B*t*t;
1012
2.14k
                break;
1013
946
              case Compounded:
1014
946
                d2Pdy2 += c * B*t*(N*t+1)/(N*(1+r/N)*(1+r/N));
1015
946
                break;
1016
432
              case Continuous:
1017
432
                d2Pdy2 += c * B*t*t;
1018
432
                break;
1019
1.90k
              case SimpleThenCompounded:
1020
1.90k
                if (t<=1.0/N)
1021
408
                    d2Pdy2 += c * 2.0*B*B*B*t*t;
1022
1.49k
                else
1023
1.49k
                    d2Pdy2 += c * B*t*(N*t+1)/(N*(1+r/N)*(1+r/N));
1024
1.90k
                break;
1025
0
              case CompoundedThenSimple:
1026
0
                if (t>1.0/N)
1027
0
                    d2Pdy2 += c * 2.0*B*B*B*t*t;
1028
0
                else
1029
0
                    d2Pdy2 += c * B*t*(N*t+1)/(N*(1+r/N)*(1+r/N));
1030
0
                break;
1031
0
              default:
1032
0
                QL_FAIL("unknown compounding convention (" <<
1033
5.42k
                        Integer(y.compounding()) << ")");
1034
5.42k
            }
1035
5.42k
            lastDate = i->date();
1036
5.42k
        }
1037
1038
1.33k
        if (P == 0.0)
1039
            // no cashflows
1040
164
            return 0.0;
1041
1042
1.16k
        return d2Pdy2/P;
1043
1.33k
    }
1044
1045
1046
    Real CashFlows::convexity(const Leg& leg,
1047
                              Rate yield,
1048
                              const DayCounter& dc,
1049
                              Compounding comp,
1050
                              Frequency freq,
1051
                              const ext::optional<bool>& includeSettlementDateFlows,
1052
                              Date settlementDate,
1053
666
                              Date npvDate) {
1054
666
        return convexity(leg, InterestRate(yield, dc, comp, freq),
1055
666
                         includeSettlementDateFlows,
1056
666
                         settlementDate, npvDate);
1057
666
    }
1058
1059
    Real CashFlows::basisPointValue(const Leg& leg,
1060
                                    const InterestRate& y,
1061
                                    const ext::optional<bool>& includeSettlementDateFlows,
1062
                                    Date settlementDate,
1063
666
                                    Date npvDate) {
1064
666
        if (leg.empty())
1065
0
            return 0.0;
1066
1067
666
        if (settlementDate == Date())
1068
666
            settlementDate = Settings::instance().evaluationDate();
1069
1070
666
        if (npvDate == Date())
1071
666
            npvDate = settlementDate;
1072
1073
666
        Real npv = CashFlows::npv(leg, y,
1074
666
                                  includeSettlementDateFlows,
1075
666
                                  settlementDate, npvDate);
1076
666
        Real modifiedDuration = CashFlows::duration(leg, y,
1077
666
                                                    Duration::Modified,
1078
666
                                                    includeSettlementDateFlows,
1079
666
                                                    settlementDate, npvDate);
1080
666
        Real convexity = CashFlows::convexity(leg, y,
1081
666
                                              includeSettlementDateFlows,
1082
666
                                              settlementDate, npvDate);
1083
666
        Real delta = -modifiedDuration*npv;
1084
666
        Real gamma = (convexity/100.0)*npv;
1085
1086
666
        Real shift = 0.0001;
1087
666
        delta *= shift;
1088
666
        gamma *= shift*shift;
1089
1090
666
        return delta + 0.5*gamma;
1091
666
    }
1092
1093
    Real CashFlows::basisPointValue(const Leg& leg,
1094
                                    Rate yield,
1095
                                    const DayCounter& dc,
1096
                                    Compounding comp,
1097
                                    Frequency freq,
1098
                                    const ext::optional<bool>& includeSettlementDateFlows,
1099
                                    Date settlementDate,
1100
666
                                    Date npvDate) {
1101
666
        return basisPointValue(leg, InterestRate(yield, dc, comp, freq),
1102
666
                               includeSettlementDateFlows,
1103
666
                               settlementDate, npvDate);
1104
666
    }
1105
1106
    Real CashFlows::yieldValueBasisPoint(const Leg& leg,
1107
                                         const InterestRate& y,
1108
                                         const ext::optional<bool>& includeSettlementDateFlows,
1109
                                         Date settlementDate,
1110
666
                                         Date npvDate) {
1111
666
        if (leg.empty())
1112
0
            return 0.0;
1113
1114
666
        if (settlementDate == Date())
1115
666
            settlementDate = Settings::instance().evaluationDate();
1116
1117
666
        if (npvDate == Date())
1118
666
            npvDate = settlementDate;
1119
1120
666
        Real npv = CashFlows::npv(leg, y,
1121
666
                                  includeSettlementDateFlows,
1122
666
                                  settlementDate, npvDate);
1123
666
        Real modifiedDuration = CashFlows::duration(leg, y,
1124
666
                                                    Duration::Modified,
1125
666
                                                    includeSettlementDateFlows,
1126
666
                                                    settlementDate, npvDate);
1127
1128
666
        Real shift = 0.01;
1129
666
        return (1.0/(-npv*modifiedDuration))*shift;
1130
666
    }
1131
1132
    Real CashFlows::yieldValueBasisPoint(const Leg& leg,
1133
                                         Rate yield,
1134
                                         const DayCounter& dc,
1135
                                         Compounding comp,
1136
                                         Frequency freq,
1137
                                         const ext::optional<bool>& includeSettlementDateFlows,
1138
                                         Date settlementDate,
1139
666
                                         Date npvDate) {
1140
666
        return yieldValueBasisPoint(leg, InterestRate(yield, dc, comp, freq),
1141
666
                                    includeSettlementDateFlows,
1142
666
                                    settlementDate, npvDate);
1143
666
    }
1144
1145
    // Z-spread utility functions
1146
    Real CashFlows::npv(const Leg& leg,
1147
                        const ext::shared_ptr<YieldTermStructure>& discountCurve,
1148
                        Spread zSpread,
1149
                        Compounding comp,
1150
                        Frequency freq,
1151
                        const ext::optional<bool>& includeSettlementDateFlows,
1152
                        Date settlementDate,
1153
0
                        Date npvDate) {
1154
1155
0
        if (leg.empty())
1156
0
            return 0.0;
1157
1158
0
        if (settlementDate == Date())
1159
0
            settlementDate = Settings::instance().evaluationDate();
1160
1161
0
        if (npvDate == Date())
1162
0
            npvDate = settlementDate;
1163
1164
0
        Handle<YieldTermStructure> discountCurveHandle(discountCurve);
1165
0
        Handle<Quote> zSpreadQuoteHandle(ext::shared_ptr<Quote>(new
1166
0
            SimpleQuote(zSpread)));
1167
1168
0
        ZeroSpreadedTermStructure spreadedCurve(discountCurveHandle,
1169
0
                                                zSpreadQuoteHandle,
1170
0
                                                comp, freq);
1171
1172
0
        return npv(leg, spreadedCurve,
1173
0
                   includeSettlementDateFlows,
1174
0
                   settlementDate, npvDate);
1175
0
    }
1176
1177
    Real CashFlows::npv(const Leg& leg,
1178
                        const ext::shared_ptr<YieldTermStructure>& discountCurve,
1179
                        Spread zSpread,
1180
                        const DayCounter&,
1181
                        Compounding comp,
1182
                        Frequency freq,
1183
                        const ext::optional<bool>& includeSettlementDateFlows,
1184
                        Date settlementDate,
1185
0
                        Date npvDate) {
1186
0
        return CashFlows::npv(leg, discountCurve, zSpread, comp, freq,
1187
0
                              includeSettlementDateFlows, settlementDate, npvDate);
1188
0
    }
1189
1190
    Spread CashFlows::zSpread(const Leg& leg,
1191
                              Real npv,
1192
                              const ext::shared_ptr<YieldTermStructure>& discount,
1193
                              Compounding compounding,
1194
                              Frequency frequency,
1195
                              const ext::optional<bool>& includeSettlementDateFlows,
1196
                              Date settlementDate,
1197
                              Date npvDate,
1198
                              Real accuracy,
1199
                              Size maxIterations,
1200
0
                              Rate guess) {
1201
1202
0
        if (settlementDate == Date())
1203
0
            settlementDate = Settings::instance().evaluationDate();
1204
1205
0
        if (npvDate == Date())
1206
0
            npvDate = settlementDate;
1207
1208
0
        auto zSpreadQuote = ext::make_shared<SimpleQuote>();
1209
0
        ZeroSpreadedTermStructure spreadedCurve(Handle<YieldTermStructure>(discount),
1210
0
                                                Handle<Quote>(zSpreadQuote),
1211
0
                                                compounding,
1212
0
                                                frequency);
1213
0
        auto objFunction = [&](Rate zSpread) {
1214
0
            zSpreadQuote->setValue(zSpread);
1215
0
            Real NPV = CashFlows::npv(leg, spreadedCurve,
1216
0
                                      includeSettlementDateFlows,
1217
0
                                      settlementDate, npvDate);
1218
0
            return npv - NPV;
1219
0
        };
1220
1221
0
        Brent solver;
1222
0
        solver.setMaxEvaluations(maxIterations);
1223
0
        Real step = 0.01;
1224
0
        return solver.solve(objFunction, accuracy, guess, step);
1225
0
    }
1226
1227
    Spread CashFlows::zSpread(const Leg& leg,
1228
                              Real npv,
1229
                              const ext::shared_ptr<YieldTermStructure>& discount,
1230
                              const DayCounter&,
1231
                              Compounding compounding,
1232
                              Frequency frequency,
1233
                              const ext::optional<bool>& includeSettlementDateFlows,
1234
                              Date settlementDate,
1235
                              Date npvDate,
1236
                              Real accuracy,
1237
                              Size maxIterations,
1238
0
                              Rate guess) {
1239
0
        return CashFlows::zSpread(leg, npv, discount, compounding, frequency,
1240
0
                                  includeSettlementDateFlows, settlementDate, npvDate,
1241
0
                                  accuracy, maxIterations, guess);
1242
0
    }
1243
1244
}