1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 3 /* 4 Copyright (C) 2006, 2007 Giorgio Facchinetti 5 Copyright (C) 2006, 2007 Mario Pucci 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 22 #include <ql/cashflows/rangeaccrual.hpp> 23 #include <ql/cashflows/cashflowvectors.hpp> 24 #include <ql/pricingengines/blackformula.hpp> 25 #include <ql/math/distributions/normaldistribution.hpp> 26 #include <ql/time/schedule.hpp> 27 #include <ql/indexes/iborindex.hpp> 28 #include <ql/termstructures/yieldtermstructure.hpp> 29 30 #include <cmath> 31 32 namespace QuantLib { 33 34 //===========================================================================// 35 // RangeAccrualFloatersCoupon // 36 //===========================================================================// 37 RangeAccrualFloatersCoupon(const Date & paymentDate,Real nominal,const ext::shared_ptr<IborIndex> & index,const Date & startDate,const Date & endDate,Natural fixingDays,const DayCounter & dayCounter,Real gearing,Rate spread,const Date & refPeriodStart,const Date & refPeriodEnd,const ext::shared_ptr<Schedule> & observationsSchedule,Real lowerTrigger,Real upperTrigger)38 RangeAccrualFloatersCoupon::RangeAccrualFloatersCoupon( 39 const Date& paymentDate, 40 Real nominal, 41 const ext::shared_ptr<IborIndex>& index, 42 const Date& startDate, // S 43 const Date& endDate, // T 44 Natural fixingDays, 45 const DayCounter& dayCounter, 46 Real gearing, 47 Rate spread, 48 const Date& refPeriodStart, 49 const Date& refPeriodEnd, 50 const ext::shared_ptr<Schedule>& observationsSchedule, 51 Real lowerTrigger, // l 52 Real upperTrigger // u 53 ) 54 : FloatingRateCoupon(paymentDate, nominal, startDate, endDate, 55 fixingDays, index, gearing, spread, 56 refPeriodStart, refPeriodEnd, dayCounter), 57 observationsSchedule_(observationsSchedule), 58 lowerTrigger_(lowerTrigger), 59 upperTrigger_(upperTrigger){ 60 61 QL_REQUIRE(lowerTrigger_<upperTrigger, 62 "lowerTrigger_>=upperTrigger"); 63 QL_REQUIRE(observationsSchedule_->startDate()==startDate, 64 "incompatible start date"); 65 QL_REQUIRE(observationsSchedule_->endDate()==endDate, 66 "incompatible end date"); 67 68 observationDates_ = observationsSchedule_->dates(); 69 observationDates_.pop_back(); //remove end date 70 observationDates_.erase(observationDates_.begin()); //remove start date 71 observationsNo_ = observationDates_.size(); 72 73 const Handle<YieldTermStructure>& rateCurve = 74 index->forwardingTermStructure(); 75 Date referenceDate = rateCurve->referenceDate(); 76 77 startTime_ = dayCounter.yearFraction(referenceDate, startDate); 78 endTime_ = dayCounter.yearFraction(referenceDate, endDate); 79 for(Size i=0;i<observationsNo_;i++) { 80 observationTimes_.push_back( 81 dayCounter.yearFraction(referenceDate, observationDates_[i])); 82 } 83 84 } 85 accept(AcyclicVisitor & v)86 void RangeAccrualFloatersCoupon::accept(AcyclicVisitor& v) { 87 Visitor<RangeAccrualFloatersCoupon>* v1 = 88 dynamic_cast<Visitor<RangeAccrualFloatersCoupon>*>(&v); 89 if (v1 != 0) 90 v1->visit(*this); 91 else 92 FloatingRateCoupon::accept(v); 93 } 94 priceWithoutOptionality(const Handle<YieldTermStructure> & discountingCurve) const95 Real RangeAccrualFloatersCoupon::priceWithoutOptionality( 96 const Handle<YieldTermStructure>& discountingCurve) const { 97 return accrualPeriod() * (gearing_*indexFixing()+spread_) * 98 nominal() * discountingCurve->discount(date()); 99 } 100 101 102 //=======================================================================// 103 // RangeAccrualPricer // 104 //=======================================================================// 105 initialize(const FloatingRateCoupon & coupon)106 void RangeAccrualPricer::initialize(const FloatingRateCoupon& coupon){ 107 coupon_ = dynamic_cast<const RangeAccrualFloatersCoupon*>(&coupon); 108 QL_REQUIRE(coupon_, "range-accrual coupon required"); 109 gearing_ = coupon_->gearing(); 110 spread_ = coupon_->spread(); 111 112 Date paymentDate = coupon_->date(); 113 114 ext::shared_ptr<IborIndex> index = 115 ext::dynamic_pointer_cast<IborIndex>(coupon_->index()); 116 const Handle<YieldTermStructure>& rateCurve = 117 index->forwardingTermStructure(); 118 discount_ = rateCurve->discount(paymentDate); 119 accrualFactor_ = coupon_->accrualPeriod(); 120 spreadLegValue_ = spread_ * accrualFactor_* discount_; 121 122 startTime_ = coupon_->startTime(); 123 endTime_ = coupon_->endTime(); 124 observationTimes_ = coupon_->observationTimes(); 125 lowerTrigger_ = coupon_->lowerTrigger(); 126 upperTrigger_ = coupon_->upperTrigger(); 127 observationsNo_ = coupon_->observationsNo(); 128 129 const std::vector<Date> &observationDates = 130 coupon_->observationsSchedule()->dates(); 131 QL_REQUIRE(observationDates.size()==observationsNo_+2, 132 "incompatible size of initialValues vector"); 133 initialValues_= std::vector<Real>(observationDates.size(),0.); 134 135 Calendar calendar = index->fixingCalendar(); 136 for(Size i=0; i<observationDates.size(); i++) { 137 initialValues_[i]=index->fixing( 138 calendar.advance(observationDates[i], 139 -static_cast<Integer>(coupon_->fixingDays()), 140 Days)); 141 } 142 143 } 144 swapletRate() const145 Real RangeAccrualPricer::swapletRate() const { 146 return swapletPrice()/(accrualFactor_*discount_); 147 } 148 capletPrice(Rate) const149 Real RangeAccrualPricer::capletPrice(Rate) const { 150 QL_FAIL("RangeAccrualPricer::capletPrice not implemented"); 151 } 152 capletRate(Rate) const153 Rate RangeAccrualPricer::capletRate(Rate) const { 154 QL_FAIL("RangeAccrualPricer::capletRate not implemented"); 155 } 156 floorletPrice(Rate) const157 Real RangeAccrualPricer::floorletPrice(Rate) const { 158 QL_FAIL("RangeAccrualPricer::floorletPrice not implemented"); 159 } 160 floorletRate(Rate) const161 Rate RangeAccrualPricer::floorletRate(Rate) const { 162 QL_FAIL("RangeAccrualPricer::floorletRate not implemented"); 163 } 164 165 //===========================================================================// 166 // RangeAccrualPricerByBgm // 167 //===========================================================================// RangeAccrualPricerByBgm(Real correlation,const ext::shared_ptr<SmileSection> & smilesOnExpiry,const ext::shared_ptr<SmileSection> & smilesOnPayment,bool withSmile,bool byCallSpread)168 RangeAccrualPricerByBgm::RangeAccrualPricerByBgm( 169 Real correlation, 170 const ext::shared_ptr<SmileSection>& smilesOnExpiry, 171 const ext::shared_ptr<SmileSection>& smilesOnPayment, 172 bool withSmile, 173 bool byCallSpread) 174 : correlation_(correlation), 175 withSmile_(withSmile), 176 byCallSpread_(byCallSpread), 177 smilesOnExpiry_(smilesOnExpiry), 178 smilesOnPayment_(smilesOnPayment), 179 eps_(1.0e-8) { 180 181 } swapletPrice() const182 Real RangeAccrualPricerByBgm::swapletPrice() const{ 183 184 Real result = 0.; 185 const Real deflator = discount_*initialValues_[0]; 186 for(Size i=0;i<observationsNo_;i++){ 187 Real digitalFloater = digitalRangePrice(lowerTrigger_, upperTrigger_,initialValues_[i+1], 188 observationTimes_[i], deflator); 189 result += digitalFloater; 190 } 191 return gearing_ *(result*accrualFactor_/observationsNo_)+ spreadLegValue_; 192 } 193 driftsOverPeriod(Real U,Real lambdaS,Real lambdaT,Real correlation) const194 std::vector<Real> RangeAccrualPricerByBgm::driftsOverPeriod(Real U, 195 Real lambdaS, 196 Real lambdaT, 197 Real correlation) const{ 198 std::vector<Real> result; 199 200 const Real p = (U-startTime_)/accrualFactor_; 201 const Real q = (endTime_-U)/accrualFactor_; 202 const Real L0T = initialValues_.back(); 203 204 const Real driftBeforeFixing = 205 p*accrualFactor_*L0T/(1.+L0T*accrualFactor_)*(p*lambdaT*lambdaT + q*lambdaS*lambdaT*correlation) + 206 q*lambdaS*lambdaS + p*lambdaS*lambdaT*correlation 207 -0.5*lambda(U,lambdaS,lambdaT)*lambda(U,lambdaS,lambdaT); 208 const Real driftAfterFixing = (p*accrualFactor_*L0T/(1.+L0T*accrualFactor_)-0.5)*lambdaT*lambdaT; 209 210 result.push_back(driftBeforeFixing); 211 result.push_back(driftAfterFixing); 212 213 return result; 214 } 215 lambdasOverPeriod(Real U,Real lambdaS,Real lambdaT) const216 std::vector<Real> RangeAccrualPricerByBgm::lambdasOverPeriod(Real U, 217 Real lambdaS, 218 Real lambdaT) const{ 219 std::vector<Real> result; 220 221 const Real p = (U-startTime_)/accrualFactor_; 222 const Real q = (endTime_-U)/accrualFactor_; 223 224 const Real lambdaBeforeFixing = q*lambdaS + p*lambdaT; 225 const Real lambdaAfterFixing = lambdaT; 226 227 result.push_back(lambdaBeforeFixing); 228 result.push_back(lambdaAfterFixing); 229 230 return result; 231 } drift(Real U,Real lambdaS,Real lambdaT,Real correlation) const232 Real RangeAccrualPricerByBgm::drift(Real U, 233 Real lambdaS, 234 Real lambdaT, 235 Real correlation) const{ 236 Real result; 237 238 const Real p = (U-startTime_)/accrualFactor_; 239 const Real q = (endTime_-U)/accrualFactor_; 240 const Real L0T = initialValues_.back(); 241 242 const Real driftBeforeFixing = 243 p*accrualFactor_*L0T/(1.+L0T*accrualFactor_)*(p*lambdaT*lambdaT + q*lambdaS*lambdaT*correlation) + 244 q*lambdaS*lambdaS + p*lambdaS*lambdaT*correlation; 245 const Real driftAfterFixing = (p*accrualFactor_*L0T/(1.+L0T*accrualFactor_)-0.5)*lambdaT*lambdaT; 246 247 if(startTime_ > 0){result = driftBeforeFixing;} 248 else {result = driftAfterFixing;} 249 250 return result; 251 } 252 lambda(Real U,Real lambdaS,Real lambdaT) const253 Real RangeAccrualPricerByBgm::lambda(Real U, 254 Real lambdaS, 255 Real lambdaT) const{ 256 Real result; 257 258 const Real p = (U-startTime_)/accrualFactor_; 259 const Real q = (endTime_-U)/accrualFactor_; 260 261 if(startTime_ > 0){result = q*lambdaS + p*lambdaT;} 262 else {result = lambdaT;} 263 264 return result; 265 } 266 267 derDriftDerLambdaS(Real U,Real lambdaS,Real lambdaT,Real correlation) const268 Real RangeAccrualPricerByBgm::derDriftDerLambdaS(Real U, 269 Real lambdaS, 270 Real lambdaT, 271 Real correlation) const{ 272 Real result; 273 274 const Real p = (U-startTime_)/accrualFactor_; 275 const Real q = (endTime_-U)/accrualFactor_; 276 const Real L0T = initialValues_.back(); 277 278 const Real driftBeforeFixing = 279 p*accrualFactor_*L0T/(1.+L0T*accrualFactor_)*(q*lambdaT*correlation) + 280 2*q*lambdaS + p*lambdaT*correlation; 281 const Real driftAfterFixing = 0.; 282 283 if(startTime_ > 0){result = driftBeforeFixing;} 284 else {result = driftAfterFixing;} 285 286 return result; 287 } 288 derLambdaDerLambdaS(Real U) const289 Real RangeAccrualPricerByBgm::derLambdaDerLambdaS(Real U) const { 290 291 if (startTime_>0) { 292 Real q = (endTime_-U)/accrualFactor_; 293 return q; 294 } else 295 return 0.0; 296 297 } 298 derDriftDerLambdaT(Real U,Real lambdaS,Real lambdaT,Real correlation) const299 Real RangeAccrualPricerByBgm::derDriftDerLambdaT(Real U, 300 Real lambdaS, 301 Real lambdaT, 302 Real correlation) const{ 303 Real result; 304 305 const Real p = (U-startTime_)/accrualFactor_; 306 const Real q = (endTime_-U)/accrualFactor_; 307 const Real L0T = initialValues_.back(); 308 309 const Real driftBeforeFixing = 310 p*accrualFactor_*L0T/(1.+L0T*accrualFactor_)*(2*p*lambdaT + q*lambdaS*correlation) + 311 + p*lambdaS*correlation; 312 const Real driftAfterFixing = (p*accrualFactor_*L0T/(1.+L0T*accrualFactor_)-0.5)*2*lambdaT; 313 314 if(startTime_ > 0){result = driftBeforeFixing;} 315 else {result = driftAfterFixing;} 316 317 return result; 318 } 319 derLambdaDerLambdaT(Real U) const320 Real RangeAccrualPricerByBgm::derLambdaDerLambdaT(Real U) const { 321 322 if (startTime_>0) { 323 Real p = (U-startTime_)/accrualFactor_; 324 return p; 325 } else 326 return 0.0; 327 328 } 329 digitalRangePrice(Real lowerTrigger,Real upperTrigger,Real initialValue,Real expiry,Real deflator) const330 Real RangeAccrualPricerByBgm::digitalRangePrice(Real lowerTrigger, 331 Real upperTrigger, 332 Real initialValue, 333 Real expiry, 334 Real deflator) const{ 335 const Real lowerPrice = digitalPrice(lowerTrigger, initialValue, expiry, deflator); 336 const Real upperPrice = digitalPrice(upperTrigger, initialValue, expiry, deflator); 337 const Real result = lowerPrice - upperPrice; 338 QL_REQUIRE(result >=0., 339 "RangeAccrualPricerByBgm::digitalRangePrice:\n digitalPrice("<<upperTrigger<< 340 "): "<<upperPrice<<" > digitalPrice("<<lowerTrigger<<"): "<<lowerPrice); 341 return result; 342 343 } digitalPrice(Real strike,Real initialValue,Real expiry,Real deflator) const344 Real RangeAccrualPricerByBgm::digitalPrice(Real strike, 345 Real initialValue, 346 Real expiry, 347 Real deflator) const { 348 Real result = deflator; 349 if(strike>eps_/2){ 350 if(withSmile_) 351 result = digitalPriceWithSmile(strike, initialValue, expiry, deflator); 352 else 353 result = digitalPriceWithoutSmile(strike, initialValue, expiry, deflator); 354 } 355 return result; 356 } 357 digitalPriceWithoutSmile(Real strike,Real initialValue,Real expiry,Real deflator) const358 Real RangeAccrualPricerByBgm::digitalPriceWithoutSmile(Real strike, 359 Real initialValue, 360 Real expiry, 361 Real deflator) const { 362 363 Real lambdaS = smilesOnExpiry_->volatility(strike); 364 Real lambdaT = smilesOnPayment_->volatility(strike); 365 366 std::vector<Real> lambdaU = lambdasOverPeriod(expiry, lambdaS, lambdaT); 367 const Real variance = 368 startTime_*lambdaU[0]*lambdaU[0]+(expiry-startTime_)*lambdaU[1]*lambdaU[1]; 369 370 Real lambdaSATM = smilesOnExpiry_->volatility(initialValue); 371 Real lambdaTATM = smilesOnPayment_->volatility(initialValue); 372 //drift of Lognormal process (of Libor) "a_U()" nel paper 373 std::vector<Real> muU = driftsOverPeriod(expiry, lambdaSATM, lambdaTATM, correlation_); 374 const Real adjustment = (startTime_*muU[0]+(expiry-startTime_)*muU[1]); 375 376 377 Real d2 = (std::log(initialValue/strike) + adjustment - 0.5*variance)/std::sqrt(variance); 378 379 CumulativeNormalDistribution phi; 380 const Real result = deflator*phi(d2); 381 382 QL_REQUIRE(result > 0., 383 "RangeAccrualPricerByBgm::digitalPriceWithoutSmile: result< 0. Result:"<<result); 384 QL_REQUIRE(result/deflator <= 1., 385 "RangeAccrualPricerByBgm::digitalPriceWithoutSmile: result/deflator > 1. Ratio: " 386 << result/deflator << " result: " << result<< " deflator: " << deflator); 387 388 return result; 389 } 390 digitalPriceWithSmile(Real strike,Real initialValue,Real expiry,Real deflator) const391 Real RangeAccrualPricerByBgm::digitalPriceWithSmile(Real strike, 392 Real initialValue, 393 Real expiry, 394 Real deflator) const { 395 Real result; 396 if (byCallSpread_) { 397 398 // Previous strike 399 const Real previousStrike = strike - eps_/2; 400 Real lambdaS = smilesOnExpiry_->volatility(previousStrike); 401 Real lambdaT = smilesOnPayment_->volatility(previousStrike); 402 403 //drift of Lognormal process (of Libor) "a_U()" nel paper 404 std::vector<Real> lambdaU = lambdasOverPeriod(expiry, lambdaS, lambdaT); 405 const Real previousVariance = std::max(startTime_, 0.)*lambdaU[0]*lambdaU[0]+ 406 std::min(expiry-startTime_, expiry)*lambdaU[1]*lambdaU[1]; 407 408 Real lambdaSATM = smilesOnExpiry_->volatility(initialValue); 409 Real lambdaTATM = smilesOnPayment_->volatility(initialValue); 410 std::vector<Real> muU = driftsOverPeriod(expiry, lambdaSATM, lambdaTATM, correlation_); 411 const Real previousAdjustment = std::exp(std::max(startTime_, 0.)*muU[0] + 412 std::min(expiry-startTime_, expiry)*muU[1]); 413 const Real previousForward = initialValue * previousAdjustment ; 414 415 // Next strike 416 const Real nextStrike = strike + eps_/2; 417 lambdaS = smilesOnExpiry_->volatility(nextStrike); 418 lambdaT = smilesOnPayment_->volatility(nextStrike); 419 420 lambdaU = lambdasOverPeriod(expiry, lambdaS, lambdaT); 421 const Real nextVariance = std::max(startTime_, 0.)*lambdaU[0]*lambdaU[0]+ 422 std::min(expiry-startTime_, expiry)*lambdaU[1]*lambdaU[1]; 423 //drift of Lognormal process (of Libor) "a_U()" nel paper 424 muU = driftsOverPeriod(expiry, lambdaSATM, lambdaTATM, correlation_); 425 const Real nextAdjustment = std::exp(std::max(startTime_, 0.)*muU[0] + 426 std::min(expiry-startTime_, expiry)*muU[1]); 427 const Real nextForward = initialValue * nextAdjustment ; 428 429 result = callSpreadPrice(previousForward,nextForward,previousStrike, nextStrike, 430 deflator, previousVariance, nextVariance); 431 432 } 433 else{ 434 result = digitalPriceWithoutSmile(strike, initialValue, expiry, deflator)+ 435 smileCorrection(strike, initialValue, expiry, deflator); 436 } 437 438 QL_REQUIRE(result > -std::pow(eps_,.5), 439 "RangeAccrualPricerByBgm::digitalPriceWithSmile: result< 0 Result:"<<result); 440 QL_REQUIRE(result/deflator <= 1.0 + std::pow(eps_,.2), 441 "RangeAccrualPricerByBgm::digitalPriceWithSmile: result/deflator > 1. Ratio: " 442 << result/deflator << " result: " << result<< " deflator: " << deflator); 443 444 return result; 445 } 446 smileCorrection(Real strike,Real forward,Real expiry,Real deflator) const447 Real RangeAccrualPricerByBgm::smileCorrection(Real strike, 448 Real forward, 449 Real expiry, 450 Real deflator) const { 451 452 const Real previousStrike = strike - eps_/2; 453 const Real nextStrike = strike + eps_/2; 454 455 const Real derSmileS = (smilesOnExpiry_->volatility(nextStrike)- 456 smilesOnExpiry_->volatility(previousStrike))/eps_; 457 const Real derSmileT = (smilesOnPayment_->volatility(nextStrike)- 458 smilesOnPayment_->volatility(previousStrike))/eps_; 459 460 Real lambdaS = smilesOnExpiry_->volatility(strike); 461 Real lambdaT = smilesOnPayment_->volatility(strike); 462 //Real lambdaU = lambda(expiry, lambdaS, lambdaT); 463 464 Real derLambdaDerK = derLambdaDerLambdaS(expiry) * derSmileS + 465 derLambdaDerLambdaT(expiry) * derSmileT; 466 //Real derDriftDerK = derDriftDerLambdaS(expiry, lambdaS, lambdaT, correlation_)*derSmileS + 467 // derDriftDerLambdaT(expiry, lambdaS, lambdaT, correlation_)*derSmileT + 468 // lambdaU * derLambdaDerK; 469 470 Real lambdaSATM = smilesOnExpiry_->volatility(forward); 471 Real lambdaTATM = smilesOnPayment_->volatility(forward); 472 std::vector<Real> lambdasOverPeriodU = lambdasOverPeriod(expiry, lambdaS, lambdaT); 473 //drift of Lognormal process (of Libor) "a_U()" nel paper 474 std::vector<Real> muU = driftsOverPeriod(expiry, lambdaSATM, lambdaTATM, correlation_); 475 476 const Real variance = std::max(startTime_, 0.)*lambdasOverPeriodU[0]*lambdasOverPeriodU[0] + 477 std::min(expiry-startTime_, expiry)*lambdasOverPeriodU[1]*lambdasOverPeriodU[1]; 478 479 const Real forwardAdjustment = std::exp(std::max(startTime_, 0.)*muU[0] + 480 std::min(expiry-startTime_, expiry)*muU[1]); 481 const Real forwardAdjusted = forward * forwardAdjustment; 482 483 const Real d1 = (std::log(forwardAdjusted/strike)+0.5*variance)/std::sqrt(variance); 484 485 const Real sqrtOfTimeToExpiry = (std::max(startTime_, 0.)*lambdasOverPeriodU[0] + 486 std::min(expiry-startTime_, expiry)*lambdasOverPeriodU[1])* 487 (1./std::sqrt(variance)); 488 489 CumulativeNormalDistribution phi; 490 NormalDistribution psi; 491 Real result = - forwardAdjusted*psi(d1)*sqrtOfTimeToExpiry*derLambdaDerK ; 492 // - forwardAdjusted*phi(d1)*expiry*derDriftDerK; 493 494 result *= deflator; 495 496 QL_REQUIRE(std::fabs(result/deflator) <= 1.0 + std::pow(eps_,.2), 497 "RangeAccrualPricerByBgm::smileCorrection: abs(result/deflator) > 1. Ratio: " 498 << result/deflator << " result: " << result<< " deflator: " << deflator); 499 500 return result; 501 } 502 callSpreadPrice(Real previousForward,Real nextForward,Real previousStrike,Real nextStrike,Real deflator,Real previousVariance,Real nextVariance) const503 Real RangeAccrualPricerByBgm::callSpreadPrice( 504 Real previousForward, 505 Real nextForward, 506 Real previousStrike, 507 Real nextStrike, 508 Real deflator, 509 Real previousVariance, 510 Real nextVariance) const{ 511 const Real nextCall = 512 blackFormula(Option::Call, nextStrike, nextForward, std::sqrt(nextVariance), deflator); 513 const Real previousCall = 514 blackFormula(Option::Call, previousStrike, previousForward, std::sqrt(previousVariance), deflator); 515 516 QL_ENSURE(nextCall <previousCall,"RangeAccrualPricerByBgm::callSpreadPrice: nextCall > previousCall" 517 "\n nextCall: strike :" << nextStrike << "; variance: " << nextVariance << 518 " adjusted initial value " << nextForward << 519 "\n previousCall: strike :" << previousStrike << "; variance: " << previousVariance << 520 " adjusted initial value " << previousForward ); 521 522 const Real result = (previousCall-nextCall)/(nextStrike-previousStrike); 523 524 return result; 525 } 526 527 528 529 RangeAccrualLeg(const Schedule & schedule,const ext::shared_ptr<IborIndex> & index)530 RangeAccrualLeg::RangeAccrualLeg( 531 const Schedule& schedule, 532 const ext::shared_ptr<IborIndex>& index) 533 : schedule_(schedule), index_(index), 534 paymentAdjustment_(Following), 535 observationConvention_(ModifiedFollowing) {} 536 withNotionals(Real notional)537 RangeAccrualLeg& RangeAccrualLeg::withNotionals(Real notional) { 538 notionals_ = std::vector<Real>(1,notional); 539 return *this; 540 } 541 withNotionals(const std::vector<Real> & notionals)542 RangeAccrualLeg& RangeAccrualLeg::withNotionals( 543 const std::vector<Real>& notionals) { 544 notionals_ = notionals; 545 return *this; 546 } 547 withPaymentDayCounter(const DayCounter & dayCounter)548 RangeAccrualLeg& RangeAccrualLeg::withPaymentDayCounter( 549 const DayCounter& dayCounter) { 550 paymentDayCounter_ = dayCounter; 551 return *this; 552 } 553 withPaymentAdjustment(BusinessDayConvention convention)554 RangeAccrualLeg& RangeAccrualLeg::withPaymentAdjustment( 555 BusinessDayConvention convention) { 556 paymentAdjustment_ = convention; 557 return *this; 558 } 559 withFixingDays(Natural fixingDays)560 RangeAccrualLeg& RangeAccrualLeg::withFixingDays(Natural fixingDays) { 561 fixingDays_ = std::vector<Natural>(1,fixingDays); 562 return *this; 563 } 564 withFixingDays(const std::vector<Natural> & fixingDays)565 RangeAccrualLeg& RangeAccrualLeg::withFixingDays( 566 const std::vector<Natural>& fixingDays) { 567 fixingDays_ = fixingDays; 568 return *this; 569 } 570 withGearings(Real gearing)571 RangeAccrualLeg& RangeAccrualLeg::withGearings(Real gearing) { 572 gearings_ = std::vector<Real>(1,gearing); 573 return *this; 574 } 575 withGearings(const std::vector<Real> & gearings)576 RangeAccrualLeg& RangeAccrualLeg::withGearings( 577 const std::vector<Real>& gearings) { 578 gearings_ = gearings; 579 return *this; 580 } 581 withSpreads(Spread spread)582 RangeAccrualLeg& RangeAccrualLeg::withSpreads(Spread spread) { 583 spreads_ = std::vector<Spread>(1,spread); 584 return *this; 585 } 586 withSpreads(const std::vector<Spread> & spreads)587 RangeAccrualLeg& RangeAccrualLeg::withSpreads( 588 const std::vector<Spread>& spreads) { 589 spreads_ = spreads; 590 return *this; 591 } 592 withLowerTriggers(Rate trigger)593 RangeAccrualLeg& RangeAccrualLeg::withLowerTriggers(Rate trigger) { 594 lowerTriggers_ = std::vector<Rate>(1,trigger); 595 return *this; 596 } 597 withLowerTriggers(const std::vector<Rate> & triggers)598 RangeAccrualLeg& RangeAccrualLeg::withLowerTriggers( 599 const std::vector<Rate>& triggers) { 600 lowerTriggers_ = triggers; 601 return *this; 602 } 603 withUpperTriggers(Rate trigger)604 RangeAccrualLeg& RangeAccrualLeg::withUpperTriggers(Rate trigger) { 605 upperTriggers_ = std::vector<Rate>(1,trigger); 606 return *this; 607 } 608 withUpperTriggers(const std::vector<Rate> & triggers)609 RangeAccrualLeg& RangeAccrualLeg::withUpperTriggers( 610 const std::vector<Rate>& triggers) { 611 upperTriggers_ = triggers; 612 return *this; 613 } 614 withObservationTenor(const Period & tenor)615 RangeAccrualLeg& RangeAccrualLeg::withObservationTenor( 616 const Period& tenor) { 617 observationTenor_ = tenor; 618 return *this; 619 } 620 withObservationConvention(BusinessDayConvention convention)621 RangeAccrualLeg& RangeAccrualLeg::withObservationConvention( 622 BusinessDayConvention convention) { 623 observationConvention_ = convention; 624 return *this; 625 } 626 operator Leg() const627 RangeAccrualLeg::operator Leg() const { 628 629 QL_REQUIRE(!notionals_.empty(), "no notional given"); 630 631 Size n = schedule_.size()-1; 632 QL_REQUIRE(notionals_.size() <= n, 633 "too many nominals (" << notionals_.size() << 634 "), only " << n << " required"); 635 QL_REQUIRE(fixingDays_.size() <= n, 636 "too many fixingDays (" << fixingDays_.size() << 637 "), only " << n << " required"); 638 QL_REQUIRE(gearings_.size()<=n, 639 "too many gearings (" << gearings_.size() << 640 "), only " << n << " required"); 641 QL_REQUIRE(spreads_.size()<=n, 642 "too many spreads (" << spreads_.size() << 643 "), only " << n << " required"); 644 QL_REQUIRE(lowerTriggers_.size()<=n, 645 "too many lowerTriggers (" << lowerTriggers_.size() << 646 "), only " << n << " required"); 647 QL_REQUIRE(upperTriggers_.size()<=n, 648 "too many upperTriggers (" << upperTriggers_.size() << 649 "), only " << n << " required"); 650 651 Leg leg(n); 652 653 // the following is not always correct 654 Calendar calendar = schedule_.calendar(); 655 656 Date refStart, start, refEnd, end; 657 Date paymentDate; 658 std::vector<ext::shared_ptr<Schedule> > observationsSchedules; 659 660 for (Size i=0; i<n; ++i) { 661 refStart = start = schedule_.date(i); 662 refEnd = end = schedule_.date(i+1); 663 paymentDate = calendar.adjust(end, paymentAdjustment_); 664 if (i==0 && schedule_.hasIsRegular() && !schedule_.isRegular(i+1)) { 665 BusinessDayConvention bdc = schedule_.businessDayConvention(); 666 refStart = calendar.adjust(end - schedule_.tenor(), bdc); 667 } 668 if (i==n-1 && schedule_.hasIsRegular() && !schedule_.isRegular(i+1)) { 669 BusinessDayConvention bdc = schedule_.businessDayConvention(); 670 refEnd = calendar.adjust(start + schedule_.tenor(), bdc); 671 } 672 if (detail::get(gearings_, i, 1.0) == 0.0) { // fixed coupon 673 leg.push_back(ext::shared_ptr<CashFlow>(new 674 FixedRateCoupon(paymentDate, 675 detail::get(notionals_, i, Null<Real>()), 676 detail::get(spreads_, i, 0.0), 677 paymentDayCounter_, 678 start, end, refStart, refEnd))); 679 } else { // floating coupon 680 observationsSchedules.push_back( 681 ext::make_shared<Schedule>(start, end, 682 observationTenor_, calendar, 683 observationConvention_, 684 observationConvention_, 685 DateGeneration::Forward, false)); 686 687 leg.push_back(ext::shared_ptr<CashFlow>(new 688 RangeAccrualFloatersCoupon( 689 paymentDate, 690 detail::get(notionals_, i, Null<Real>()), 691 index_, 692 start, end, 693 detail::get(fixingDays_, i, 2), 694 paymentDayCounter_, 695 detail::get(gearings_, i, 1.0), 696 detail::get(spreads_, i, 0.0), 697 refStart, refEnd, 698 observationsSchedules.back(), 699 detail::get(lowerTriggers_, i, Null<Rate>()), 700 detail::get(upperTriggers_, i, Null<Rate>())))); 701 } 702 } 703 return leg; 704 } 705 706 } 707