1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 3 /* 4 Copyright (C) 2009 Chris Kenyon 5 6 This file is part of QuantLib, a free-software/open-source library 7 for financial quantitative analysts and developers - http://quantlib.org/ 8 9 QuantLib is free software: you can redistribute it and/or modify it 10 under the terms of the QuantLib license. You should have received a 11 copy of the license along with this program; if not, please email 12 <quantlib-dev@lists.sf.net>. The license is also available online at 13 <http://quantlib.org/license.shtml>. 14 15 This program is distributed in the hope that it will be useful, but WITHOUT 16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 17 FOR A PARTICULAR PURPOSE. See the license for more details. 18 */ 19 20 21 22 #include <ql/cashflows/inflationcoupon.hpp> 23 #include <ql/cashflows/cashflowvectors.hpp> 24 #include <ql/time/daycounters/thirty360.hpp> 25 26 #include <ql/cashflows/cpicoupon.hpp> 27 #include <ql/cashflows/cpicouponpricer.hpp> 28 29 30 namespace QuantLib { 31 32 CPICoupon:: CPICoupon(Real baseCPI,const Date & paymentDate,Real nominal,const Date & startDate,const Date & endDate,Natural fixingDays,const ext::shared_ptr<ZeroInflationIndex> & zeroIndex,const Period & observationLag,CPI::InterpolationType observationInterpolation,const DayCounter & dayCounter,Real fixedRate,Spread spread,const Date & refPeriodStart,const Date & refPeriodEnd,const Date & exCouponDate)33 CPICoupon( 34 Real baseCPI, // user provided 35 const Date& paymentDate, 36 Real nominal, 37 const Date& startDate, 38 const Date& endDate, 39 Natural fixingDays, 40 const ext::shared_ptr<ZeroInflationIndex>& zeroIndex, 41 const Period& observationLag, 42 CPI::InterpolationType observationInterpolation, 43 const DayCounter& dayCounter, 44 Real fixedRate, // aka gearing 45 Spread spread, 46 const Date& refPeriodStart, 47 const Date& refPeriodEnd, 48 const Date& exCouponDate) 49 : InflationCoupon(paymentDate, nominal, startDate, endDate, 50 fixingDays, zeroIndex, observationLag, 51 dayCounter, refPeriodStart, refPeriodEnd, exCouponDate), 52 baseCPI_(baseCPI), fixedRate_(fixedRate), spread_(spread), 53 observationInterpolation_(observationInterpolation) { 54 55 QL_REQUIRE(std::fabs(baseCPI_) > 1e-16, 56 "|baseCPI_| < 1e-16, future divide-by-zero problem"); 57 } 58 59 accept(AcyclicVisitor & v)60 void CPICoupon::accept(AcyclicVisitor& v) { 61 Visitor<CPICoupon>* v1 = 62 dynamic_cast<Visitor<CPICoupon>*>(&v); 63 if (v1 != 0) 64 v1->visit(*this); 65 else 66 InflationCoupon::accept(v); 67 } 68 69 checkPricerImpl(const ext::shared_ptr<InflationCouponPricer> & pricer) const70 bool CPICoupon::checkPricerImpl( 71 const ext::shared_ptr<InflationCouponPricer>&pricer) const { 72 return static_cast<bool>( 73 ext::dynamic_pointer_cast<CPICouponPricer>(pricer)); 74 } 75 76 indexFixing(const Date & d) const77 Rate CPICoupon::indexFixing(const Date &d) const { 78 // you may want to modify the interpolation of the index 79 // this gives you the chance 80 81 Rate I1; 82 // what interpolation do we use? Index / flat / linear 83 if (observationInterpolation() == CPI::AsIndex) { 84 I1 = cpiIndex()->fixing(d); 85 86 } else { 87 // work out what it should be 88 std::pair<Date,Date> dd = inflationPeriod(d, cpiIndex()->frequency()); 89 Real indexStart = cpiIndex()->fixing(dd.first); 90 if (observationInterpolation() == CPI::Linear) { 91 Real indexEnd = cpiIndex()->fixing(dd.second+Period(1,Days)); 92 // linear interpolation 93 I1 = indexStart + (indexEnd - indexStart) * (d - dd.first) 94 / (Real)( (dd.second+Period(1,Days)) - dd.first); // can't get to next period's value within current period 95 } else { 96 // no interpolation, i.e. flat = constant, so use start-of-period value 97 I1 = indexStart; 98 } 99 100 } 101 return I1; 102 } 103 104 105 106 baseDate() const107 Date CPICashFlow::baseDate() const { 108 // you may not have a valid date 109 QL_FAIL("no base date specified"); 110 } 111 baseFixing() const112 Real CPICashFlow::baseFixing() const { 113 return baseFixing_; 114 } 115 amount() const116 Real CPICashFlow::amount() const { 117 Real I0 = baseFixing(); 118 Real I1; 119 120 // what interpolation do we use? Index / flat / linear 121 if (interpolation() == CPI::AsIndex ) { 122 I1 = index()->fixing(fixingDate()); 123 } else { 124 // work out what it should be 125 //std::cout << fixingDate() << " and " << frequency() << std::endl; 126 //std::pair<Date,Date> dd = inflationPeriod(fixingDate(), frequency()); 127 //std::cout << fixingDate() << " and " << dd.first << " " << dd.second << std::endl; 128 // work out what it should be 129 std::pair<Date,Date> dd = inflationPeriod(fixingDate(), frequency()); 130 Real indexStart = index()->fixing(dd.first); 131 if (interpolation() == CPI::Linear) { 132 Real indexEnd = index()->fixing(dd.second+Period(1,Days)); 133 // linear interpolation 134 //std::cout << indexStart << " and " << indexEnd << std::endl; 135 I1 = indexStart + (indexEnd - indexStart) * (fixingDate() - dd.first) 136 / ( (dd.second+Period(1,Days)) - dd.first); // can't get to next period's value within current period 137 } else { 138 // no interpolation, i.e. flat = constant, so use start-of-period value 139 I1 = indexStart; 140 } 141 142 } 143 144 145 if (growthOnly()) 146 return notional() * (I1 / I0 - 1.0); 147 else 148 return notional() * (I1 / I0); 149 } 150 151 CPILeg(const Schedule & schedule,const ext::shared_ptr<ZeroInflationIndex> & index,const Real baseCPI,const Period & observationLag)152 CPILeg::CPILeg(const Schedule& schedule, const ext::shared_ptr<ZeroInflationIndex>& index, 153 const Real baseCPI, const Period& observationLag) : 154 schedule_(schedule), index_(index), 155 baseCPI_(baseCPI), observationLag_(observationLag), 156 paymentDayCounter_(Thirty360()), 157 paymentAdjustment_(ModifiedFollowing), 158 paymentCalendar_(schedule.calendar()), 159 fixingDays_(std::vector<Natural>(1,0)), 160 observationInterpolation_(CPI::AsIndex), 161 subtractInflationNominal_(true), 162 spreads_(std::vector<Real>(1,0)) 163 {} 164 165 withObservationInterpolation(CPI::InterpolationType interp)166 CPILeg& CPILeg::withObservationInterpolation(CPI::InterpolationType interp) { 167 observationInterpolation_ = interp; 168 return *this; 169 } 170 171 withFixedRates(Real fixedRate)172 CPILeg& CPILeg::withFixedRates(Real fixedRate) { 173 fixedRates_ = std::vector<Real>(1,fixedRate); 174 return *this; 175 } 176 withFixedRates(const std::vector<Real> & fixedRates)177 CPILeg& CPILeg::withFixedRates(const std::vector<Real>& fixedRates) { 178 fixedRates_ = fixedRates; 179 return *this; 180 } 181 withNotionals(Real notional)182 CPILeg& CPILeg::withNotionals(Real notional) { 183 notionals_ = std::vector<Real>(1,notional); 184 return *this; 185 } 186 withNotionals(const std::vector<Real> & notionals)187 CPILeg& CPILeg::withNotionals(const std::vector<Real>& notionals) { 188 notionals_ = notionals; 189 return *this; 190 } 191 withSubtractInflationNominal(bool growthOnly)192 CPILeg& CPILeg::withSubtractInflationNominal(bool growthOnly) { 193 subtractInflationNominal_ = growthOnly; 194 return *this; 195 } 196 withPaymentDayCounter(const DayCounter & dayCounter)197 CPILeg& CPILeg::withPaymentDayCounter(const DayCounter& dayCounter) { 198 paymentDayCounter_ = dayCounter; 199 return *this; 200 } 201 withPaymentAdjustment(BusinessDayConvention convention)202 CPILeg& CPILeg::withPaymentAdjustment(BusinessDayConvention convention) { 203 paymentAdjustment_ = convention; 204 return *this; 205 } 206 withPaymentCalendar(const Calendar & cal)207 CPILeg& CPILeg::withPaymentCalendar(const Calendar& cal) { 208 paymentCalendar_ = cal; 209 return *this; 210 } 211 withFixingDays(Natural fixingDays)212 CPILeg& CPILeg::withFixingDays(Natural fixingDays) { 213 fixingDays_ = std::vector<Natural>(1,fixingDays); 214 return *this; 215 } 216 withFixingDays(const std::vector<Natural> & fixingDays)217 CPILeg& CPILeg::withFixingDays(const std::vector<Natural>& fixingDays) { 218 fixingDays_ = fixingDays; 219 return *this; 220 } 221 withSpreads(Spread spread)222 CPILeg& CPILeg::withSpreads(Spread spread) { 223 spreads_ = std::vector<Spread>(1,spread); 224 return *this; 225 } 226 withSpreads(const std::vector<Spread> & spreads)227 CPILeg& CPILeg::withSpreads(const std::vector<Spread>& spreads) { 228 spreads_ = spreads; 229 return *this; 230 } 231 withCaps(Rate cap)232 CPILeg& CPILeg::withCaps(Rate cap) { 233 caps_ = std::vector<Rate>(1,cap); 234 return *this; 235 } 236 withCaps(const std::vector<Rate> & caps)237 CPILeg& CPILeg::withCaps(const std::vector<Rate>& caps) { 238 caps_ = caps; 239 return *this; 240 } 241 withFloors(Rate floor)242 CPILeg& CPILeg::withFloors(Rate floor) { 243 floors_ = std::vector<Rate>(1,floor); 244 return *this; 245 } 246 withFloors(const std::vector<Rate> & floors)247 CPILeg& CPILeg::withFloors(const std::vector<Rate>& floors) { 248 floors_ = floors; 249 return *this; 250 } 251 withExCouponPeriod(const Period & period,const Calendar & cal,BusinessDayConvention convention,bool endOfMonth)252 CPILeg& CPILeg::withExCouponPeriod( 253 const Period& period, 254 const Calendar& cal, 255 BusinessDayConvention convention, 256 bool endOfMonth) { 257 exCouponPeriod_ = period; 258 exCouponCalendar_ = cal; 259 exCouponAdjustment_ = convention; 260 exCouponEndOfMonth_ = endOfMonth; 261 return *this; 262 } 263 264 operator Leg() const265 CPILeg::operator Leg() const { 266 267 QL_REQUIRE(!notionals_.empty(), "no notional given"); 268 Size n = schedule_.size()-1; 269 Leg leg; 270 leg.reserve(n+1); // +1 for notional, we always have some sort ... 271 if (n>0) { 272 QL_REQUIRE(!fixedRates_.empty() || !spreads_.empty(), 273 "no fixedRates or spreads given"); 274 275 Date refStart, start, refEnd, end; 276 277 for (Size i=0; i<n; ++i) { 278 refStart = start = schedule_.date(i); 279 refEnd = end = schedule_.date(i+1); 280 Date paymentDate = paymentCalendar_.adjust(end, paymentAdjustment_); 281 282 Date exCouponDate; 283 if (exCouponPeriod_ != Period()) 284 { 285 exCouponDate = exCouponCalendar_.advance(paymentDate, 286 -exCouponPeriod_, 287 exCouponAdjustment_, 288 exCouponEndOfMonth_); 289 } 290 291 if (i==0 && schedule_.hasIsRegular() && !schedule_.isRegular(i+1)) { 292 BusinessDayConvention bdc = schedule_.businessDayConvention(); 293 refStart = schedule_.calendar().adjust(end - schedule_.tenor(), bdc); 294 } 295 if (i==n-1 && schedule_.hasIsRegular() && !schedule_.isRegular(i+1)) { 296 BusinessDayConvention bdc = schedule_.businessDayConvention(); 297 refEnd = schedule_.calendar().adjust(start + schedule_.tenor(), bdc); 298 } 299 if (detail::get(fixedRates_, i, 1.0) == 0.0) { // fixed coupon 300 leg.push_back(ext::shared_ptr<CashFlow> 301 (new FixedRateCoupon 302 (paymentDate, detail::get(notionals_, i, 0.0), 303 detail::effectiveFixedRate(spreads_,caps_,floors_,i), 304 paymentDayCounter_, start, end, refStart, refEnd, exCouponDate))); 305 } else { // zero inflation coupon 306 if (detail::noOption(caps_, floors_, i)) { // just swaplet 307 ext::shared_ptr<CPICoupon> coup; 308 309 coup = ext::shared_ptr<CPICoupon> 310 (new CPICoupon(baseCPI_, // all have same base for ratio 311 paymentDate, 312 detail::get(notionals_, i, 0.0), 313 start, end, 314 detail::get(fixingDays_, i, 0.0), 315 index_, observationLag_, 316 observationInterpolation_, 317 paymentDayCounter_, 318 detail::get(fixedRates_, i, 0.0), 319 detail::get(spreads_, i, 0.0), 320 refStart, refEnd, exCouponDate)); 321 322 // in this case you can set a pricer 323 // straight away because it only provides computation - not data 324 ext::shared_ptr<CPICouponPricer> pricer = 325 ext::make_shared<CPICouponPricer>(Handle<CPIVolatilitySurface>(), 326 Handle<YieldTermStructure>()); 327 coup->setPricer(pricer); 328 leg.push_back(ext::dynamic_pointer_cast<CashFlow>(coup)); 329 330 } else { // cap/floorlet 331 QL_FAIL("caps/floors on CPI coupons not implemented."); 332 } 333 } 334 } 335 } 336 337 // in CPI legs you always have a notional flow of some sort 338 Date paymentDate = paymentCalendar_.adjust(schedule_.date(n), paymentAdjustment_); 339 Date fixingDate = paymentDate - observationLag_; 340 ext::shared_ptr<CashFlow> xnl(new CPICashFlow 341 (detail::get(notionals_, n, 0.0), index_, 342 Date(), // is fake, i.e. you do not have one 343 baseCPI_, fixingDate, paymentDate, 344 subtractInflationNominal_, observationInterpolation_, 345 index_->frequency()) 346 ); 347 leg.push_back(xnl); 348 349 350 return leg; 351 } 352 353 354 355 356 357 358 } // namespace RiskLib 359 360