• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lballabio / QuantLib / 18905857194

29 Oct 2025 11:09AM UTC coverage: 74.32% (+0.4%) from 73.914%
18905857194

Pull #2344

github

web-flow
Merge d353587bf into d823f4ecb
Pull Request #2344: add multicurve bootstrap

99 of 103 new or added lines in 8 files covered. (96.12%)

216 existing lines in 13 files now uncovered.

57072 of 76792 relevant lines covered (74.32%)

8781066.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.91
/ql/instruments/zerocouponinflationswap.cpp
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2

3
/*
4
 Copyright (C) 2007, 2009 Chris Kenyon
5
 Copyright (C) 2009 StatPro Italia srl
6
 Copyright (C) 2021 Ralf Konrad Eckel
7

8
 This file is part of QuantLib, a free-software/open-source library
9
 for financial quantitative analysts and developers - http://quantlib.org/
10

11
 QuantLib is free software: you can redistribute it and/or modify it
12
 under the terms of the QuantLib license.  You should have received a
13
 copy of the license along with this program; if not, please email
14
 <quantlib-dev@lists.sf.net>. The license is also available online at
15
 <https://www.quantlib.org/license.shtml>.
16

17
 This program is distributed in the hope that it will be useful, but WITHOUT
18
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19
 FOR A PARTICULAR PURPOSE.  See the license for more details.
20
 */
21

22
#include <ql/cashflows/zeroinflationcashflow.hpp>
23
#include <ql/cashflows/simplecashflow.hpp>
24
#include <ql/instruments/zerocouponinflationswap.hpp>
25
#include <ql/time/calendars/nullcalendar.hpp>
26
#include <utility>
27

