QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.32
Loading...
Searching...
No Matches
creditdefaultswap.cpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2008, 2009 Jose Aparicio
5 Copyright (C) 2008 Roland Lichters
6 Copyright (C) 2008 StatPro Italia srl
7
8 This file is part of QuantLib, a free-software/open-source library
9 for financial quantitative analysts and developers - http://quantlib.org/
10
11 QuantLib is free software: you can redistribute it and/or modify it
12 under the terms of the QuantLib license. You should have received a
13 copy of the license along with this program; if not, please email
14 <quantlib-dev@lists.sf.net>. The license is also available online at
15 <http://quantlib.org/license.shtml>.
16
17 This program is distributed in the hope that it will be useful, but WITHOUT
18 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 FOR A PARTICULAR PURPOSE. See the license for more details.
20*/
21
22#include <ql/cashflows/fixedratecoupon.hpp>
23#include <ql/cashflows/simplecashflow.hpp>
24#include <ql/instruments/claim.hpp>
25#include <ql/instruments/creditdefaultswap.hpp>
26#include <ql/math/solvers1d/brent.hpp>
27#include <ql/pricingengines/credit/isdacdsengine.hpp>
28#include <ql/pricingengines/credit/midpointcdsengine.hpp>
29#include <ql/quotes/simplequote.hpp>
30#include <ql/termstructures/credit/flathazardrate.hpp>
31#include <ql/termstructures/yieldtermstructure.hpp>
32#include <ql/time/calendars/weekendsonly.hpp>
33#include <ql/time/schedule.hpp>
34#include <ql/optional.hpp>
35#include <utility>
36
37namespace QuantLib {
38
40 Real notional,
41 Rate spread,
42 const Schedule& schedule,
43 BusinessDayConvention convention,
44 const DayCounter& dayCounter,
45 bool settlesAccrual,
46 bool paysAtDefaultTime,
47 const Date& protectionStart,
48 ext::shared_ptr<Claim> claim,
49 const DayCounter& lastPeriodDayCounter,
50 const bool rebatesAccrual,
51 const Date& tradeDate,
52 Natural cashSettlementDays)
53 : side_(side), notional_(notional), upfront_(ext::nullopt), runningSpread_(spread),
54 settlesAccrual_(settlesAccrual), paysAtDefaultTime_(paysAtDefaultTime),
55 claim_(std::move(claim)),
56 protectionStart_(protectionStart == Null<Date>() ? schedule[0] : protectionStart),
57 tradeDate_(tradeDate), cashSettlementDays_(cashSettlementDays) {
58
59 init(schedule, convention, dayCounter, lastPeriodDayCounter, rebatesAccrual);
60 }
61
63 Real notional,
64 Rate upfront,
65 Rate runningSpread,
66 const Schedule& schedule,
67 BusinessDayConvention convention,
68 const DayCounter& dayCounter,
69 bool settlesAccrual,
70 bool paysAtDefaultTime,
71 const Date& protectionStart,
72 const Date& upfrontDate,
73 ext::shared_ptr<Claim> claim,
74 const DayCounter& lastPeriodDayCounter,
75 const bool rebatesAccrual,
76 const Date& tradeDate,
77 Natural cashSettlementDays)
78 : side_(side), notional_(notional), upfront_(upfront), runningSpread_(runningSpread),
79 settlesAccrual_(settlesAccrual), paysAtDefaultTime_(paysAtDefaultTime),
80 claim_(std::move(claim)),
81 protectionStart_(protectionStart == Null<Date>() ? schedule[0] : protectionStart),
82 tradeDate_(tradeDate), cashSettlementDays_(cashSettlementDays) {
83
84 init(schedule, convention, dayCounter, lastPeriodDayCounter, rebatesAccrual, upfrontDate);
85 }
86
87 void CreditDefaultSwap::init(const Schedule& schedule, BusinessDayConvention paymentConvention,
88 const DayCounter& dayCounter, const DayCounter& lastPeriodDayCounter,
89 bool rebatesAccrual, const Date& upfrontDate) {
90
91 QL_REQUIRE(!schedule.empty(), "CreditDefaultSwap needs a non-empty schedule.");
92
93 bool postBigBang = false;
94 if (schedule.hasRule()) {
95 DateGeneration::Rule rule = schedule.rule();
96 postBigBang = rule == DateGeneration::CDS || rule == DateGeneration::CDS2015;
97 }
98
99 if (!postBigBang) {
100 QL_REQUIRE(protectionStart_ <= schedule[0], "protection can not start after accrual");
101 }
102
103 leg_ = FixedRateLeg(schedule)
105 .withCouponRates(runningSpread_, dayCounter)
106 .withPaymentAdjustment(paymentConvention)
107 .withLastPeriodDayCounter(lastPeriodDayCounter);
108
109 // Deduce the trade date if not given.
110 if (tradeDate_ == Date()) {
111 if (postBigBang) {
113 } else {
115 }
116 }
117
118 // Deduce the cash settlement date if not given.
119 Date effectiveUpfrontDate = upfrontDate;
120 if (effectiveUpfrontDate == Date()) {
121 effectiveUpfrontDate = schedule.calendar().advance(tradeDate_,
122 cashSettlementDays_, Days, paymentConvention);
123 }
124 QL_REQUIRE(effectiveUpfrontDate >= protectionStart_,
125 "The cash settlement date must not be before the protection start date.");
126
127 // Create the upfront payment, if one is provided.
128 Real upfrontAmount = 0.0;
129 if (upfront_) // NOLINT(readability-implicit-bool-conversion)
130 upfrontAmount = *upfront_ * notional_;
131 upfrontPayment_ = ext::make_shared<SimpleCashFlow>(upfrontAmount, effectiveUpfrontDate);
132
133 // Set the maturity date.
134 maturity_ = schedule.dates().back();
135
136 // Deal with the accrual rebate. We use the standard conventions for accrual calculation introduced with the
137 // CDS Big Bang in 2009.
138 if (rebatesAccrual) {
139
140 Real rebateAmount = 0.0;
141 Date refDate = tradeDate_ + 1;
142
143 if (tradeDate_ >= schedule.dates().front()) {
144 for (Size i = 0; i < leg_.size(); ++i) {
145 const ext::shared_ptr<CashFlow>& cf = leg_[i];
146 if (refDate > cf->date()) {
147 // This coupon is in the past; check the next one
148 continue;
149 } else if (refDate == cf->date()) {
150 // This coupon pays at the reference date.
151 // If it's not the last coupon, the accrual is 0 so do nothing.
152 if (i < leg_.size() - 1)
153 rebateAmount = 0.0;
154 else {
155 // On last coupon
156 ext::shared_ptr<FixedRateCoupon> frc = ext::dynamic_pointer_cast<FixedRateCoupon>(cf);
157 rebateAmount = frc->amount();
158 }
159 break;
160 } else {
161 // This coupon pays in the future, and is the first coupon to do so (since they're sorted).
162 // Calculate the accrual and skip further coupons
163 ext::shared_ptr<FixedRateCoupon> frc = ext::dynamic_pointer_cast<FixedRateCoupon>(cf);
164 rebateAmount = frc->accruedAmount(refDate);
165 break;
166 }
167 }
168 }
169
170 accrualRebate_ = ext::make_shared<SimpleCashFlow>(rebateAmount, effectiveUpfrontDate);
171 }
172
173 if (!claim_)
174 claim_ = ext::make_shared<FaceValueClaim>();
176 }
177
179 return side_;
180 }
181
183 return notional_;
184 }
185
187 return runningSpread_;
188 }
189
190 ext::optional<Rate> CreditDefaultSwap::upfront() const {
191 return upfront_;
192 }
193
195 return settlesAccrual_;
196 }
197
199 return paysAtDefaultTime_;
200 }
201
203 return leg_;
204 }
205
206
208 for (auto i = leg_.rbegin(); i != leg_.rend(); ++i) {
209 if (!(*i)->hasOccurred())
210 return false;
211 }
212 return true;
213 }
214
220 }
221
223 PricingEngine::arguments* args) const {
224 auto* arguments = dynamic_cast<CreditDefaultSwap::arguments*>(args);
225 QL_REQUIRE(arguments != nullptr, "wrong argument type");
226
229 arguments->leg = leg_;
239 }
240
241
243 const PricingEngine::results* r) const {
245
246 const auto* results = dynamic_cast<const CreditDefaultSwap::results*>(r);
247 QL_REQUIRE(results != nullptr, "wrong result type");
248
257 }
258
260 calculate();
261 QL_REQUIRE(fairUpfront_ != Null<Rate>(),
262 "fair upfront not available");
263 return fairUpfront_;
264 }
265
267 calculate();
268 QL_REQUIRE(fairSpread_ != Null<Rate>(),
269 "fair spread not available");
270 return fairSpread_;
271 }
272
274 calculate();
275 QL_REQUIRE(couponLegBPS_ != Null<Rate>(),
276 "coupon-leg BPS not available");
277 return couponLegBPS_;
278 }
279
281 calculate();
282 QL_REQUIRE(couponLegNPV_ != Null<Rate>(),
283 "coupon-leg NPV not available");
284 return couponLegNPV_;
285 }
286
288 calculate();
289 QL_REQUIRE(defaultLegNPV_ != Null<Rate>(),
290 "default-leg NPV not available");
291 return defaultLegNPV_;
292 }
293
295 calculate();
296 QL_REQUIRE(upfrontNPV_ != Null<Real>(),
297 "upfront NPV not available");
298 return upfrontNPV_;
299 }
300
302 calculate();
303 QL_REQUIRE(upfrontBPS_ != Null<Real>(),
304 "upfront BPS not available");
305 return upfrontBPS_;
306 }
307
309 calculate();
310 QL_REQUIRE(accrualRebateNPV_ != Null<Real>(),
311 "accrual Rebate NPV not available");
312 return accrualRebateNPV_;
313 }
314
315 namespace {
316
317 class ObjectiveFunction {
318 public:
319 ObjectiveFunction(Real target,
320 SimpleQuote& quote,
321 PricingEngine& engine,
323 : target_(target), quote_(quote),
324 engine_(engine), results_(results) {}
325
326 Real operator()(Real guess) const {
327 quote_.setValue(guess);
328 engine_.calculate();
329 return results_->value - target_;
330 }
331 private:
332 Real target_;
333 SimpleQuote& quote_;
334 PricingEngine& engine_;
335 const CreditDefaultSwap::results* results_;
336 };
337
338 }
339
341 Real targetNPV,
342 const Handle<YieldTermStructure>& discountCurve,
343 const DayCounter& dayCounter,
344 Real recoveryRate,
345 Real accuracy,
346 PricingModel model) const {
347
348 ext::shared_ptr<SimpleQuote> flatRate = ext::make_shared<SimpleQuote>(0.0);
349
352 ext::make_shared<FlatHazardRate>(0, WeekendsOnly(),
353 Handle<Quote>(flatRate), dayCounter));
354
355 ext::shared_ptr<PricingEngine> engine;
356 switch (model) {
357 case Midpoint:
358 engine = ext::make_shared<MidPointCdsEngine>(
359 probability, recoveryRate, discountCurve);
360 break;
361 case ISDA:
362 engine = ext::make_shared<IsdaCdsEngine>(
363 probability, recoveryRate, discountCurve,
368 break;
369 default:
370 QL_FAIL("unknown CDS pricing model: " << model);
371 }
372
374 const auto* results = dynamic_cast<const CreditDefaultSwap::results*>(engine->getResults());
375
376 ObjectiveFunction f(targetNPV, *flatRate, *engine, results);
377 //very close guess if targetNPV = 0.
378 Rate guess = runningSpread_ / (1 - recoveryRate) * 365./360.;
379 Real step = 0.1 * guess;
380 return Brent().solve(f, accuracy, guess, step);
381 }
382
384 Real conventionalRecovery,
385 const Handle<YieldTermStructure>& discountCurve,
386 const DayCounter& dayCounter,
387 PricingModel model) const {
388
389 ext::shared_ptr<SimpleQuote> flatRate = ext::make_shared<SimpleQuote>(0.0);
390
393 ext::make_shared<FlatHazardRate>(0, WeekendsOnly(),
394 Handle<Quote>(flatRate), dayCounter));
395
396 ext::shared_ptr<PricingEngine> engine;
397 switch (model) {
398 case Midpoint:
399 engine = ext::make_shared<MidPointCdsEngine>(
400 probability, conventionalRecovery, discountCurve);
401 break;
402 case ISDA:
403 engine = ext::make_shared<IsdaCdsEngine>(
404 probability, conventionalRecovery, discountCurve,
409 break;
410 default:
411 QL_FAIL("unknown CDS pricing model: " << model);
412 }
413
415 const auto* results = dynamic_cast<const CreditDefaultSwap::results*>(engine->getResults());
416
417 ObjectiveFunction f(0., *flatRate, *engine, results);
418 Rate guess = runningSpread_ / (1 - conventionalRecovery) * 365./360.;
419 Real step = guess * 0.1;
420
421 Brent().solve(f, 1e-9, guess, step);
422 return results->fairSpread;
423 }
424
425
427 return protectionStart_;
428 }
429
431 return ext::dynamic_pointer_cast<Coupon>(leg_.back())
432 ->accrualEndDate();
433 }
434
435 const ext::shared_ptr<SimpleCashFlow>& CreditDefaultSwap::upfrontPayment() const {
436 return upfrontPayment_;
437 }
438
439 const ext::shared_ptr<SimpleCashFlow>& CreditDefaultSwap::accrualRebate() const {
440 return accrualRebate_;
441 }
442
444 return tradeDate_;
445 }
446
448 return cashSettlementDays_;
449 }
450
452 : side(Protection::Side(-1)), notional(Null<Real>()),
453 spread(Null<Rate>()) {}
454
456 QL_REQUIRE(side != Protection::Side(-1), "side not set");
457 QL_REQUIRE(notional != Null<Real>(), "notional not set");
458 QL_REQUIRE(notional != 0.0, "null notional set");
459 QL_REQUIRE(spread != Null<Rate>(), "spread not set");
460 QL_REQUIRE(!leg.empty(), "coupons not set");
461 QL_REQUIRE(upfrontPayment, "upfront payment not set");
462 QL_REQUIRE(claim, "claim not set");
463 QL_REQUIRE(protectionStart != Null<Date>(),
464 "protection start date not set");
465 QL_REQUIRE(maturity != Null<Date>(),
466 "maturity date not set");
467 }
468
479 }
480
482
483 QL_REQUIRE(rule == DateGeneration::CDS2015 || rule == DateGeneration::CDS || rule == DateGeneration::OldCDS,
484 "cdsMaturity should only be used with date generation rule CDS2015, CDS or OldCDS");
485
486 QL_REQUIRE(tenor.units() == Years || (tenor.units() == Months && tenor.length() % 3 == 0),
487 "cdsMaturity expects a tenor that is a multiple of 3 months.");
488
489 if (rule == DateGeneration::OldCDS) {
490 QL_REQUIRE(tenor != 0 * Months, "A tenor of 0M is not supported for OldCDS.");
491 }
492
493 Date anchorDate = previousTwentieth(tradeDate, rule);
494 if (rule == DateGeneration::CDS2015 && (anchorDate == Date(20, Dec, anchorDate.year()) ||
495 anchorDate == Date(20, Jun, anchorDate.year()))) {
496 if (tenor.length() == 0) {
497 return Null<Date>();
498 } else {
499 anchorDate -= 3 * Months;
500 }
501 }
502
503 Date maturity = anchorDate + tenor + 3 * Months;
504 QL_REQUIRE(maturity > tradeDate, "error calculating CDS maturity. Tenor is " << tenor << ", trade date is " <<
505 io::iso_date(tradeDate) << " generating a maturity of " << io::iso_date(maturity) << " <= trade date.");
506
507 return maturity;
508 }
509
510}
Brent 1-D solver
Definition: brent.hpp:37
Date advance(const Date &, Integer n, TimeUnit unit, BusinessDayConvention convention=Following, bool endOfMonth=false) const
Definition: calendar.cpp:130
ext::shared_ptr< SimpleCashFlow > accrualRebate
ext::shared_ptr< SimpleCashFlow > upfrontPayment
const Date & protectionStartDate() const
The first date for which defaults will trigger the contract.
const ext::shared_ptr< SimpleCashFlow > & upfrontPayment() const
void init(const Schedule &schedule, BusinessDayConvention paymentConvention, const DayCounter &dayCounter, const DayCounter &lastPeriodDayCounter, bool rebatesAccrual, const Date &upfrontDate=Date())
Shared initialisation.
void setupArguments(PricingEngine::arguments *) const override
bool isExpired() const override
returns whether the instrument might have value greater than zero.
const Date & tradeDate() const
CreditDefaultSwap(Protection::Side side, Real notional, Rate spread, const Schedule &schedule, BusinessDayConvention paymentConvention, const DayCounter &dayCounter, bool settlesAccrual=true, bool paysAtDefaultTime=true, const Date &protectionStart=Date(), ext::shared_ptr< Claim >=ext::shared_ptr< Claim >(), const DayCounter &lastPeriodDayCounter=DayCounter(), bool rebatesAccrual=true, const Date &tradeDate=Date(), Natural cashSettlementDays=3)
CDS quoted as running-spread only.
ext::shared_ptr< Claim > claim_
ext::shared_ptr< SimpleCashFlow > accrualRebate_
ext::optional< Rate > upfront() const
ext::optional< Rate > upfront_
Rate impliedHazardRate(Real targetNPV, const Handle< YieldTermStructure > &discountCurve, const DayCounter &dayCounter, Real recoveryRate=0.4, Real accuracy=1.0e-8, PricingModel model=Midpoint) const
Implied hazard rate calculation.
const ext::shared_ptr< SimpleCashFlow > & accrualRebate() const
void setupExpired() const override
const Date & protectionEndDate() const
The last date for which defaults will trigger the contract.
void fetchResults(const PricingEngine::results *) const override
Rate conventionalSpread(Real conventionalRecovery, const Handle< YieldTermStructure > &discountCurve, const DayCounter &dayCounter, PricingModel model=Midpoint) const
Conventional/standard upfront-to-spread conversion.
ext::shared_ptr< SimpleCashFlow > upfrontPayment_
Protection::Side side() const
Concrete date class.
Definition: date.hpp:125
Year year() const
Definition: date.cpp:93
day counter class
Definition: daycounter.hpp:44
helper class building a sequence of fixed rate coupons
FixedRateLeg & withNotionals(Real)
FixedRateLeg & withPaymentAdjustment(BusinessDayConvention)
FixedRateLeg & withCouponRates(Rate, const DayCounter &paymentDayCounter, Compounding comp=Simple, Frequency freq=Annual)
FixedRateLeg & withLastPeriodDayCounter(const DayCounter &)
PricingEngine::arguments * getArguments() const override
const PricingEngine::results * getResults() const override
Shared handle to an observable.
Definition: handle.hpp:41
void calculate() const override
Definition: instrument.hpp:129
virtual void fetchResults(const PricingEngine::results *) const
Definition: instrument.hpp:155
virtual void setupExpired() const
Definition: instrument.hpp:140
template class providing a null value for a given type.
Definition: null.hpp:76
std::pair< iterator, bool > registerWith(const ext::shared_ptr< Observable > &)
Definition: observable.hpp:228
TimeUnit units() const
Definition: period.hpp:51
Integer length() const
Definition: period.hpp:50
interface for pricing engines
Payment schedule.
Definition: schedule.hpp:40
const Calendar & calendar() const
Definition: schedule.hpp:176
const std::vector< Date > & dates() const
Definition: schedule.hpp:75
bool hasRule() const
Definition: schedule.hpp:212
bool empty() const
Definition: schedule.hpp:82
DateGeneration::Rule rule() const
Definition: schedule.hpp:216
market element returning a stored value
Definition: simplequote.hpp:33
Real solve(const F &f, Real accuracy, Real guess, Real step) const
Definition: solver1d.hpp:84
Weekends-only calendar.
BusinessDayConvention
Business Day conventions.
@ Jun
Definition: date.hpp:73
@ Dec
Definition: date.hpp:79
detail::iso_date_holder iso_date(const Date &d)
output dates in ISO format (yyyy-mm-dd)
Definition: date.cpp:991
QL_REAL Real
real number
Definition: types.hpp:50
unsigned QL_INTEGER Natural
positive integer
Definition: types.hpp:43
Real Rate
interest rates
Definition: types.hpp:70
std::size_t Size
size of a container
Definition: types.hpp:58
const boost::none_t & nullopt
Definition: optional.cpp:27
Definition: any.hpp:35
Date previousTwentieth(const Date &d, DateGeneration::Rule rule)
Definition: schedule.cpp:640
Date cdsMaturity(const Date &tradeDate, const Period &tenor, DateGeneration::Rule rule)
std::vector< ext::shared_ptr< CashFlow > > Leg
Sequence of cash-flows.
Definition: cashflow.hpp:78
STL namespace.
information on a default-protection contract
Definition: default.hpp:32