23#include <ql/cashflows/coupon.hpp>
24#include <ql/timegrid.hpp>
33 const Handle<YieldTermStructure>& irCurve,
const Handle<DefaultProbabilityTermStructure>& creditCurve,
34 const Handle<Quote>& marketRecovery,
const Size timeStepsPerYear,
const bool generateAdditionalResults)
35 : irCurve_(irCurve), creditCurve_(creditCurve), marketRecovery_(marketRecovery),
36 timeStepsPerYear_(timeStepsPerYear), generateAdditionalResults_(generateAdditionalResults) {
43 QL_REQUIRE(!
irCurve_.empty(),
"DiscountingCreditLinkedSwapEngine::calculate(): ir curve is empty");
44 QL_REQUIRE(!
creditCurve_.empty(),
"DiscountingCreditLinkedSwapEngine::calculate(): ir curve is empty");
45 QL_REQUIRE(!
marketRecovery_.empty(),
"DiscountingCreditLinkedSwapEngine::calculate(): market recovery is empty");
47 Date today = Settings::instance().evaluationDate();
48 Real npv_independent = 0.0, npv_contingent = 0.0, default_accrual_contingent = 0.0, npv_default_payments = 0.0,
49 npv_recovery_payments = 0.0;
51 std::vector<QuantExt::CashFlowResults> cfResults;
55 for (Size i = 0; i <
arguments_.legs.size(); ++i) {
57 Real multiplier = (
arguments_.legPayers[i] ? -1.0 : 1.0);
62 if (c->date() <= today)
64 npv_independent += multiplier * c->amount() *
irCurve_->discount(c->date());
75 if (c->date() <= today)
77 Real dsc =
irCurve_->discount(c->date());
79 npv_contingent += multiplier * c->amount() * dsc *
P;
83 cfResults.back().amount *=
P;
84 cfResults.back().discountFactor = dsc;
85 cfResults.back().presentValue = cfResults.back().amount * dsc;
89 if (
auto cpn = QuantLib::ext::dynamic_pointer_cast<Coupon>(c)) {
90 Date start = std::max(cpn->accrualStartDate(), today);
91 Date end = cpn->accrualEndDate();
93 Date mid = Date((start.serialNumber() + end.serialNumber()) / 2);
96 default_accrual_contingent += multiplier * cpn->accruedAmount(mid) *
P * dsc;
100 cfResults.back().amount = multiplier * cpn->accruedAmount(mid) *
P;
101 cfResults.back().accrualStartDate = cpn->accrualStartDate();
102 cfResults.back().accrualEndDate = cpn->accrualEndDate();
103 cfResults.back().payDate = mid;
104 cfResults.back().currency =
arguments_.currency.code();
105 cfResults.back().legNumber = 2;
106 cfResults.back().type =
"CreditLinkedDefaultAccrual";
107 cfResults.back().discountFactor = dsc;
108 cfResults.back().presentValue = cfResults.back().amount * dsc;
124 std::set<Real> defaultPaymentTimes, recoveryPaymentTimes, allTimes;
125 for (Size i = 0; i <
arguments_.legs.size(); ++i) {
128 if (c->date() > today) {
129 defaultPaymentTimes.insert(
creditCurve_->timeFromReference(c->date()));
134 if (c->date() > today) {
135 recoveryPaymentTimes.insert(
creditCurve_->timeFromReference(c->date()));
141 std::vector<Real> defaultPaymentAmounts(defaultPaymentTimes.size() + 1, 0.0),
142 recoveryPaymentAmounts(recoveryPaymentTimes.size() + 1, 0.0);
143 std::vector<Date> defaultPeriodEndDates(defaultPaymentTimes.size() + 1, Null<Date>()),
144 recoveryPeriodEndDates(recoveryPaymentTimes.size() + 1, Null<Date>());
146 for (Size i = 0; i <
arguments_.legs.size(); ++i) {
147 Real multiplier = (
arguments_.legPayers[i] ? -1.0 : 1.0);
150 if (c->date() > today) {
151 Size index = std::distance(defaultPaymentTimes.begin(),
152 defaultPaymentTimes.find(
creditCurve_->timeFromReference(c->date())));
154 index < defaultPaymentTimes.size(),
155 "DiscountingCreditLinkedSwapEngine: internal error: default payment times index out of range");
156 defaultPaymentAmounts[index] += c->amount() * multiplier;
157 defaultPeriodEndDates[index] = c->date();
162 if (c->date() > today) {
163 Size index = std::distance(recoveryPaymentTimes.begin(),
164 recoveryPaymentTimes.find(
creditCurve_->timeFromReference(c->date())));
166 index < recoveryPaymentTimes.size(),
167 "DiscountingCreditLinkedSwapEngine: internal error: recovery payment times index out of range");
168 recoveryPaymentAmounts[index] += c->amount() * multiplier;
169 recoveryPeriodEndDates[index] = c->date();
175 allTimes.insert(defaultPaymentTimes.begin(), defaultPaymentTimes.end());
176 allTimes.insert(recoveryPaymentTimes.begin(), recoveryPaymentTimes.end());
178 if (!allTimes.empty()) {
180 allTimes.begin(), allTimes.end(),
181 std::max<Size>(1,
static_cast<Size
>(0.5 +
static_cast<Real
>(
timeStepsPerYear_) * (*allTimes.rbegin()))));
186 for (Size i = 0; i < grid.size() - 1; ++i) {
188 Real t1 = grid[i + 1];
190 auto defaultPayTime = defaultPaymentTimes.lower_bound(t1);
191 auto recoveryPayTime = recoveryPaymentTimes.lower_bound(t1);
192 Size index_d = std::distance(defaultPaymentTimes.begin(), defaultPayTime);
193 Size index_r = std::distance(recoveryPaymentTimes.begin(), recoveryPayTime);
197 Real dscDefault = 0.0, dscRecovery = 0.0;
198 Date payDateDefault, payDateRecovery;
199 if (
arguments_.defaultPaymentTime == QuantExt::CreditDefaultSwap::ProtectionPaymentTime::atDefault) {
200 dscDefault = dscRecovery =
irCurve_->discount(0.5 * (t0 + t1));
202 if (defaultPayTime != defaultPaymentTimes.end()) {
206 start = defaultPeriodEndDates[index_d - 1];
207 ts = *(std::next(defaultPayTime, -1));
209 Date end = defaultPeriodEndDates[index_d];
211 start +
static_cast<Size
>(0.5 +
static_cast<Real
>(end.serialNumber() - start.serialNumber()) *
212 (t1 - ts) / (*defaultPayTime - ts));
214 if (recoveryPayTime != recoveryPaymentTimes.end()) {
218 start = recoveryPeriodEndDates[index_r - 1];
219 ts = *(std::next(recoveryPayTime, -1));
221 Date end = recoveryPeriodEndDates[index_r];
223 start +
static_cast<Size
>(0.5 +
static_cast<Real
>(end.serialNumber() - start.serialNumber()) *
224 (t1 - ts) / (*recoveryPayTime - ts));
227 QuantExt::CreditDefaultSwap::ProtectionPaymentTime::atPeriodEnd) {
228 if (defaultPayTime != defaultPaymentTimes.end()) {
229 dscDefault =
irCurve_->discount(*defaultPayTime);
230 payDateDefault = defaultPeriodEndDates[index_d];
232 if (recoveryPayTime != recoveryPaymentTimes.end()) {
233 dscRecovery =
irCurve_->discount(*recoveryPayTime);
234 payDateRecovery = recoveryPeriodEndDates[index_r];
237 QuantExt::CreditDefaultSwap::ProtectionPaymentTime::atMaturity) {
239 payDateDefault = payDateRecovery =
arguments_.maturityDate;
241 QL_FAIL(
"DiscountingCreditLinkedSwapEngine: internal error: unhandled default payment time");
244 npv_default_payments += defaultPaymentAmounts[index_d] *
P * (1.0 - rr) * dscDefault;
245 npv_recovery_payments += recoveryPaymentAmounts[index_r] *
P * rr * dscRecovery;
248 if (!
close_enough(defaultPaymentAmounts[index_d], 0.0)) {
250 cfResults.back().amount = defaultPaymentAmounts[index_d] *
P * (1.0 - rr);
251 cfResults.back().payDate = payDateDefault;
252 cfResults.back().currency =
arguments_.currency.code();
253 cfResults.back().legNumber = 3;
254 cfResults.back().type =
"DefaultPayment";
255 cfResults.back().discountFactor = dscDefault;
256 cfResults.back().presentValue = cfResults.back().discountFactor * cfResults.back().amount;
258 if (!
close_enough(recoveryPaymentAmounts[index_r], 0.0)) {
260 cfResults.back().amount = recoveryPaymentAmounts[index_r] *
P * rr;
261 cfResults.back().payDate = payDateRecovery;
262 cfResults.back().currency =
arguments_.currency.code();
263 cfResults.back().legNumber = 3;
264 cfResults.back().type =
"RecoveryPayment";
265 cfResults.back().discountFactor = dscRecovery;
266 cfResults.back().presentValue = cfResults.back().discountFactor * cfResults.back().amount;
275 npv_independent + npv_contingent + default_accrual_contingent + npv_default_payments + npv_recovery_payments;
278 results_.additionalResults[
"npv_independent"] = npv_independent;
279 results_.additionalResults[
"npv_credit_linked"] = npv_contingent;
280 results_.additionalResults[
"npv_credit_linked_accruals"] = default_accrual_contingent;
281 results_.additionalResults[
"npv_default_payments"] = npv_default_payments;
282 results_.additionalResults[
"npv_recovery_payments"] = npv_recovery_payments;
283 results_.additionalResults[
"cashFlowResults"] = cfResults;
class holding cashflow-related results
const Instrument::results * results_
Handle< Quote > marketRecovery_
Handle< DefaultProbabilityTermStructure > creditCurve_
void calculate() const override
Handle< YieldTermStructure > irCurve_
DiscountingCreditLinkedSwapEngine(const Handle< YieldTermStructure > &irCurve, const Handle< DefaultProbabilityTermStructure > &creditCurve, const Handle< Quote > &marketRecovery, const Size timeStepsPerYear, const bool generateAdditionalResults)
bool generateAdditionalResults_
credit linked swap pricing engine
const P2_< E1, E2 > P(const E1 &e1, const E2 &e2)
CashFlowResults standardCashFlowResults(const QuantLib::ext::shared_ptr< CashFlow > &c, const Real multiplier, const std::string &type, const Size legNo, const Currency ¤cy, const Handle< YieldTermStructure > &discountCurve)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
Swap::arguments * arguments_