Coverage Report

Created: 2025-08-28 06:30

/src/quantlib/ql/instruments/zerocouponinflationswap.cpp
Line
Count
Source (jump to first uncovered line)
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
 <http://quantlib.org/license.shtml>.
16
17
 This program is distributed in the hope that it will be useful, but WITHOUT
18
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19
 FOR A PARTICULAR PURPOSE.  See the license for more details.
20
 */
21
22
#include <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(
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)
50
0
    : Swap(2), type_(type), nominal_(nominal), startDate_(startDate), maturityDate_(maturity),
51
0
      fixCalendar_(std::move(fixCalendar)), fixConvention_(fixConvention), fixedRate_(fixedRate),
52
0
      infIndex_(infIndex), observationLag_(observationLag),
53
0
      observationInterpolation_(observationInterpolation), adjustInfObsDates_(adjustInfObsDates),
54
0
      infCalendar_(std::move(infCalendar)), infConvention_(infConvention),
55
0
      dayCounter_(std::move(dayCounter)) {
56
        // first check compatibility of index and swap definitions
57
0
        if (detail::CPI::effectiveInterpolationType(observationInterpolation_) == CPI::Linear) {
58
0
            Period pShift(infIndex_->frequency());
59
0
            QL_REQUIRE(observationLag_ - pShift >= infIndex_->availabilityLag(),
60
0
                       "inconsistency between swap observation lag "
61
0
                           << observationLag_ << ", interpolated index period "
62
0
                           << pShift << " and index availability " << infIndex_->availabilityLag()
63
0
                           << ": need (obsLag-index period) >= availLag");
64
0
        } else {
65
0
            QL_REQUIRE(infIndex_->availabilityLag() <= observationLag_,
66
0
                       "index tries to observe inflation fixings that do not yet exist: "
67
0
                           << " availability lag " << infIndex_->availabilityLag()
68
0
                           << " versus obs lag = " << observationLag_);
69
0
        }
70
71
0
        if (infCalendar_ == Calendar())
72
0
            infCalendar_ = fixCalendar_;
73
0
        if (infConvention_ == BusinessDayConvention())
74
0
            infConvention_ = fixConvention_;
75
76
0
        Date infPayDate = infCalendar_.adjust(maturity, infConvention_);
77
0
        Date fixedPayDate = fixCalendar_.adjust(maturity, fixConvention_);
78
79
0
        bool growthOnly = true;
80
81
0
        auto inflationCashFlow =
82
0
            ext::make_shared<ZeroInflationCashFlow>(nominal, infIndex, observationInterpolation_,
83
0
                                                    startDate, maturity, observationLag_,
84
0
                                                    infPayDate, growthOnly);
85
86
0
        baseDate_ = inflationCashFlow->baseDate();
87
0
        obsDate_ = inflationCashFlow->fixingDate();
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
0
        Real T =
93
0
            inflationYearFraction(infIndex_->frequency(),
94
0
                                  detail::CPI::isInterpolated(observationInterpolation_),
95
0
                                  dayCounter_, baseDate_, obsDate_);
96
        // N.B. the -1.0 is because swaps only exchange growth, not notionals as well
97
0
        Real fixedAmount = nominal * (std::pow(1.0 + fixedRate, T) - 1.0);
98
99
0
        auto fixedCashFlow = ext::make_shared<SimpleCashFlow>(fixedAmount, fixedPayDate);
100
101
0
        legs_[0].push_back(fixedCashFlow);
102
0
        legs_[1].push_back(inflationCashFlow);
103
104
0
        registerWith(inflationCashFlow);
105
106
0
        switch (type_) {
107
0
            case Payer:
108
0
                payer_[0] = +1.0;
109
0
                payer_[1] = -1.0;
110
0
                break;
111
0
            case Receiver:
112
0
                payer_[0] = -1.0;
113
0
                payer_[1] = +1.0;
114
0
                break;
115
0
            default:
116
0
                QL_FAIL("Unknown zero-inflation-swap type");
117
0
        }
118
0
    }
Unexecuted instantiation: QuantLib::ZeroCouponInflationSwap::ZeroCouponInflationSwap(QuantLib::Swap::Type, double, QuantLib::Date const&, QuantLib::Date const&, QuantLib::Calendar, QuantLib::BusinessDayConvention, QuantLib::DayCounter, double, boost::shared_ptr<QuantLib::ZeroInflationIndex> const&, QuantLib::Period const&, QuantLib::CPI::InterpolationType, bool, QuantLib::Calendar, QuantLib::BusinessDayConvention)
Unexecuted instantiation: QuantLib::ZeroCouponInflationSwap::ZeroCouponInflationSwap(QuantLib::Swap::Type, double, QuantLib::Date const&, QuantLib::Date const&, QuantLib::Calendar, QuantLib::BusinessDayConvention, QuantLib::DayCounter, double, boost::shared_ptr<QuantLib::ZeroInflationIndex> const&, QuantLib::Period const&, QuantLib::CPI::InterpolationType, bool, QuantLib::Calendar, QuantLib::BusinessDayConvention)
119
120
121
0
    Real ZeroCouponInflationSwap::fairRate() const {
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
0
        ext::shared_ptr<IndexedCashFlow> icf =
128
0
        ext::dynamic_pointer_cast<IndexedCashFlow>(legs_[1].at(0));
129
0
        QL_REQUIRE(icf,"failed to downcast to IndexedCashFlow in ::fairRate()");
130
131
        // +1 because the IndexedCashFlow has growthOnly=true
132
0
        Real growth = icf->amount() / icf->notional() + 1.0;
133
0
        Real T =
134
0
            inflationYearFraction(infIndex_->frequency(),
135
0
                                  detail::CPI::isInterpolated(observationInterpolation_),
136
0
                                  dayCounter_, baseDate_, obsDate_);
137
138
0
        return std::pow(growth,1.0/T) - 1.0;
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
0
    }
145
146
147
0
    Real ZeroCouponInflationSwap::fixedLegNPV() const {
148
0
        calculate();
149
0
        QL_REQUIRE(legNPV_[0] != Null<Real>(), "result not available");
150
0
        return legNPV_[0];
151
0
    }
152
153
0
    Real ZeroCouponInflationSwap::inflationLegNPV() const {
154
0
        calculate();
155
0
        QL_REQUIRE(legNPV_[1] != Null<Real>(), "result not available");
156
0
        return legNPV_[1];
157
0
    }
158
159
0
    const Leg& ZeroCouponInflationSwap::fixedLeg() const {
160
0
        return legs_[0];
161
0
    }
162
163
0
    const Leg& ZeroCouponInflationSwap::inflationLeg() const {
164
0
        return legs_[1];
165
0
    }
166
167
}