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

source code of quantlib/ql/experimental/credit/riskyassetswap.cpp