28
namespace QuantLib {
29

30
    /* Generally inflation indices are available with a lag of 1month
31
       and then observed with a lag of 2-3 months depending whether
32
       they use an interpolated fixing or not.  Here, we make the
33
       swap use the interpolation of the index to avoid incompatibilities.
34
    */
35
    ZeroCouponInflationSwap::ZeroCouponInflationSwap(
246✔
36
        Type type,
37
        Real nominal,
38
        const Date& startDate, // start date of contract (only)
39
        const Date& maturity,  // this is pre-adjustment!
40
        Calendar fixCalendar,
41
        BusinessDayConvention fixConvention,
42
        DayCounter dayCounter,
43
        Rate fixedRate,
44
        const ext::shared_ptr<ZeroInflationIndex>& infIndex,
45
        const Period& observationLag,
46
        CPI::InterpolationType observationInterpolation,
47
        bool adjustInfObsDates,
48
        Calendar infCalendar,
49
        BusinessDayConvention infConvention)
246✔
50
    : Swap(2), type_(type), nominal_(nominal), startDate_(startDate), maturityDate_(maturity),
246✔
51
      fixCalendar_(std::move(fixCalendar)), fixConvention_(fixConvention), fixedRate_(fixedRate),
246✔
52
      infIndex_(infIndex), observationLag_(observationLag),
246✔
53
      observationInterpolation_(observationInterpolation), adjustInfObsDates_(adjustInfObsDates),
246✔
54
      infCalendar_(std::move(infCalendar)), infConvention_(infConvention),
246✔
55
      dayCounter_(std::move(dayCounter)) {
246✔
56
        // first check compatibility of index and swap definitions
57
        if (detail::CPI::effectiveInterpolationType(observationInterpolation_) == CPI::Linear) {
246✔
58
            Period pShift(infIndex_->frequency());
×
59
            QL_REQUIRE(observationLag_ - pShift >= infIndex_->availabilityLag(),
×
60
                       "inconsistency between swap observation lag "
61
                           << observationLag_ << ", interpolated index period "
62
                           << pShift << " and index availability " << infIndex_->availabilityLag()
63
                           << ": need (obsLag-index period) >= availLag");
64
        } else {
65
            QL_REQUIRE(infIndex_->availabilityLag() <= observationLag_,
246✔
66
                       "index tries to observe inflation fixings that do not yet exist: "
67
                           << " availability lag " << infIndex_->availabilityLag()
68
                           << " versus obs lag = " << observationLag_);
69
        }
70

71
        if (infCalendar_ == Calendar())
492✔
72
            infCalendar_ = fixCalendar_;
73
        if (infConvention_ == BusinessDayConvention())
246✔
74
            infConvention_ = fixConvention_;
246✔
75

76
        Date infPayDate = infCalendar_.adjust(maturity, infConvention_);
246✔
77
        Date fixedPayDate = fixCalendar_.adjust(maturity, fixConvention_);
246✔
78

79
        bool growthOnly = true;
246✔
80

81
        auto inflationCashFlow =
82
            ext::make_shared<ZeroInflationCashFlow>(nominal, infIndex, observationInterpolation_,
83
                                                    startDate, maturity, observationLag_,
246✔
84
                                                    infPayDate, growthOnly);
246✔
85

86
        baseDate_ = inflationCashFlow->baseDate();
246✔
87
        obsDate_ = inflationCashFlow->fixingDate();
246✔
88

89
        // At this point the index may not be able to forecast
90
        // i.e. do not want to force the existence of an inflation
91
        // term structure before allowing users to create instruments.
92
        Real T =
93
            inflationYearFraction(infIndex_->frequency(),
246✔
94
                                  detail::CPI::isInterpolated(observationInterpolation_),
246✔
95
                                  dayCounter_, baseDate_, obsDate_);
96
        // N.B. the -1.0 is because swaps only exchange growth, not notionals as well
97
        Real fixedAmount = nominal * (std::pow(1.0 + fixedRate, T) - 1.0);
246✔
98

99
        auto fixedCashFlow = ext::make_shared<SimpleCashFlow>(fixedAmount, fixedPayDate);
246✔
100

101
        legs_[0].push_back(fixedCashFlow);
246✔
102
        legs_[1].push_back(inflationCashFlow);
246✔
103

104
        registerWith(inflationCashFlow);
246✔
105

106
        switch (type_) {
246✔
107
            case Payer:
246✔
108
                payer_[0] = +1.0;
246✔
109
                payer_[1] = -1.0;
246✔
110
                break;
246✔
111
            case Receiver:
×
112
                payer_[0] = -1.0;
×
113
                payer_[1] = +1.0;
×
114
                break;
×
115
            default:
×
116
                QL_FAIL("Unknown zero-inflation-swap type");
×
117
        }
118
    }
246✔
119

120

121
    Real ZeroCouponInflationSwap::fairRate() const {
1,533✔
122
        // What does this mean before or after trade date?
123
        // Always means that NPV is zero for _this_ instrument
124
        // if it was created with _this_ rate
125
        // _knowing_ the time from base to obs (etc).
126

127
        ext::shared_ptr<IndexedCashFlow> icf =
128
        ext::dynamic_pointer_cast<IndexedCashFlow>(legs_[1].at(0));
1,533✔
129
        QL_REQUIRE(icf,"failed to downcast to IndexedCashFlow in ::fairRate()");
1,533✔
130

131
        // +1 because the IndexedCashFlow has growthOnly=true
132
        Real growth = icf->amount() / icf->notional() + 1.0;
1,533✔
133
        Real T =
134
            inflationYearFraction(infIndex_->frequency(),
1,533✔
135
                                  detail::CPI::isInterpolated(observationInterpolation_),
1,533✔
136
                                  dayCounter_, baseDate_, obsDate_);
1,533✔
137

138
        return std::pow(growth,1.0/T) - 1.0;
3,066✔
139

140
        // we cannot use this simple definition because
141
        // it does not work for already-issued instruments
142
        // return infIndex_->zeroInflationTermStructure()->zeroRate(
143
        //      maturityDate(), observationLag(), infIndex_->interpolated());
144
    }
145

146
    Real ZeroCouponInflationSwap::fixedLegBPS() const {
28✔
147
        // legBPS_[0] is 0, because fixed leg uses a SimpleCashFlow. BPSCalculator assumes
148
        // that simple cashflows are not sensitive to fixedRate. We could change that to a
149
        // FixedRateCoupon, however BPSCalculator also assumes that all coupons are linear
150
        // in fixedRate. Our fixed leg uses annual compounding, so it does not compute the
151
        // right number.
152
        calculate();
28✔
153
        QL_REQUIRE(endDiscounts_[0] != Null<DiscountFactor>(),
28✔
154
                   "cannot calculate fixedLegBPS because end discount is not populated");
155

156
        const Spread basisPoint = 1.0e-4;
157
        DiscountFactor df = payer_[0] * endDiscounts_[0];
28✔
158
        Real T =
159
            inflationYearFraction(infIndex_->frequency(),
28✔
160
                                  detail::CPI::isInterpolated(observationInterpolation_),
28✔
161
                                  dayCounter_, baseDate_, obsDate_);
28✔
162

163
        return df * nominal_ * (pow(1.0 + fixedRate_ + basisPoint, T) - pow(1.0 + fixedRate_, T));
28✔
164
    }
165

UNCOV
166
    Real ZeroCouponInflationSwap::fixedLegNPV() const {
×
UNCOV
167
        calculate();
×
UNCOV
168
        QL_REQUIRE(legNPV_[0] != Null<Real>(), "result not available");
×
UNCOV
169
        return legNPV_[0];
×
170
    }
171

UNCOV
172
    Real ZeroCouponInflationSwap::inflationLegNPV() const {
×
UNCOV
173
        calculate();
×
UNCOV
174
        QL_REQUIRE(legNPV_[1] != Null<Real>(), "result not available");
×
UNCOV
175
        return legNPV_[1];
×
176
    }
177

UNCOV
178
    const Leg& ZeroCouponInflationSwap::fixedLeg() const {
×
UNCOV
179
        return legs_[0];
×
180
    }
181

182
    const Leg& ZeroCouponInflationSwap::inflationLeg() const {
1✔
183
        return legs_[1];
1✔
184
    }
185

186
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc