Skip to content

Commit 467d114

Browse files
committed
Merge branch 'QPR-11115' into 'master'
QPR-11115 update cds so that it can handle indexed coupons Closes QPR-11115 See merge request qs/quantlib!38
2 parents 43b6606 + 62dbd76 commit 467d114

3 files changed

Lines changed: 109 additions & 62 deletions

File tree

ql/instruments/creditdefaultswap.cpp

Lines changed: 74 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,15 @@ namespace QuantLib {
4848
bool rebatesAccrual,
4949
const Date& tradeDate,
5050
Natural cashSettlementDays)
51-
: side_(side), notional_(notional), upfront_(boost::none),
52-
runningSpread_(spread), settlesAccrual_(settlesAccrual),
51+
: side_(side), notional_(notional), upfront_(boost::none), runningSpread_(spread),
52+
schedule_(schedule), paymentConvention_(convention), settlesAccrual_(settlesAccrual),
5353
paysAtDefaultTime_(paysAtDefaultTime),
54-
protectionPaymentTime_(settlesAccrual ? atDefault : atPeriodEnd),
55-
claim_(claim),
54+
protectionPaymentTime_(settlesAccrual ? atDefault : atPeriodEnd), claim_(claim),
5655
protectionStart_(protectionStart == Null<Date>() ? schedule[0] : protectionStart),
57-
tradeDate_(tradeDate), cashSettlementDays_(cashSettlementDays) {
56+
tradeDate_(tradeDate), cashSettlementDays_(cashSettlementDays),
57+
rebatesAccrual_(rebatesAccrual) {
5858

59-
init(schedule, convention, dayCounter, lastPeriodDayCounter, rebatesAccrual);
59+
init(dayCounter, lastPeriodDayCounter);
6060
}
6161

6262
CreditDefaultSwap::CreditDefaultSwap(Protection::Side side,
@@ -75,15 +75,15 @@ namespace QuantLib {
7575
bool rebatesAccrual,
7676
const Date& tradeDate,
7777
Natural cashSettlementDays)
78-
: side_(side), notional_(notional), upfront_(upfront),
79-
runningSpread_(runningSpread), settlesAccrual_(settlesAccrual),
78+
: side_(side), notional_(notional), upfront_(upfront), runningSpread_(runningSpread),
79+
schedule_(schedule), paymentConvention_(convention), settlesAccrual_(settlesAccrual),
8080
paysAtDefaultTime_(paysAtDefaultTime),
81-
protectionPaymentTime_(settlesAccrual ? atDefault : atPeriodEnd),
82-
claim_(claim),
81+
protectionPaymentTime_(settlesAccrual ? atDefault : atPeriodEnd), claim_(claim),
8382
protectionStart_(protectionStart == Null<Date>() ? schedule[0] : protectionStart),
84-
tradeDate_(tradeDate), cashSettlementDays_(cashSettlementDays) {
83+
tradeDate_(tradeDate), cashSettlementDays_(cashSettlementDays),
84+
rebatesAccrual_(rebatesAccrual) {
8585

86-
init(schedule, convention, dayCounter, lastPeriodDayCounter, rebatesAccrual, upfrontDate);
86+
init(dayCounter, lastPeriodDayCounter, upfrontDate);
8787
}
8888

8989
CreditDefaultSwap::CreditDefaultSwap(Protection::Side side,
@@ -100,13 +100,15 @@ namespace QuantLib {
100100
bool rebatesAccrual,
101101
const Date& tradeDate,
102102
Natural cashSettlementDays)
103-
: side_(side), notional_(notional), upfront_(boost::none), runningSpread_(spread), settlesAccrual_(settlesAccrual),
103+
: side_(side), notional_(notional), upfront_(boost::none), runningSpread_(spread),
104+
schedule_(schedule), paymentConvention_(convention), settlesAccrual_(settlesAccrual),
104105
paysAtDefaultTime_(protectionPaymentTime == atDefault ? true : false),
105106
protectionPaymentTime_(protectionPaymentTime), claim_(claim),
106-
protectionStart_(protectionStart == Date() ? schedule[0] : protectionStart), tradeDate_(tradeDate),
107-
cashSettlementDays_(cashSettlementDays) {
107+
protectionStart_(protectionStart == Date() ? schedule[0] : protectionStart),
108+
tradeDate_(tradeDate), cashSettlementDays_(cashSettlementDays),
109+
rebatesAccrual_(rebatesAccrual) {
108110

109-
init(schedule, convention, dayCounter, lastPeriodDayCounter, rebatesAccrual);
111+
init(dayCounter, lastPeriodDayCounter);
110112
}
111113

112114
CreditDefaultSwap::CreditDefaultSwap(Protection::Side side,
@@ -126,13 +128,14 @@ namespace QuantLib {
126128
const Date& tradeDate,
127129
Natural cashSettlementDays)
128130
: side_(side), notional_(notional), upfront_(upfront), runningSpread_(runningSpread),
129-
settlesAccrual_(settlesAccrual),
131+
schedule_(schedule), paymentConvention_(convention), settlesAccrual_(settlesAccrual),
130132
paysAtDefaultTime_(protectionPaymentTime == atDefault ? true : false),
131133
protectionPaymentTime_(protectionPaymentTime), claim_(claim),
132-
protectionStart_(protectionStart == Date() ? schedule[0] : protectionStart), tradeDate_(tradeDate),
133-
cashSettlementDays_(cashSettlementDays) {
134+
protectionStart_(protectionStart == Date() ? schedule[0] : protectionStart),
135+
tradeDate_(tradeDate), cashSettlementDays_(cashSettlementDays),
136+
rebatesAccrual_(rebatesAccrual) {
134137

135-
init(schedule, convention, dayCounter, lastPeriodDayCounter, rebatesAccrual, upfrontDate);
138+
init(dayCounter, lastPeriodDayCounter, upfrontDate);
136139
}
137140

138141
CreditDefaultSwap::CreditDefaultSwap(Protection::Side side,
@@ -150,13 +153,15 @@ namespace QuantLib {
150153
bool rebatesAccrual,
151154
const Date& tradeDate,
152155
Natural cashSettlementDays)
153-
: side_(side), notional_(notional), upfront_(boost::none), runningSpread_(spread), settlesAccrual_(settlesAccrual),
156+
: side_(side), notional_(notional), upfront_(boost::none), runningSpread_(spread),
157+
schedule_(schedule), paymentConvention_(convention), settlesAccrual_(settlesAccrual),
154158
paysAtDefaultTime_(protectionPaymentTime == atDefault ? true : false),
155159
protectionPaymentTime_(protectionPaymentTime), claim_(claim), leg_(amortized_leg),
156-
protectionStart_(protectionStart == Date() ? schedule[0] : protectionStart), tradeDate_(tradeDate),
157-
cashSettlementDays_(cashSettlementDays) {
160+
protectionStart_(protectionStart == Date() ? schedule[0] : protectionStart),
161+
tradeDate_(tradeDate), cashSettlementDays_(cashSettlementDays),
162+
rebatesAccrual_(rebatesAccrual) {
158163

159-
init(schedule, convention, dayCounter, lastPeriodDayCounter, rebatesAccrual);
164+
init(dayCounter, lastPeriodDayCounter);
160165
}
161166

162167
CreditDefaultSwap::CreditDefaultSwap(Protection::Side side,
@@ -177,82 +182,89 @@ namespace QuantLib {
177182
const Date& tradeDate,
178183
Natural cashSettlementDays)
179184
: side_(side), notional_(notional), upfront_(upfront), runningSpread_(runningSpread),
180-
settlesAccrual_(settlesAccrual),
185+
schedule_(schedule), paymentConvention_(convention), settlesAccrual_(settlesAccrual),
181186
paysAtDefaultTime_(protectionPaymentTime == atDefault ? true : false),
182-
protectionPaymentTime_(protectionPaymentTime), claim_(claim),
183-
leg_(amortized_leg), protectionStart_(protectionStart == Date() ? schedule[0] : protectionStart),
184-
tradeDate_(tradeDate), cashSettlementDays_(cashSettlementDays) {
187+
protectionPaymentTime_(protectionPaymentTime), claim_(claim), leg_(amortized_leg),
188+
protectionStart_(protectionStart == Date() ? schedule[0] : protectionStart),
189+
tradeDate_(tradeDate), cashSettlementDays_(cashSettlementDays),
190+
rebatesAccrual_(rebatesAccrual) {
185191

186-
init(schedule, convention, dayCounter, lastPeriodDayCounter, rebatesAccrual, upfrontDate);
192+
init(dayCounter, lastPeriodDayCounter, upfrontDate);
187193
}
188194

189195

190-
void CreditDefaultSwap::init(const Schedule& schedule, BusinessDayConvention paymentConvention,
191-
const DayCounter& dayCounter, const DayCounter& lastPeriodDayCounter,
192-
bool rebatesAccrual, const Date& upfrontDate) {
196+
void CreditDefaultSwap::init(const DayCounter& dayCounter,
197+
const DayCounter& lastPeriodDayCounter,
198+
const Date& upfrontDate) {
193199

194-
QL_REQUIRE(!schedule.empty(), "CreditDefaultSwap needs a non-empty schedule.");
200+
QL_REQUIRE(!schedule_.empty(), "CreditDefaultSwap needs a non-empty schedule.");
195201

196-
bool postBigBang = false;
197-
if (schedule.hasRule()) {
198-
DateGeneration::Rule rule = schedule.rule();
199-
postBigBang = rule == DateGeneration::CDS || rule == DateGeneration::CDS2015;
202+
postBigBang_ = false;
203+
if (schedule_.hasRule()) {
204+
DateGeneration::Rule rule = schedule_.rule();
205+
postBigBang_ = rule == DateGeneration::CDS || rule == DateGeneration::CDS2015;
200206
}
201207

202-
if (!postBigBang) {
203-
QL_REQUIRE(protectionStart_ <= schedule[0], "CreditDefaultSwap: protection can not start after accrual");
208+
if (!postBigBang_) {
209+
QL_REQUIRE(protectionStart_ <= schedule_[0], "CreditDefaultSwap: protection can not start after accrual");
204210
}
205211

206212
// If the leg_ has not already been populated via amortised leg ctor, populate it.
207213
if (leg_.empty()) {
208-
leg_ = FixedRateLeg(schedule)
214+
leg_ = FixedRateLeg(schedule_)
209215
.withNotionals(notional_)
210216
.withCouponRates(runningSpread_, dayCounter)
211-
.withPaymentAdjustment(paymentConvention)
217+
.withPaymentAdjustment(paymentConvention_)
212218
.withLastPeriodDayCounter(lastPeriodDayCounter);
213219
}
214220

215221
// Deduce the trade date if not given.
216222
if (tradeDate_ == Date()) {
217-
if (postBigBang) {
223+
if (postBigBang_) {
218224
tradeDate_ = protectionStart_;
219225
} else {
220226
tradeDate_ = protectionStart_ - 1;
221227
}
222228
}
223229

224230
// Deduce the cash settlement date if not given.
225-
Date effectiveUpfrontDate = upfrontDate;
226-
if (effectiveUpfrontDate == Date()) {
227-
effectiveUpfrontDate = schedule.calendar().advance(tradeDate_, cashSettlementDays_, Days, paymentConvention);
231+
effectiveUpfrontDate_ = upfrontDate;
232+
if (effectiveUpfrontDate_ == Date()) {
233+
effectiveUpfrontDate_ = schedule_.calendar().advance(tradeDate_, cashSettlementDays_,
234+
Days, paymentConvention_);
228235
}
229-
QL_REQUIRE(effectiveUpfrontDate >= protectionStart_,
236+
QL_REQUIRE(effectiveUpfrontDate_ >= protectionStart_,
230237
"The cash settlement date must not be before the protection start date.");
231238

232239
// Create the upfront payment, if one is provided.
233240
Real upfrontAmount = 0.0;
234241
if (upfront_)
235242
upfrontAmount = *upfront_ * notional_;
236-
upfrontPayment_ = boost::make_shared<SimpleCashFlow>(upfrontAmount, effectiveUpfrontDate);
237-
243+
upfrontPayment_ = boost::make_shared<SimpleCashFlow>(upfrontAmount, effectiveUpfrontDate_);
244+
238245
// Set the maturity date.
239-
maturity_ = schedule.dates().back();
246+
maturity_ = schedule_.dates().back();
240247

248+
if (!claim_)
249+
claim_ = boost::make_shared<FaceValueClaim>();
250+
registerWith(claim_);
251+
}
252+
253+
void CreditDefaultSwap::performCalculations() const {
241254
// Deal with the accrual rebate. We use the standard conventions for accrual calculation introduced with the
242255
// CDS Big Bang in 2009
243-
if (rebatesAccrual && postBigBang) {
256+
if (rebatesAccrual_ && postBigBang_) {
244257
accrualRebate_ = boost::make_shared<SimpleCashFlow>(
245-
CashFlows::accruedAmount(leg_, leg_.back()->date() == tradeDate_ + 1, tradeDate_ + 1),
246-
effectiveUpfrontDate);
258+
CashFlows::accruedAmount(leg_, leg_.back()->date() == tradeDate_ + 1,
259+
tradeDate_ + 1),
260+
effectiveUpfrontDate_);
247261
Date current = std::max((Date)Settings::instance().evaluationDate(), tradeDate_);
248262
accrualRebateCurrent_ = boost::make_shared<SimpleCashFlow>(
249-
CashFlows::accruedAmount(leg_, false, current + 1),
250-
schedule.calendar().advance(current, cashSettlementDays_, Days, paymentConvention));
263+
CashFlows::accruedAmount(leg_, false, current + 1),
264+
schedule_.calendar().advance(current, cashSettlementDays_, Days,
265+
paymentConvention_));
251266
}
252-
253-
if (!claim_)
254-
claim_ = boost::make_shared<FaceValueClaim>();
255-
registerWith(claim_);
267+
Instrument::performCalculations();
256268
}
257269

258270
Protection::Side CreditDefaultSwap::side() const {
@@ -471,6 +483,7 @@ namespace QuantLib {
471483
Real accuracy,
472484
PricingModel model) const {
473485

486+
calculate();
474487
ext::shared_ptr<SimpleQuote> flatRate = ext::make_shared<SimpleQuote>(0.0);
475488

476489
Handle<DefaultProbabilityTermStructure> probability =
@@ -498,6 +511,7 @@ namespace QuantLib {
498511
const DayCounter& dayCounter,
499512
PricingModel model) const {
500513

514+
calculate();
501515
ext::shared_ptr<SimpleQuote> flatRate = ext::make_shared<SimpleQuote>(0.0);
502516

503517
Handle<DefaultProbabilityTermStructure> probability =
@@ -552,10 +566,12 @@ namespace QuantLib {
552566
}
553567

554568
const ext::shared_ptr<SimpleCashFlow>& CreditDefaultSwap::accrualRebate() const {
569+
calculate();
555570
return accrualRebate_;
556571
}
557572

558573
const ext::shared_ptr<SimpleCashFlow>& CreditDefaultSwap::accrualRebateCurrent() const {
574+
calculate();
559575
return accrualRebateCurrent_;
560576
}
561577

ql/instruments/creditdefaultswap.hpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ namespace QuantLib {
470470
PricingModel model = Midpoint) const;
471471
//@}
472472
protected:
473+
void performCalculations() const override;
473474
//! \name Instrument interface
474475
//@{
475476
void setupExpired() const override;
@@ -485,16 +486,21 @@ namespace QuantLib {
485486
Real notional_;
486487
boost::optional<Rate> upfront_;
487488
Rate runningSpread_;
489+
Schedule schedule_;
490+
BusinessDayConvention paymentConvention_;
488491
bool settlesAccrual_, paysAtDefaultTime_;
489492
ProtectionPaymentTime protectionPaymentTime_;
490493
ext::shared_ptr<Claim> claim_;
491494
Leg leg_;
492495
ext::shared_ptr<SimpleCashFlow> upfrontPayment_;
493-
ext::shared_ptr<SimpleCashFlow> accrualRebate_;
494-
ext::shared_ptr<SimpleCashFlow> accrualRebateCurrent_;
496+
mutable ext::shared_ptr<SimpleCashFlow> accrualRebate_;
497+
mutable ext::shared_ptr<SimpleCashFlow> accrualRebateCurrent_;
495498
Date protectionStart_;
496499
Date tradeDate_;
497500
Natural cashSettlementDays_;
501+
bool rebatesAccrual_;
502+
bool postBigBang_;
503+
Date effectiveUpfrontDate_;
498504
Date maturity_;
499505
// results
500506
mutable Rate fairUpfront_;
@@ -509,8 +515,9 @@ namespace QuantLib {
509515

510516
private:
511517
//! Shared initialisation.
512-
void init(const Schedule& schedule, BusinessDayConvention paymentConvention, const DayCounter& dayCounter,
513-
const DayCounter& lastPeriodDayCounter, bool rebatesAccrual, const Date& upfrontDate = Date());
518+
void init(const DayCounter& dayCounter,
519+
const DayCounter& lastPeriodDayCounter,
520+
const Date& upfrontDate = Date());
514521
};
515522

516523

test-suite/creditdefaultswap.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,12 @@ void CreditDefaultSwapTest::testIsdaEngine() {
688688

689689
ext::shared_ptr<CreditDefaultSwap> quotedTrade =
690690
MakeCreditDefaultSwap(termDate, spread).withNominal(10000000.);
691+
quotedTrade->setPricingEngine(boost::make_shared<MidPointCdsEngine>(
692+
Handle<DefaultProbabilityTermStructure>(boost::make_shared<FlatHazardRate>(
693+
0, NullCalendar(), 0.0, Actual365Fixed())),
694+
0.0,
695+
Handle<YieldTermStructure>(boost::make_shared<FlatForward>(
696+
0, NullCalendar(), 0.0, Actual365Fixed()))));
691697

692698
Rate h = quotedTrade->impliedHazardRate(0., discountCurve, Actual365Fixed(),
693699
recovery, 1e-10, CreditDefaultSwap::ISDA);
@@ -766,6 +772,12 @@ void CreditDefaultSwapTest::testAccrualRebateAmounts() {
766772
Settings::instance().evaluationDate() = input.first;
767773
CreditDefaultSwap cds = MakeCreditDefaultSwap(maturity, spread)
768774
.withNominal(notional);
775+
cds.setPricingEngine(boost::make_shared<MidPointCdsEngine>(
776+
Handle<DefaultProbabilityTermStructure>(
777+
boost::make_shared<FlatHazardRate>(0, NullCalendar(), 0.0, Actual365Fixed())),
778+
0.0,
779+
Handle<YieldTermStructure>(
780+
boost::make_shared<FlatForward>(0, NullCalendar(), 0.0, Actual365Fixed()))));
769781
BOOST_TEST_MESSAGE("asof " << io::iso_date(input.first)
770782
<< " expected " << std::fixed << std::setprecision(4) << input.second
771783
<< " calculated " << cds.accrualRebate()->amount());
@@ -838,6 +850,12 @@ void CreditDefaultSwapTest::testIsdaCalculatorReconcileSingleQuote ()
838850

839851
ext::shared_ptr<CreditDefaultSwap> quotedTrade =
840852
MakeCreditDefaultSwap(instrumentMaturity, conventionalSpread).withNominal(nominal);
853+
quotedTrade->setPricingEngine(boost::make_shared<MidPointCdsEngine>(
854+
Handle<DefaultProbabilityTermStructure>(
855+
boost::make_shared<FlatHazardRate>(0, NullCalendar(), 0.0, Actual365Fixed())),
856+
0.0,
857+
Handle<YieldTermStructure>(
858+
boost::make_shared<FlatForward>(0, NullCalendar(), 0.0, Actual365Fixed()))));
841859

842860
Rate h = quotedTrade->impliedHazardRate(0., discountCurve, Actual365Fixed(),
843861
recovery, 1e-10, CreditDefaultSwap::ISDA);
@@ -952,6 +970,12 @@ void CreditDefaultSwapTest::testIsdaCalculatorReconcileSingleWithIssueDateInTheP
952970
ext::shared_ptr<CreditDefaultSwap> quotedTrade =
953971
MakeCreditDefaultSwap(instrumentMaturity, conventionalSpread)
954972
.withNominal(nominal);
973+
quotedTrade->setPricingEngine(boost::make_shared<MidPointCdsEngine>(
974+
Handle<DefaultProbabilityTermStructure>(
975+
boost::make_shared<FlatHazardRate>(0, NullCalendar(), 0.0, Actual365Fixed())),
976+
0.0,
977+
Handle<YieldTermStructure>(
978+
boost::make_shared<FlatForward>(0, NullCalendar(), 0.0, Actual365Fixed()))));
955979

956980
Rate h = quotedTrade->impliedHazardRate(0., discountCurve, Actual365Fixed(),
957981
recovery, 1e-10, CreditDefaultSwap::ISDA);

0 commit comments

Comments
 (0)