Skip to content

Commit

Permalink
Fix sign of fair upfront in ISDA engine for CDS (#1492)
Browse files Browse the repository at this point in the history
  • Loading branch information
lballabio committed Oct 5, 2022
2 parents d79ef32 + a3b603c commit b381364
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 49 deletions.
35 changes: 20 additions & 15 deletions ql/instruments/creditdefaultswap.cpp
Expand Up @@ -84,8 +84,8 @@ namespace QuantLib {
}

void CreditDefaultSwap::init(const Schedule& schedule, BusinessDayConvention paymentConvention,
const DayCounter& dayCounter, const DayCounter& lastPeriodDayCounter,
bool rebatesAccrual, const Date& upfrontDate) {
const DayCounter& dayCounter, const DayCounter& lastPeriodDayCounter,
bool rebatesAccrual, const Date& upfrontDate) {

QL_REQUIRE(!schedule.empty(), "CreditDefaultSwap needs a non-empty schedule.");

Expand Down Expand Up @@ -120,8 +120,8 @@ namespace QuantLib {
effectiveUpfrontDate = schedule.calendar().advance(tradeDate_,
cashSettlementDays_, Days, paymentConvention);
}
QL_REQUIRE(effectiveUpfrontDate >= protectionStart_, "The cash settlement date must not " <<
"be before the protection start date.");
QL_REQUIRE(effectiveUpfrontDate >= protectionStart_,
"The cash settlement date must not be before the protection start date.");

// Create the upfront payment, if one is provided.
Real upfrontAmount = 0.0;
Expand All @@ -142,20 +142,25 @@ namespace QuantLib {
if (tradeDate_ >= schedule.dates().front()) {
for (Size i = 0; i < leg_.size(); ++i) {
const ext::shared_ptr<CashFlow>& cf = leg_[i];
if (refDate < cf->date()) {
// Calculate the accrual. The most likely scenario.
ext::shared_ptr<FixedRateCoupon> frc = ext::dynamic_pointer_cast<FixedRateCoupon>(cf);
rebateAmount = frc->accruedAmount(refDate);
break;
} else if (refDate == cf->date() && i < leg_.size() - 1) {
// If not the last coupon and trade date + 1 is the next coupon payment date,
// the accrual is 0 so do nothing.
if (refDate > cf->date()) {
// This coupon is in the past; check the next one
continue;
} else if (refDate == cf->date()) {
// This coupon pays at the reference date.
// If it's not the last coupon, the accrual is 0 so do nothing.
if (i < leg_.size() - 1)
rebateAmount = 0.0;
else {
// On last coupon
ext::shared_ptr<FixedRateCoupon> frc = ext::dynamic_pointer_cast<FixedRateCoupon>(cf);
rebateAmount = frc->amount();
}
break;
} else {
// Must have trade date + 1 >= last coupon's payment date. '>' here probably does not make
// sense - should possibly have an exception above if trade date >= last coupon's date.
// This coupon pays in the future, and is the first coupon to do so (since they're sorted).
// Calculate the accrual and skip further coupons
ext::shared_ptr<FixedRateCoupon> frc = ext::dynamic_pointer_cast<FixedRateCoupon>(cf);
rebateAmount = frc->amount();
rebateAmount = frc->accruedAmount(refDate);
break;
}
}
Expand Down
8 changes: 7 additions & 1 deletion ql/instruments/makecds.cpp
Expand Up @@ -46,7 +46,7 @@ namespace QuantLib {

MakeCreditDefaultSwap::operator ext::shared_ptr<CreditDefaultSwap>() const {

Date tradeDate = Settings::instance().evaluationDate();
Date tradeDate = (tradeDate_ != Null<Date>()) ? tradeDate_ : Settings::instance().evaluationDate();
Date upfrontDate = WeekendsOnly().advance(tradeDate, cashSettlementDays_, Days);

Date protectionStart;
Expand Down Expand Up @@ -131,4 +131,10 @@ namespace QuantLib {
engine_ = engine;
return *this;
}

MakeCreditDefaultSwap& MakeCreditDefaultSwap::withTradeDate(const Date& tradeDate) {
tradeDate_ = tradeDate;
return *this;
}

}
3 changes: 3 additions & 0 deletions ql/instruments/makecds.hpp
Expand Up @@ -53,6 +53,8 @@ namespace QuantLib {

MakeCreditDefaultSwap& withPricingEngine(const ext::shared_ptr<PricingEngine>&);

MakeCreditDefaultSwap& withTradeDate(const Date& tradeDate);

private:
Protection::Side side_;
Real nominal_;
Expand All @@ -65,6 +67,7 @@ namespace QuantLib {
DayCounter lastPeriodDayCounter_;
DateGeneration::Rule rule_;
Natural cashSettlementDays_;
Date tradeDate_;

ext::shared_ptr<PricingEngine> engine_;
};
Expand Down
15 changes: 10 additions & 5 deletions ql/pricingengines/credit/isdacdsengine.cpp
Expand Up @@ -307,14 +307,19 @@ namespace QuantLib {
arguments_.accrualRebate->amount();
}

Real upfrontSign = Protection::Seller != 0U ? 1.0 : -1.0;

if (arguments_.side == Protection::Seller) {
Real upfrontSign = 1.0;
switch (arguments_.side) {
case Protection::Seller:
results_.defaultLegNPV *= -1.0;
results_.accrualRebateNPV *= -1.0;
} else {
break;
case Protection::Buyer:
results_.couponLegNPV *= -1.0;
results_.upfrontNPV *= -1.0;
results_.upfrontNPV *= -1.0;
upfrontSign = -1.0;
break;
default:
QL_FAIL("unknown protection side");
}

results_.value = results_.defaultLegNPV + results_.couponLegNPV +
Expand Down

0 comments on commit b381364

Please sign in to comment.