Coverage Report

Created: 2025-10-14 06:32

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