/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 | | } |