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