1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 3 /* 4 Copyright (C) 2008, 2009 Roland Lichters 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 #include <ql/experimental/credit/riskyassetswap.hpp> 21 #include <ql/event.hpp> 22 23 #include <ql/utilities/null_deleter.hpp> 24 25 namespace QuantLib { 26 RiskyAssetSwap(bool fixedPayer,Real nominal,const Schedule & fixedSchedule,const Schedule & floatSchedule,const DayCounter & fixedDayCounter,const DayCounter & floatDayCounter,Rate spread,Rate recoveryRate,const Handle<YieldTermStructure> & yieldTS,const Handle<DefaultProbabilityTermStructure> & defaultTS,Rate coupon)27 RiskyAssetSwap::RiskyAssetSwap( 28 bool fixedPayer, 29 Real nominal, 30 const Schedule& fixedSchedule, 31 const Schedule& floatSchedule, 32 const DayCounter& fixedDayCounter, 33 const DayCounter& floatDayCounter, 34 Rate spread, 35 Rate recoveryRate, 36 const Handle<YieldTermStructure>& yieldTS, 37 const Handle<DefaultProbabilityTermStructure>& defaultTS, 38 Rate coupon) 39 : fixedPayer_(fixedPayer), nominal_(nominal), 40 fixedSchedule_(fixedSchedule), floatSchedule_(floatSchedule), 41 fixedDayCounter_(fixedDayCounter), floatDayCounter_(floatDayCounter), 42 spread_(spread), recoveryRate_(recoveryRate), 43 yieldTS_(yieldTS), defaultTS_(defaultTS), coupon_(coupon) { 44 45 registerWith (yieldTS_); 46 registerWith (defaultTS_); 47 } 48 isExpired() const49 bool RiskyAssetSwap::isExpired () const { 50 return detail::simple_event(fixedSchedule_.dates().back()) 51 .hasOccurred(yieldTS_->referenceDate()); 52 } 53 54 setupExpired() const55 void RiskyAssetSwap::setupExpired() const { 56 Instrument::setupExpired(); 57 } 58 59 performCalculations() const60 void RiskyAssetSwap::performCalculations() const { 61 // order of calls is essential 62 floatAnnuity_ = floatAnnuity(); 63 fixedAnnuity_ = fixedAnnuity(); 64 parCoupon_ = parCoupon(); 65 66 if (coupon_ == Null<Rate>()) coupon_ = parCoupon_; 67 68 recoveryValue_ = recoveryValue(); 69 riskyBondPrice_ = riskyBondPrice(); 70 71 NPV_ = riskyBondPrice_ 72 - coupon_ * fixedAnnuity_ 73 + yieldTS_->discount (fixedSchedule_.dates().front()) 74 - yieldTS_->discount (fixedSchedule_.dates().back()) 75 + spread_ * floatAnnuity_; 76 77 NPV_ *= nominal_; 78 79 if (!fixedPayer_) 80 NPV_ *= -1; 81 } 82 83 floatAnnuity() const84 Real RiskyAssetSwap::floatAnnuity () const { 85 Real annuity = 0; 86 for (Size i = 1; i < floatSchedule_.size(); i++) { 87 Time dcf = floatDayCounter_.yearFraction (floatSchedule_[i-1], 88 floatSchedule_[i]); 89 annuity += dcf * yieldTS_->discount (floatSchedule_[i]); 90 } 91 return annuity; 92 } 93 94 fixedAnnuity() const95 Real RiskyAssetSwap::fixedAnnuity () const { 96 Real annuity = 0; 97 for (Size i = 1; i < floatSchedule_.size(); i++) { 98 Time dcf = fixedDayCounter_.yearFraction (floatSchedule_[i-1], 99 floatSchedule_[i]); 100 annuity += dcf * yieldTS_->discount (floatSchedule_[i]); 101 } 102 return annuity; 103 } 104 105 parCoupon() const106 Real RiskyAssetSwap::parCoupon () const { 107 return (yieldTS_->discount(fixedSchedule_.dates().front()) 108 -yieldTS_->discount(fixedSchedule_.dates().back())) 109 / fixedAnnuity_; 110 } 111 112 recoveryValue() const113 Real RiskyAssetSwap::recoveryValue() const { 114 Real recoveryValue = 0; 115 // simple Euler integral to evaluate the recovery value 116 for (Size i = 1; i < fixedSchedule_.size(); i++) { 117 TimeUnit stepSize = Days; 118 Date d; 119 if (fixedSchedule_[i-1] >= defaultTS_->referenceDate()) 120 d = fixedSchedule_[i-1]; 121 else 122 d = defaultTS_->referenceDate(); 123 Date d0 = d; 124 do { 125 Real disc = yieldTS_->discount (d); 126 Real dd = defaultTS_->defaultDensity (d, true); 127 Real dcf = defaultTS_->dayCounter().yearFraction (d0, d); 128 129 recoveryValue += disc * dd * dcf; 130 131 d0 = d; 132 133 d = NullCalendar().advance (d0, 1, stepSize, Unadjusted); 134 } 135 while (d < fixedSchedule_[i]); 136 } 137 recoveryValue *= recoveryRate_; 138 139 return recoveryValue; 140 } 141 142 riskyBondPrice() const143 Real RiskyAssetSwap::riskyBondPrice () const { 144 Real value = 0; 145 for (Size i = 1; i < fixedSchedule_.size(); i++) { 146 Time dcf = fixedDayCounter_.yearFraction (fixedSchedule_[i-1], 147 fixedSchedule_[i]); 148 value += dcf * yieldTS_->discount (fixedSchedule_[i]) 149 * defaultTS_->survivalProbability (fixedSchedule_[i], true); 150 } 151 value *= coupon_; 152 153 value += yieldTS_->discount (fixedSchedule_.dates().back()) 154 * defaultTS_->survivalProbability (fixedSchedule_.dates().back(), 155 true); 156 157 return value + recoveryValue_; 158 } 159 160 fairSpread()161 Real RiskyAssetSwap::fairSpread () { 162 calculate(); 163 164 Real value = 0; 165 for (Size i = 1; i < fixedSchedule_.size(); i++) { 166 Time dcf = fixedDayCounter_.yearFraction (fixedSchedule_[i-1], 167 fixedSchedule_[i]); 168 value += dcf * yieldTS_->discount (fixedSchedule_[i]) 169 * defaultTS_->defaultProbability (fixedSchedule_[i], true); 170 } 171 value *= coupon_; 172 173 value += yieldTS_->discount (fixedSchedule_.dates().back()) 174 * defaultTS_->defaultProbability (fixedSchedule_.dates().back(), 175 true); 176 177 Real initialDiscount = yieldTS_->discount(fixedSchedule_[0]); 178 179 return (1.0 - initialDiscount + value - recoveryValue_) / fixedAnnuity_; 180 } 181 182 183 AssetSwapHelper(const Handle<Quote> & spread,const Period & tenor,Natural settlementDays,const Calendar & calendar,const Period & fixedPeriod,BusinessDayConvention fixedConvention,const DayCounter & fixedDayCount,const Period & floatPeriod,BusinessDayConvention floatConvention,const DayCounter & floatDayCount,Real recoveryRate,const RelinkableHandle<YieldTermStructure> & yieldTS,const Period & integrationStepSize)184 AssetSwapHelper::AssetSwapHelper( 185 const Handle<Quote>& spread, 186 const Period& tenor, 187 Natural settlementDays, 188 const Calendar& calendar, 189 const Period& fixedPeriod, 190 BusinessDayConvention fixedConvention, 191 const DayCounter& fixedDayCount, 192 const Period& floatPeriod, 193 BusinessDayConvention floatConvention, 194 const DayCounter& floatDayCount, 195 Real recoveryRate, 196 const RelinkableHandle<YieldTermStructure>& yieldTS, 197 const Period& integrationStepSize) 198 : DefaultProbabilityHelper(spread), 199 tenor_(tenor), settlementDays_(settlementDays), calendar_(calendar), 200 fixedConvention_(fixedConvention), 201 fixedPeriod_(fixedPeriod), fixedDayCount_(fixedDayCount), 202 floatConvention_(floatConvention), 203 floatPeriod_(floatPeriod), floatDayCount_(floatDayCount), 204 recoveryRate_(recoveryRate), yieldTS_(yieldTS), 205 integrationStepSize_(integrationStepSize) { 206 207 initializeDates(); 208 209 registerWith(Settings::instance().evaluationDate()); 210 registerWith(yieldTS); 211 } 212 impliedQuote() const213 Real AssetSwapHelper::impliedQuote() const { 214 QL_REQUIRE(!probability_.empty(), 215 "default term structure not set"); 216 // we didn't register as observers - force calculation 217 asw_->recalculate(); 218 return asw_->fairSpread(); 219 } 220 setTermStructure(DefaultProbabilityTermStructure * ts)221 void AssetSwapHelper::setTermStructure( 222 DefaultProbabilityTermStructure* ts) { 223 DefaultProbabilityHelper::setTermStructure(ts); 224 225 probability_.linkTo( 226 ext::shared_ptr<DefaultProbabilityTermStructure>(ts, null_deleter()), 227 false); 228 229 initializeDates(); 230 } 231 update()232 void AssetSwapHelper::update() { 233 if (evaluationDate_ != Settings::instance().evaluationDate()) 234 initializeDates(); 235 236 DefaultProbabilityHelper::update(); 237 } 238 initializeDates()239 void AssetSwapHelper::initializeDates() { 240 evaluationDate_ = Settings::instance().evaluationDate(); 241 242 earliestDate_ = calendar_.advance (evaluationDate_, 243 settlementDays_, Days); 244 245 Date maturity = earliestDate_ + tenor_; 246 247 latestDate_ = calendar_.adjust (maturity, fixedConvention_); 248 249 Schedule fixedSchedule(earliestDate_, maturity, 250 fixedPeriod_, calendar_, 251 fixedConvention_, fixedConvention_, 252 DateGeneration::Forward, false); 253 Schedule floatSchedule(earliestDate_, maturity, 254 floatPeriod_, calendar_, 255 floatConvention_, floatConvention_, 256 DateGeneration::Forward, false); 257 258 asw_ = ext::shared_ptr<RiskyAssetSwap>( 259 new RiskyAssetSwap(true, 260 100.0, 261 fixedSchedule, 262 floatSchedule, 263 fixedDayCount_, 264 floatDayCount_, 265 0.01, 266 recoveryRate_, 267 yieldTS_, 268 probability_)); 269 } 270 271 } 272