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