1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 3 /* 4 Copyright (C) 2007 Giorgio Facchinetti 5 Copyright (C) 2007 Cristina Duminuco 6 Copyright (C) 2010, 2011 Ferdinando Ametrano 7 Copyright (C) 2017 Joseph Jeisman 8 Copyright (C) 2017 Fabrice Lecuyer 9 10 This file is part of QuantLib, a free-software/open-source library 11 for financial quantitative analysts and developers - http://quantlib.org/ 12 13 QuantLib is free software: you can redistribute it and/or modify it 14 under the terms of the QuantLib license. You should have received a 15 copy of the license along with this program; if not, please email 16 <quantlib-dev@lists.sf.net>. The license is also available online at 17 <http://quantlib.org/license.shtml>. 18 19 This program is distributed in the hope that it will be useful, but WITHOUT 20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 21 FOR A PARTICULAR PURPOSE. See the license for more details. 22 */ 23 24 #include <ql/cashflows/iborcoupon.hpp> 25 #include <ql/cashflows/couponpricer.hpp> 26 #include <ql/cashflows/capflooredcoupon.hpp> 27 #include <ql/cashflows/cashflowvectors.hpp> 28 #include <ql/indexes/interestrateindex.hpp> 29 #include <ql/termstructures/yieldtermstructure.hpp> 30 31 namespace QuantLib { 32 33 bool IborCoupon::constructorWasNotCalled_ = true; 34 35 #ifndef QL_USE_INDEXED_COUPON 36 bool IborCoupon::usingAtParCoupons_ = true; 37 #else 38 bool IborCoupon::usingAtParCoupons_ = false; 39 #endif 40 createAtParCoupons()41 void IborCoupon::createAtParCoupons() { 42 QL_ASSERT(constructorWasNotCalled_, 43 "Cannot call this method after the first IborCoupon was created."); 44 usingAtParCoupons_ = true; 45 } 46 createIndexedCoupons()47 void IborCoupon::createIndexedCoupons() { 48 QL_ASSERT(constructorWasNotCalled_, 49 "Cannot call this method after the first IborCoupon was created."); 50 usingAtParCoupons_ = false; 51 } 52 IborCoupon(const Date & paymentDate,Real nominal,const Date & startDate,const Date & endDate,Natural fixingDays,const ext::shared_ptr<IborIndex> & iborIndex,Real gearing,Spread spread,const Date & refPeriodStart,const Date & refPeriodEnd,const DayCounter & dayCounter,bool isInArrears,const Date & exCouponDate)53 IborCoupon::IborCoupon(const Date& paymentDate, 54 Real nominal, 55 const Date& startDate, 56 const Date& endDate, 57 Natural fixingDays, 58 const ext::shared_ptr<IborIndex>& iborIndex, 59 Real gearing, 60 Spread spread, 61 const Date& refPeriodStart, 62 const Date& refPeriodEnd, 63 const DayCounter& dayCounter, 64 bool isInArrears, 65 const Date& exCouponDate) 66 : FloatingRateCoupon(paymentDate, nominal, startDate, endDate, 67 fixingDays, iborIndex, gearing, spread, 68 refPeriodStart, refPeriodEnd, 69 dayCounter, isInArrears, exCouponDate), 70 iborIndex_(iborIndex) { 71 constructorWasNotCalled_ = false; 72 73 fixingDate_ = fixingDate(); 74 75 const Calendar& fixingCalendar = index_->fixingCalendar(); 76 Natural indexFixingDays = index_->fixingDays(); 77 78 fixingValueDate_ = fixingCalendar.advance( 79 fixingDate_, indexFixingDays, Days); 80 81 if (usingAtParCoupons_) { 82 if (isInArrears_) 83 fixingEndDate_ = index_->maturityDate(fixingValueDate_); 84 else { // par coupon approximation 85 Date nextFixingDate = fixingCalendar.advance( 86 accrualEndDate_, -static_cast<Integer>(fixingDays_), Days); 87 fixingEndDate_ = fixingCalendar.advance( 88 nextFixingDate, indexFixingDays, Days); 89 // make sure the estimation period contains at least one day 90 fixingEndDate_ = std::max(fixingEndDate_, fixingValueDate_ + 1); 91 } 92 } else { 93 fixingEndDate_ = index_->maturityDate(fixingValueDate_); 94 } 95 96 const DayCounter& dc = index_->dayCounter(); 97 spanningTime_ = dc.yearFraction(fixingValueDate_, 98 fixingEndDate_); 99 QL_REQUIRE(spanningTime_>0.0, 100 "\n cannot calculate forward rate between " << 101 fixingValueDate_ << " and " << fixingEndDate_ << 102 ":\n non positive time (" << spanningTime_ << 103 ") using " << dc.name() << " daycounter"); 104 } 105 indexFixing() const106 Rate IborCoupon::indexFixing() const { 107 108 /* instead of just returning index_->fixing(fixingValueDate_) 109 its logic is duplicated here using a specialized iborIndex 110 forecastFixing overload which 111 1) allows to save date/time recalculations, and 112 2) takes into account par coupon needs 113 */ 114 Date today = Settings::instance().evaluationDate(); 115 116 if (fixingDate_>today) 117 return iborIndex_->forecastFixing(fixingValueDate_, 118 fixingEndDate_, 119 spanningTime_); 120 121 if (fixingDate_<today || 122 Settings::instance().enforcesTodaysHistoricFixings()) { 123 // do not catch exceptions 124 Rate result = index_->pastFixing(fixingDate_); 125 QL_REQUIRE(result != Null<Real>(), 126 "Missing " << index_->name() << " fixing for " << fixingDate_); 127 return result; 128 } 129 130 try { 131 Rate result = index_->pastFixing(fixingDate_); 132 if (result!=Null<Real>()) 133 return result; 134 else 135 ; // fall through and forecast 136 } catch (Error&) { 137 ; // fall through and forecast 138 } 139 return iborIndex_->forecastFixing(fixingValueDate_, 140 fixingEndDate_, 141 spanningTime_); 142 } 143 accept(AcyclicVisitor & v)144 void IborCoupon::accept(AcyclicVisitor& v) { 145 Visitor<IborCoupon>* v1 = 146 dynamic_cast<Visitor<IborCoupon>*>(&v); 147 if (v1 != 0) 148 v1->visit(*this); 149 else 150 FloatingRateCoupon::accept(v); 151 } 152 153 154 IborLeg(const Schedule & schedule,const ext::shared_ptr<IborIndex> & index)155 IborLeg::IborLeg(const Schedule& schedule, 156 const ext::shared_ptr<IborIndex>& index) 157 : schedule_(schedule), index_(index), 158 paymentAdjustment_(Following), 159 paymentLag_(0), paymentCalendar_(Calendar()), 160 inArrears_(false), zeroPayments_(false), 161 exCouponPeriod_(Period()), exCouponCalendar_(Calendar()), 162 exCouponAdjustment_(Unadjusted), exCouponEndOfMonth_(false) {} 163 withNotionals(Real notional)164 IborLeg& IborLeg::withNotionals(Real notional) { 165 notionals_ = std::vector<Real>(1,notional); 166 return *this; 167 } 168 withNotionals(const std::vector<Real> & notionals)169 IborLeg& IborLeg::withNotionals(const std::vector<Real>& notionals) { 170 notionals_ = notionals; 171 return *this; 172 } 173 withPaymentDayCounter(const DayCounter & dayCounter)174 IborLeg& IborLeg::withPaymentDayCounter(const DayCounter& dayCounter) { 175 paymentDayCounter_ = dayCounter; 176 return *this; 177 } 178 withPaymentAdjustment(BusinessDayConvention convention)179 IborLeg& IborLeg::withPaymentAdjustment(BusinessDayConvention convention) { 180 paymentAdjustment_ = convention; 181 return *this; 182 } 183 withPaymentLag(Natural lag)184 IborLeg& IborLeg::withPaymentLag(Natural lag) { 185 paymentLag_ = lag; 186 return *this; 187 } 188 withPaymentCalendar(const Calendar & cal)189 IborLeg& IborLeg::withPaymentCalendar(const Calendar& cal) { 190 paymentCalendar_ = cal; 191 return *this; 192 } 193 withFixingDays(Natural fixingDays)194 IborLeg& IborLeg::withFixingDays(Natural fixingDays) { 195 fixingDays_ = std::vector<Natural>(1,fixingDays); 196 return *this; 197 } 198 withFixingDays(const std::vector<Natural> & fixingDays)199 IborLeg& IborLeg::withFixingDays(const std::vector<Natural>& fixingDays) { 200 fixingDays_ = fixingDays; 201 return *this; 202 } 203 withGearings(Real gearing)204 IborLeg& IborLeg::withGearings(Real gearing) { 205 gearings_ = std::vector<Real>(1,gearing); 206 return *this; 207 } 208 withGearings(const std::vector<Real> & gearings)209 IborLeg& IborLeg::withGearings(const std::vector<Real>& gearings) { 210 gearings_ = gearings; 211 return *this; 212 } 213 withSpreads(Spread spread)214 IborLeg& IborLeg::withSpreads(Spread spread) { 215 spreads_ = std::vector<Spread>(1,spread); 216 return *this; 217 } 218 withSpreads(const std::vector<Spread> & spreads)219 IborLeg& IborLeg::withSpreads(const std::vector<Spread>& spreads) { 220 spreads_ = spreads; 221 return *this; 222 } 223 withCaps(Rate cap)224 IborLeg& IborLeg::withCaps(Rate cap) { 225 caps_ = std::vector<Rate>(1,cap); 226 return *this; 227 } 228 withCaps(const std::vector<Rate> & caps)229 IborLeg& IborLeg::withCaps(const std::vector<Rate>& caps) { 230 caps_ = caps; 231 return *this; 232 } 233 withFloors(Rate floor)234 IborLeg& IborLeg::withFloors(Rate floor) { 235 floors_ = std::vector<Rate>(1,floor); 236 return *this; 237 } 238 withFloors(const std::vector<Rate> & floors)239 IborLeg& IborLeg::withFloors(const std::vector<Rate>& floors) { 240 floors_ = floors; 241 return *this; 242 } 243 inArrears(bool flag)244 IborLeg& IborLeg::inArrears(bool flag) { 245 inArrears_ = flag; 246 return *this; 247 } 248 withZeroPayments(bool flag)249 IborLeg& IborLeg::withZeroPayments(bool flag) { 250 zeroPayments_ = flag; 251 return *this; 252 } 253 withExCouponPeriod(const Period & period,const Calendar & cal,BusinessDayConvention convention,bool endOfMonth)254 IborLeg& IborLeg::withExCouponPeriod(const Period& period, 255 const Calendar& cal, 256 BusinessDayConvention convention, 257 bool endOfMonth) { 258 exCouponPeriod_ = period; 259 exCouponCalendar_ = cal; 260 exCouponAdjustment_ = convention; 261 exCouponEndOfMonth_ = endOfMonth; 262 return *this; 263 } 264 operator Leg() const265 IborLeg::operator Leg() const { 266 267 Leg leg = FloatingLeg<IborIndex, IborCoupon, CappedFlooredIborCoupon>( 268 schedule_, notionals_, index_, paymentDayCounter_, 269 paymentAdjustment_, fixingDays_, gearings_, spreads_, 270 caps_, floors_, inArrears_, zeroPayments_, paymentLag_, paymentCalendar_, 271 exCouponPeriod_, exCouponCalendar_, exCouponAdjustment_, exCouponEndOfMonth_); 272 273 if (caps_.empty() && floors_.empty() && !inArrears_) { 274 ext::shared_ptr<IborCouponPricer> pricer(new BlackIborCouponPricer); 275 setCouponPricer(leg, pricer); 276 } 277 278 return leg; 279 } 280 281 } 282