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 
7  This file is part of QuantLib, a free-software/open-source library
8  for financial quantitative analysts and developers - http://quantlib.org/
9 
10  QuantLib is free software: you can redistribute it and/or modify it
11  under the terms of the QuantLib license.  You should have received a
12  copy of the license along with this program; if not, please email
13  <quantlib-dev@lists.sf.net>. The license is also available online at
14  <http://quantlib.org/license.shtml>.
15 
16  This program is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  FOR A PARTICULAR PURPOSE.  See the license for more details.
19  */
20 
21 #include <ql/cashflows/indexedcashflow.hpp>
22 #include <ql/cashflows/simplecashflow.hpp>
23 #include <ql/indexes/inflationindex.hpp>
24 #include <ql/instruments/zerocouponinflationswap.hpp>
25 #include <utility>
26 
27 namespace QuantLib {
28 
29     /* Generally inflation indices are available with a lag of 1month
30        and then observed with a lag of 2-3 months depending whether
31        they use an interpolated fixing or not.  Here, we make the
32        swap use the interpolation of the index to avoid incompatibilities.
33     */
ZeroCouponInflationSwap(Type type,Real nominal,const Date & startDate,const Date & maturity,const Calendar & fixCalendar,BusinessDayConvention fixConvention,const DayCounter & dayCounter,Rate fixedRate,const ext::shared_ptr<ZeroInflationIndex> & infIndex,const Period & observationLag,bool adjustInfObsDates,const Calendar & infCalendar,BusinessDayConvention infConvention)34     ZeroCouponInflationSwap::ZeroCouponInflationSwap(
35         Type type,
36         Real nominal,
37         const Date& startDate, // start date of contract (only)
38         const Date& maturity,  // this is pre-adjustment!
39         const Calendar& fixCalendar,
40         BusinessDayConvention fixConvention,
41         const DayCounter& dayCounter,
42         Rate fixedRate,
43         const ext::shared_ptr<ZeroInflationIndex>& infIndex,
44         const Period& observationLag,
45         bool adjustInfObsDates,
46         const Calendar& infCalendar,
47         BusinessDayConvention infConvention)
48     : Swap(2), type_(type), nominal_(nominal), startDate_(startDate), maturityDate_(maturity),
49       fixCalendar_(fixCalendar), fixConvention_(fixConvention), fixedRate_(fixedRate),
50       infIndex_(infIndex), observationLag_(observationLag), adjustInfObsDates_(adjustInfObsDates),
51       infCalendar_(infCalendar), infConvention_(infConvention), dayCounter_(dayCounter) {
52         // first check compatibility of index and swap definitions
53         if (infIndex_->interpolated()) {
54             Period pShift(infIndex_->frequency());
55             QL_REQUIRE(observationLag_ - pShift > infIndex_->availabilityLag(),
56                        "inconsistency between swap observation of index " << observationLag_ <<
57                        " index availability " << infIndex_->availabilityLag() <<
58                        " interpolated index period " << pShift <<
59                        " and index availability " << infIndex_->availabilityLag() <<
60                        " need (obsLag-index period) > availLag");
61         } else {
62             QL_REQUIRE(infIndex_->availabilityLag() < observationLag_,
63                        "index tries to observe inflation fixings that do not yet exist: "
64                        << " availability lag " << infIndex_->availabilityLag()
65                        << " versus obs lag = " << observationLag_);
66         }
67 
68         if (infCalendar_==Calendar()) infCalendar_ = fixCalendar_;
69         if (infConvention_==BusinessDayConvention()) infConvention_ = fixConvention_;
70 
71         if (adjustInfObsDates_) {
72             baseDate_ = infCalendar_.adjust(startDate - observationLag_, infConvention_);
73             obsDate_ = infCalendar_.adjust(maturity - observationLag_, infConvention_);
74         } else {
75             baseDate_ = startDate - observationLag_;
76             obsDate_ = maturity - observationLag_;
77         }
78 
79         Date infPayDate = infCalendar_.adjust(maturity, infConvention_);
80         Date fixedPayDate = fixCalendar_.adjust(maturity, fixConvention_);
81 
82         // At this point the index may not be able to forecast
83         // i.e. do not want to force the existence of an inflation
84         // term structure before allowing users to create instruments.
85         Real T = inflationYearFraction(infIndex_->frequency(), infIndex_->interpolated(),
86                                        dayCounter_, baseDate_, obsDate_);
87         // N.B. the -1.0 is because swaps only exchange growth, not notionals as well
88         Real fixedAmount = nominal * ( std::pow(1.0 + fixedRate, T) - 1.0 );
89 
90         legs_[0].push_back(ext::shared_ptr<CashFlow>(
91             new SimpleCashFlow(fixedAmount, fixedPayDate)));
92         bool growthOnly = true;
93         legs_[1].push_back(ext::shared_ptr<CashFlow>(
94             new IndexedCashFlow(nominal,infIndex,baseDate_,obsDate_,infPayDate,growthOnly)));
95 
96         for (Size j=0; j<2; ++j) {
97             for (Leg::iterator i = legs_[j].begin(); i!= legs_[j].end(); ++i)
98                 registerWith(*i);
99         }
100 
101         switch (type_) {
102             case Payer:
103                 payer_[0] = +1.0;
104                 payer_[1] = -1.0;
105                 break;
106             case Receiver:
107                 payer_[0] = -1.0;
108                 payer_[1] = +1.0;
109                 break;
110             default:
111                 QL_FAIL("Unknown zero-inflation-swap type");
112         }
113 
114     }
115 
116 
117 
setupArguments(PricingEngine::arguments * args) const118     void ZeroCouponInflationSwap::setupArguments(PricingEngine::arguments* args) const {
119         Swap::setupArguments(args);
120         // you don't actually need to do anything else because it is so simple
121     }
122 
validate() const123     void ZeroCouponInflationSwap::arguments::validate() const {
124         Swap::arguments::validate();
125         // you don't actually need to do anything else because it is so simple
126     }
127 
fetchResults(const PricingEngine::results * r) const128     void ZeroCouponInflationSwap::fetchResults(const PricingEngine::results* r) const {
129         Swap::fetchResults(r);
130         // you don't actually need to do anything else because it is so simple
131     }
132 
fairRate() const133     Real ZeroCouponInflationSwap::fairRate() const {
134         // What does this mean before or after trade date?
135         // Always means that NPV is zero for _this_ instrument
136         // if it was created with _this_ rate
137         // _knowing_ the time from base to obs (etc).
138 
139         ext::shared_ptr<IndexedCashFlow> icf =
140         ext::dynamic_pointer_cast<IndexedCashFlow>(legs_[1].at(0));
141         QL_REQUIRE(icf,"failed to downcast to IndexedCashFlow in ::fairRate()");
142 
143         // +1 because the IndexedCashFlow has growthOnly=true
144         Real growth = icf->amount() / icf->notional() + 1.0;
145         Real T = inflationYearFraction(infIndex_->frequency(),
146                                        infIndex_->interpolated(),
147                                        dayCounter_, baseDate_, obsDate_);
148 
149         return std::pow(growth,1.0/T) - 1.0;
150 
151         // we cannot use this simple definition because
152         // it does not work for already-issued instruments
153         // return infIndex_->zeroInflationTermStructure()->zeroRate(
154         //      maturityDate(), observationLag(), infIndex_->interpolated());
155     }
156 
157 
fixedLegNPV() const158     Real ZeroCouponInflationSwap::fixedLegNPV() const {
159         calculate();
160         QL_REQUIRE(legNPV_[0] != Null<Real>(), "result not available");
161         return legNPV_[0];
162     }
163 
inflationLegNPV() const164     Real ZeroCouponInflationSwap::inflationLegNPV() const {
165         calculate();
166         QL_REQUIRE(legNPV_[1] != Null<Real>(), "result not available");
167         return legNPV_[1];
168     }
169 
fixedLeg() const170     const Leg& ZeroCouponInflationSwap::fixedLeg() const {
171         return legs_[0];
172     }
173 
inflationLeg() const174     const Leg& ZeroCouponInflationSwap::inflationLeg() const {
175         return legs_[1];
176     }
177 
178 }
179 
180