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