1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 3 /* 4 Copyright (C) 2004 Jeff Yu 5 Copyright (C) 2004 M-Dimension Consulting Inc. 6 Copyright (C) 2005, 2006, 2007, 2008, 2010 StatPro Italia srl 7 Copyright (C) 2007, 2008, 2009 Ferdinando Ametrano 8 Copyright (C) 2007 Chiara Fornarola 9 Copyright (C) 2008 Simon Ibbotson 10 11 This file is part of QuantLib, a free-software/open-source library 12 for financial quantitative analysts and developers - http://quantlib.org/ 13 14 QuantLib is free software: you can redistribute it and/or modify it 15 under the terms of the QuantLib license. You should have received a 16 copy of the license along with this program; if not, please email 17 <quantlib-dev@lists.sf.net>. The license is also available online at 18 <http://quantlib.org/license.shtml>. 19 20 This program is distributed in the hope that it will be useful, but WITHOUT 21 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 22 FOR A PARTICULAR PURPOSE. See the license for more details. 23 */ 24 25 #include <ql/instruments/bond.hpp> 26 #include <ql/cashflows/cashflows.hpp> 27 #include <ql/cashflows/floatingratecoupon.hpp> 28 #include <ql/math/solvers1d/brent.hpp> 29 #include <ql/cashflows/simplecashflow.hpp> 30 #include <ql/pricingengines/bond/discountingbondengine.hpp> 31 #include <ql/pricingengines/bond/bondfunctions.hpp> 32 33 namespace QuantLib { 34 Bond(Natural settlementDays,const Calendar & calendar,const Date & issueDate,const Leg & coupons)35 Bond::Bond(Natural settlementDays, 36 const Calendar& calendar, 37 const Date& issueDate, 38 const Leg& coupons) 39 : settlementDays_(settlementDays), calendar_(calendar), 40 cashflows_(coupons), issueDate_(issueDate) { 41 42 if (!coupons.empty()) { 43 std::sort(cashflows_.begin(), cashflows_.end(), 44 earlier_than<ext::shared_ptr<CashFlow> >()); 45 46 if (issueDate_ != Date()) { 47 QL_REQUIRE(issueDate_<cashflows_[0]->date(), 48 "issue date (" << issueDate_ << 49 ") must be earlier than first payment date (" << 50 cashflows_[0]->date() << ")"); 51 } 52 53 maturityDate_ = coupons.back()->date(); 54 55 addRedemptionsToCashflows(); 56 } 57 58 registerWith(Settings::instance().evaluationDate()); 59 for (Leg::const_iterator c = cashflows_.begin(); c != cashflows_.end(); 60 ++c) 61 registerWith(*c); 62 } 63 Bond(Natural settlementDays,const Calendar & calendar,Real faceAmount,const Date & maturityDate,const Date & issueDate,const Leg & cashflows)64 Bond::Bond(Natural settlementDays, 65 const Calendar& calendar, 66 Real faceAmount, 67 const Date& maturityDate, 68 const Date& issueDate, 69 const Leg& cashflows) 70 : settlementDays_(settlementDays), calendar_(calendar), 71 cashflows_(cashflows), maturityDate_(maturityDate), 72 issueDate_(issueDate) { 73 74 if (!cashflows.empty()) { 75 76 std::sort(cashflows_.begin(), cashflows_.end()-1, 77 earlier_than<ext::shared_ptr<CashFlow> >()); 78 79 if (maturityDate_ == Date()) 80 maturityDate_ = CashFlows::maturityDate(cashflows); 81 82 if (issueDate_ != Date()) { 83 QL_REQUIRE(issueDate_<cashflows_[0]->date(), 84 "issue date (" << issueDate_ << 85 ") must be earlier than first payment date (" << 86 cashflows_[0]->date() << ")"); 87 } 88 89 notionals_.resize(2); 90 notionalSchedule_.resize(2); 91 92 notionalSchedule_[0] = Date(); 93 notionals_[0] = faceAmount; 94 95 notionalSchedule_[1] = maturityDate_; 96 notionals_[1] = 0.0; 97 98 redemptions_.push_back(cashflows.back()); 99 } 100 101 registerWith(Settings::instance().evaluationDate()); 102 for (Leg::const_iterator c = cashflows_.begin(); c != cashflows_.end(); 103 ++c) 104 registerWith(*c); 105 } 106 isExpired() const107 bool Bond::isExpired() const { 108 // this is the Instrument interface, so it doesn't use 109 // BondFunctions, and includeSettlementDateFlows is true 110 // (unless QL_TODAY_PAYMENTS will set it to false later on) 111 return CashFlows::isExpired(cashflows_, 112 true, 113 Settings::instance().evaluationDate()); 114 } 115 notional(Date d) const116 Real Bond::notional(Date d) const { 117 if (d == Date()) 118 d = settlementDate(); 119 120 if (d > notionalSchedule_.back()) { 121 // after maturity 122 return 0.0; 123 } 124 125 // After the check above, d is between the schedule 126 // boundaries. We search starting from the second notional 127 // date, since the first is null. After the call to 128 // lower_bound, *i is the earliest date which is greater or 129 // equal than d. Its index is greater or equal to 1. 130 std::vector<Date>::const_iterator i = 131 std::lower_bound(notionalSchedule_.begin()+1, 132 notionalSchedule_.end(), d); 133 Size index = std::distance(notionalSchedule_.begin(), i); 134 135 if (d < notionalSchedule_[index]) { 136 // no doubt about what to return 137 return notionals_[index-1]; 138 } else { 139 // d is equal to a redemption date. 140 // As per bond conventions, the payment has occurred; 141 // the bond already changed notional. 142 return notionals_[index]; 143 } 144 } 145 redemption() const146 const ext::shared_ptr<CashFlow>& Bond::redemption() const { 147 QL_REQUIRE(redemptions_.size() == 1, 148 "multiple redemption cash flows given"); 149 return redemptions_.back(); 150 } 151 startDate() const152 Date Bond::startDate() const { 153 return BondFunctions::startDate(*this); 154 } 155 maturityDate() const156 Date Bond::maturityDate() const { 157 if (maturityDate_!=Null<Date>()) 158 return maturityDate_; 159 else 160 return BondFunctions::maturityDate(*this); 161 } 162 isTradable(Date d) const163 bool Bond::isTradable(Date d) const { 164 return BondFunctions::isTradable(*this, d); 165 } 166 settlementDate(Date d) const167 Date Bond::settlementDate(Date d) const { 168 if (d==Date()) 169 d = Settings::instance().evaluationDate(); 170 171 // usually, the settlement is at T+n... 172 Date settlement = calendar_.advance(d, settlementDays_, Days); 173 // ...but the bond won't be traded until the issue date (if given.) 174 if (issueDate_ == Date()) 175 return settlement; 176 else 177 return std::max(settlement, issueDate_); 178 } 179 cleanPrice() const180 Real Bond::cleanPrice() const { 181 return dirtyPrice() - accruedAmount(settlementDate()); 182 } 183 dirtyPrice() const184 Real Bond::dirtyPrice() const { 185 Real currentNotional = notional(settlementDate()); 186 if (currentNotional == 0.0) 187 return 0.0; 188 else 189 return settlementValue()*100.0/currentNotional; 190 } 191 settlementValue() const192 Real Bond::settlementValue() const { 193 calculate(); 194 QL_REQUIRE(settlementValue_ != Null<Real>(), 195 "settlement value not provided"); 196 return settlementValue_; 197 } 198 settlementValue(Real cleanPrice) const199 Real Bond::settlementValue(Real cleanPrice) const { 200 Real dirtyPrice = cleanPrice + accruedAmount(settlementDate()); 201 return dirtyPrice / 100.0 * notional(settlementDate()); 202 } 203 yield(const DayCounter & dc,Compounding comp,Frequency freq,Real accuracy,Size maxEvaluations,Real guess,Bond::Price::Type priceType) const204 Rate Bond::yield(const DayCounter& dc, 205 Compounding comp, 206 Frequency freq, 207 Real accuracy, 208 Size maxEvaluations, 209 Real guess, 210 Bond::Price::Type priceType) const { 211 Real currentNotional = notional(settlementDate()); 212 if (currentNotional == 0.0) 213 return 0.0; 214 215 Real price = priceType == Bond::Price::Clean ? cleanPrice() : dirtyPrice(); 216 217 return BondFunctions::yield(*this, price, dc, comp, freq, 218 settlementDate(), 219 accuracy, maxEvaluations, 220 guess, priceType); 221 } 222 cleanPrice(Rate y,const DayCounter & dc,Compounding comp,Frequency freq,Date settlement) const223 Real Bond::cleanPrice(Rate y, 224 const DayCounter& dc, 225 Compounding comp, 226 Frequency freq, 227 Date settlement) const { 228 return BondFunctions::cleanPrice(*this, y, dc, comp, freq, settlement); 229 } 230 dirtyPrice(Rate y,const DayCounter & dc,Compounding comp,Frequency freq,Date settlement) const231 Real Bond::dirtyPrice(Rate y, 232 const DayCounter& dc, 233 Compounding comp, 234 Frequency freq, 235 Date settlement) const { 236 Real currentNotional = notional(settlement); 237 if (currentNotional == 0.0) 238 return 0.0; 239 240 return BondFunctions::cleanPrice(*this, y, dc, comp, freq, settlement) 241 + accruedAmount(settlement); 242 } 243 yield(Real price,const DayCounter & dc,Compounding comp,Frequency freq,Date settlement,Real accuracy,Size maxEvaluations,Real guess,Bond::Price::Type priceType) const244 Rate Bond::yield(Real price, 245 const DayCounter& dc, 246 Compounding comp, 247 Frequency freq, 248 Date settlement, 249 Real accuracy, 250 Size maxEvaluations, 251 Real guess, 252 Bond::Price::Type priceType) const { 253 Real currentNotional = notional(settlement); 254 if (currentNotional == 0.0) 255 return 0.0; 256 257 return BondFunctions::yield(*this, price, dc, comp, freq, 258 settlement, accuracy, maxEvaluations, 259 guess, priceType); 260 } 261 accruedAmount(Date settlement) const262 Real Bond::accruedAmount(Date settlement) const { 263 Real currentNotional = notional(settlement); 264 if (currentNotional == 0.0) 265 return 0.0; 266 267 return BondFunctions::accruedAmount(*this, settlement); 268 } 269 nextCouponRate(Date settlement) const270 Rate Bond::nextCouponRate(Date settlement) const { 271 return BondFunctions::nextCouponRate(*this, settlement); 272 } 273 previousCouponRate(Date settlement) const274 Rate Bond::previousCouponRate(Date settlement) const { 275 return BondFunctions::previousCouponRate(*this, settlement); 276 } 277 nextCashFlowDate(Date settlement) const278 Date Bond::nextCashFlowDate(Date settlement) const { 279 return BondFunctions::nextCashFlowDate(*this, settlement); 280 } 281 previousCashFlowDate(Date settlement) const282 Date Bond::previousCashFlowDate(Date settlement) const { 283 return BondFunctions::previousCashFlowDate(*this, settlement); 284 } 285 setupExpired() const286 void Bond::setupExpired() const { 287 Instrument::setupExpired(); 288 settlementValue_ = 0.0; 289 } 290 setupArguments(PricingEngine::arguments * args) const291 void Bond::setupArguments(PricingEngine::arguments* args) const { 292 Bond::arguments* arguments = dynamic_cast<Bond::arguments*>(args); 293 QL_REQUIRE(arguments != 0, "wrong argument type"); 294 295 arguments->settlementDate = settlementDate(); 296 arguments->cashflows = cashflows_; 297 arguments->calendar = calendar_; 298 } 299 fetchResults(const PricingEngine::results * r) const300 void Bond::fetchResults(const PricingEngine::results* r) const { 301 302 Instrument::fetchResults(r); 303 304 const Bond::results* results = 305 dynamic_cast<const Bond::results*>(r); 306 QL_ENSURE(results != 0, "wrong result type"); 307 308 settlementValue_ = results->settlementValue; 309 } 310 addRedemptionsToCashflows(const std::vector<Real> & redemptions)311 void Bond::addRedemptionsToCashflows(const std::vector<Real>& redemptions) { 312 // First, we gather the notional information from the cashflows 313 calculateNotionalsFromCashflows(); 314 // Then, we create the redemptions based on the notional 315 // information and we add them to the cashflows vector after 316 // the coupons. 317 redemptions_.clear(); 318 for (Size i=1; i<notionalSchedule_.size(); ++i) { 319 Real R = i < redemptions.size() ? redemptions[i] : 320 !redemptions.empty() ? redemptions.back() : 321 100.0; 322 Real amount = (R/100.0)*(notionals_[i-1]-notionals_[i]); 323 ext::shared_ptr<CashFlow> payment; 324 if (i < notionalSchedule_.size()-1) 325 payment.reset(new AmortizingPayment(amount, 326 notionalSchedule_[i])); 327 else 328 payment.reset(new Redemption(amount, notionalSchedule_[i])); 329 cashflows_.push_back(payment); 330 redemptions_.push_back(payment); 331 } 332 // stable_sort now moves the redemptions to the right places 333 // while ensuring that they follow coupons with the same date. 334 std::stable_sort(cashflows_.begin(), cashflows_.end(), 335 earlier_than<ext::shared_ptr<CashFlow> >()); 336 } 337 setSingleRedemption(Real notional,Real redemption,const Date & date)338 void Bond::setSingleRedemption(Real notional, 339 Real redemption, 340 const Date& date) { 341 342 ext::shared_ptr<CashFlow> redemptionCashflow( 343 new Redemption(notional*redemption/100.0, date)); 344 setSingleRedemption(notional, redemptionCashflow); 345 } 346 setSingleRedemption(Real notional,const ext::shared_ptr<CashFlow> & redemption)347 void Bond::setSingleRedemption(Real notional, 348 const ext::shared_ptr<CashFlow>& redemption) { 349 notionals_.resize(2); 350 notionalSchedule_.resize(2); 351 redemptions_.clear(); 352 353 notionalSchedule_[0] = Date(); 354 notionals_[0] = notional; 355 356 notionalSchedule_[1] = redemption->date(); 357 notionals_[1] = 0.0; 358 359 cashflows_.push_back(redemption); 360 redemptions_.push_back(redemption); 361 } 362 deepUpdate()363 void Bond::deepUpdate() { 364 for (Size k = 0; k < cashflows_.size(); ++k) { 365 ext::shared_ptr<LazyObject> f = 366 ext::dynamic_pointer_cast<LazyObject>(cashflows_[k]); 367 if (f != 0) 368 f->update(); 369 } 370 update(); 371 } 372 calculateNotionalsFromCashflows()373 void Bond::calculateNotionalsFromCashflows() { 374 notionalSchedule_.clear(); 375 notionals_.clear(); 376 377 Date lastPaymentDate = Date(); 378 notionalSchedule_.push_back(Date()); 379 for (Size i=0; i<cashflows_.size(); ++i) { 380 ext::shared_ptr<Coupon> coupon = 381 ext::dynamic_pointer_cast<Coupon>(cashflows_[i]); 382 if (!coupon) 383 continue; 384 385 Real notional = coupon->nominal(); 386 // we add the notional only if it is the first one... 387 if (notionals_.empty()) { 388 notionals_.push_back(coupon->nominal()); 389 lastPaymentDate = coupon->date(); 390 } else if (!close(notional, notionals_.back())) { 391 // ...or if it has changed. 392 QL_REQUIRE(notional < notionals_.back(), 393 "increasing coupon notionals"); 394 notionals_.push_back(coupon->nominal()); 395 // in this case, we also add the last valid date for 396 // the previous one... 397 notionalSchedule_.push_back(lastPaymentDate); 398 // ...and store the candidate for this one. 399 lastPaymentDate = coupon->date(); 400 } else { 401 // otherwise, we just extend the valid range of dates 402 // for the current notional. 403 lastPaymentDate = coupon->date(); 404 } 405 } 406 QL_REQUIRE(!notionals_.empty(), "no coupons provided"); 407 notionals_.push_back(0.0); 408 notionalSchedule_.push_back(lastPaymentDate); 409 } 410 411 validate() const412 void Bond::arguments::validate() const { 413 QL_REQUIRE(settlementDate != Date(), "no settlement date provided"); 414 QL_REQUIRE(!cashflows.empty(), "no cash flow provided"); 415 for (Size i=0; i<cashflows.size(); ++i) 416 QL_REQUIRE(cashflows[i], "null cash flow provided"); 417 } 418 419 } 420