1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 3 /* 4 Copyright (C) 2007 Cristina Duminuco 5 Copyright (C) 2007 Giorgio Facchinetti 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/capflooredcoupon.hpp> 22 #include <ql/cashflows/digitalcoupon.hpp> 23 #include <ql/indexes/indexmanager.hpp> 24 #include <ql/indexes/interestrateindex.hpp> 25 26 namespace QuantLib { 27 DigitalCoupon(const ext::shared_ptr<FloatingRateCoupon> & underlying,Rate callStrike,Position::Type callPosition,bool isCallATMIncluded,Rate callDigitalPayoff,Rate putStrike,Position::Type putPosition,bool isPutATMIncluded,Rate putDigitalPayoff,const ext::shared_ptr<DigitalReplication> & replication,const bool nakedOption)28 DigitalCoupon::DigitalCoupon(const ext::shared_ptr<FloatingRateCoupon>& underlying, 29 Rate callStrike, 30 Position::Type callPosition, 31 bool isCallATMIncluded, 32 Rate callDigitalPayoff, 33 Rate putStrike, 34 Position::Type putPosition, 35 bool isPutATMIncluded, 36 Rate putDigitalPayoff, 37 const ext::shared_ptr<DigitalReplication>& replication, 38 const bool nakedOption) 39 : FloatingRateCoupon(underlying->date(), 40 underlying->nominal(), 41 underlying->accrualStartDate(), 42 underlying->accrualEndDate(), 43 underlying->fixingDays(), 44 underlying->index(), 45 underlying->gearing(), 46 underlying->spread(), 47 underlying->referencePeriodStart(), 48 underlying->referencePeriodEnd(), 49 underlying->dayCounter(), 50 underlying->isInArrears()), 51 underlying_(underlying), callCsi_(0.), putCsi_(0.), 52 isCallATMIncluded_(isCallATMIncluded), isPutATMIncluded_(isPutATMIncluded), 53 isCallCashOrNothing_(false), isPutCashOrNothing_(false), 54 callLeftEps_(replication->gap()/2.), callRightEps_(replication->gap()/2.), 55 putLeftEps_(replication->gap()/2.), putRightEps_(replication->gap()/2.), 56 hasPutStrike_(false), hasCallStrike_(false), 57 replicationType_(replication->replicationType()), nakedOption_(nakedOption) { 58 59 QL_REQUIRE(replication->gap()>0.0, "Non positive epsilon not allowed"); 60 61 if (putStrike == Null<Rate>()) { 62 QL_REQUIRE(putDigitalPayoff == Null<Rate>(), 63 "Put Cash rate non allowed if put strike is null"); 64 } 65 if (callStrike == Null<Rate>()) { 66 QL_REQUIRE(callDigitalPayoff == Null<Rate>(), 67 "Call Cash rate non allowed if call strike is null"); 68 } 69 if (callStrike != Null<Rate>()) { 70 hasCallStrike_ = true; 71 callStrike_ = callStrike; 72 switch (callPosition) { 73 case Position::Long : 74 callCsi_ = 1.0; 75 break; 76 case Position::Short : 77 callCsi_ = -1.0; 78 break; 79 default: 80 QL_FAIL("unsupported position type"); 81 } 82 if (callDigitalPayoff != Null<Rate>()){ 83 callDigitalPayoff_ = callDigitalPayoff; 84 isCallCashOrNothing_ = true; 85 } 86 } 87 if (putStrike != Null<Rate>()){ 88 hasPutStrike_ = true; 89 putStrike_ = putStrike; 90 switch (putPosition) { 91 case Position::Long : 92 putCsi_ = 1.0; 93 break; 94 case Position::Short : 95 putCsi_ = -1.0; 96 break; 97 default: 98 QL_FAIL("unsupported position type"); 99 } 100 if (putDigitalPayoff != Null<Rate>()){ 101 putDigitalPayoff_ = putDigitalPayoff; 102 isPutCashOrNothing_ = true; 103 } 104 } 105 106 switch (replicationType_) { 107 case Replication::Central : 108 // do nothing 109 break; 110 case Replication::Sub : 111 if (hasCallStrike_) { 112 switch (callPosition) { 113 case Position::Long : 114 callLeftEps_ = 0.; 115 callRightEps_ = replication->gap(); 116 break; 117 case Position::Short : 118 callLeftEps_ = replication->gap(); 119 callRightEps_ = 0.; 120 break; 121 default: 122 QL_FAIL("unsupported position type"); 123 } 124 } 125 if (hasPutStrike_) { 126 switch (putPosition) { 127 case Position::Long : 128 putLeftEps_ = replication->gap(); 129 putRightEps_ = 0.; 130 break; 131 case Position::Short : 132 putLeftEps_ = 0.; 133 putRightEps_ = replication->gap(); 134 break; 135 default: 136 QL_FAIL("unsupported position type"); 137 } 138 } 139 break; 140 case Replication::Super : 141 if (hasCallStrike_) { 142 switch (callPosition) { 143 case Position::Long : 144 callLeftEps_ = replication->gap(); 145 callRightEps_ = 0.; 146 break; 147 case Position::Short : 148 callLeftEps_ = 0.; 149 callRightEps_ = replication->gap(); 150 break; 151 default: 152 QL_FAIL("unsupported position type"); 153 } 154 } 155 if (hasPutStrike_) { 156 switch (putPosition) { 157 case Position::Long : 158 putLeftEps_ = 0.; 159 putRightEps_ = replication->gap(); 160 break; 161 case Position::Short : 162 putLeftEps_ = replication->gap(); 163 putRightEps_ = 0.; 164 break; 165 default: 166 QL_FAIL("unsupported position type"); 167 } 168 } 169 break; 170 default: 171 QL_FAIL("unsupported replication type"); 172 } 173 174 registerWith(underlying); 175 } 176 177 callOptionRate() const178 Rate DigitalCoupon::callOptionRate() const { 179 180 Rate callOptionRate = Rate(0.); 181 if(hasCallStrike_) { 182 // Step function 183 callOptionRate = isCallCashOrNothing_ ? callDigitalPayoff_ : callStrike_; 184 CappedFlooredCoupon next(underlying_, callStrike_ + callRightEps_); 185 CappedFlooredCoupon previous(underlying_, callStrike_ - callLeftEps_); 186 callOptionRate *= (next.rate() - previous.rate()) 187 / (callLeftEps_ + callRightEps_); 188 if (!isCallCashOrNothing_) { 189 // Call 190 CappedFlooredCoupon atStrike(underlying_, callStrike_); 191 Rate call = underlying_->rate() - atStrike.rate(); 192 // Sum up 193 callOptionRate += call; 194 } 195 } 196 return callOptionRate; 197 } 198 putOptionRate() const199 Rate DigitalCoupon::putOptionRate() const { 200 201 Rate putOptionRate = Rate(0.); 202 if(hasPutStrike_) { 203 // Step function 204 putOptionRate = isPutCashOrNothing_ ? putDigitalPayoff_ : putStrike_; 205 CappedFlooredCoupon next(underlying_, Null<Rate>(), putStrike_ + putRightEps_); 206 CappedFlooredCoupon previous(underlying_, Null<Rate>(), putStrike_ - putLeftEps_); 207 putOptionRate *= (next.rate() - previous.rate()) 208 / (putLeftEps_ + putRightEps_); 209 if (!isPutCashOrNothing_) { 210 // Put 211 CappedFlooredCoupon atStrike(underlying_, Null<Rate>(), putStrike_); 212 Rate put = - underlying_->rate() + atStrike.rate(); 213 // Sum up 214 putOptionRate -= put; 215 } 216 } 217 return putOptionRate; 218 } 219 rate() const220 Rate DigitalCoupon::rate() const { 221 222 QL_REQUIRE(underlying_->pricer(), "pricer not set"); 223 224 Date fixingDate = underlying_->fixingDate(); 225 Date today = Settings::instance().evaluationDate(); 226 bool enforceTodaysHistoricFixings = 227 Settings::instance().enforcesTodaysHistoricFixings(); 228 Rate underlyingRate = nakedOption_ ? 0.0 : underlying_->rate(); 229 if (fixingDate < today || 230 ((fixingDate == today) && enforceTodaysHistoricFixings)) { 231 // must have been fixed 232 return underlyingRate + callCsi_ * callPayoff() + putCsi_ * putPayoff(); 233 } 234 if (fixingDate == today) { 235 // might have been fixed 236 Rate pastFixing = 237 IndexManager::instance().getHistory((underlying_->index())->name())[fixingDate]; 238 if (pastFixing != Null<Real>()) { 239 return underlyingRate + callCsi_ * callPayoff() + putCsi_ * putPayoff(); 240 } else 241 return underlyingRate + callCsi_ * callOptionRate() + putCsi_ * putOptionRate(); 242 } 243 return underlyingRate + callCsi_ * callOptionRate() + putCsi_ * putOptionRate(); 244 } 245 convexityAdjustment() const246 Rate DigitalCoupon::convexityAdjustment() const { 247 return underlying_->convexityAdjustment(); 248 } 249 callStrike() const250 Rate DigitalCoupon::callStrike() const { 251 if (hasCall()) 252 return callStrike_; 253 else 254 return Null<Rate>(); 255 } 256 putStrike() const257 Rate DigitalCoupon::putStrike() const { 258 if (hasPut()) 259 return putStrike_; 260 else 261 return Null<Rate>(); 262 } 263 callDigitalPayoff() const264 Rate DigitalCoupon::callDigitalPayoff() const { 265 if (isCallCashOrNothing_) 266 return callDigitalPayoff_; 267 else 268 return Null<Rate>(); 269 } 270 putDigitalPayoff() const271 Rate DigitalCoupon::putDigitalPayoff() const { 272 if (isPutCashOrNothing_) 273 return putDigitalPayoff_; 274 else 275 return Null<Rate>(); 276 } 277 update()278 void DigitalCoupon::update() { 279 notifyObservers(); 280 } 281 accept(AcyclicVisitor & v)282 void DigitalCoupon::accept(AcyclicVisitor& v) { 283 typedef FloatingRateCoupon super; 284 Visitor<DigitalCoupon>* v1 = 285 dynamic_cast<Visitor<DigitalCoupon>*>(&v); 286 if (v1 != 0) 287 v1->visit(*this); 288 else 289 super::accept(v); 290 } 291 callPayoff() const292 Rate DigitalCoupon::callPayoff() const { 293 // to use only if index has fixed 294 Rate payoff(0.); 295 if(hasCallStrike_) { 296 Rate underlyingRate = underlying_->rate(); 297 if ( (underlyingRate - callStrike_) > 1.e-16 ) { 298 payoff = isCallCashOrNothing_ ? callDigitalPayoff_ : underlyingRate; 299 } else { 300 if (isCallATMIncluded_) { 301 if ( std::abs(callStrike_ - underlyingRate) <= 1.e-16 ) 302 payoff = isCallCashOrNothing_ ? callDigitalPayoff_ : underlyingRate; 303 } 304 } 305 } 306 return payoff; 307 } 308 putPayoff() const309 Rate DigitalCoupon::putPayoff() const { 310 // to use only if index has fixed 311 Rate payoff(0.); 312 if(hasPutStrike_) { 313 Rate underlyingRate = underlying_->rate(); 314 if ( (putStrike_ - underlyingRate) > 1.e-16 ) { 315 payoff = isPutCashOrNothing_ ? putDigitalPayoff_ : underlyingRate; 316 } else { 317 // putStrike_ <= underlyingRate 318 if (isPutATMIncluded_) { 319 if ( std::abs(putStrike_ - underlyingRate) <= 1.e-16 ) 320 payoff = isPutCashOrNothing_ ? putDigitalPayoff_ : underlyingRate; 321 } 322 } 323 } 324 return payoff; 325 } 326 327 } 328 329