Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
indexcdstrancheengine.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2020 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
20
21#ifndef QL_PATCH_SOLARIS
22
23#include <ql/cashflows/fixedratecoupon.hpp>
24#include <ql/termstructures/yieldtermstructure.hpp>
25#include <boost/timer/timer.hpp>
26
27using namespace QuantLib;
28using boost::timer::cpu_timer;
29using std::vector;
30
31namespace QuantExt {
32
33IndexCdsTrancheEngine::IndexCdsTrancheEngine(const Handle<YieldTermStructure>& discountCurve,
34 boost::optional<bool> includeSettlementDateFlows)
35 : discountCurve_(discountCurve),
36 includeSettlementDateFlows_(includeSettlementDateFlows) {
37 registerWith(discountCurve);
38}
39
41
42 cpu_timer timer;
43 timer.start();
44
45 // Upfront premium
46 results_.upfrontPremiumValue = 0.0;
47 Real upfrontPremiumAmount = arguments_.upfrontPayment->amount();
48 if (arguments_.upfrontPayment && !arguments_.upfrontPayment->hasOccurred(
50 results_.upfrontPremiumValue = upfrontPremiumAmount *
51 discountCurve_->discount(arguments_.upfrontPayment->date());
52 }
53
54 // Accrual rebate
55 results_.accrualRebateValue = 0.0;
56 if (arguments_.accrualRebate && !arguments_.accrualRebate->hasOccurred(
58 results_.accrualRebateValue = arguments_.accrualRebate->amount() *
59 discountCurve_->discount(arguments_.accrualRebate->date());
60 }
61
62 // Accrual rebate current
63 results_.accrualRebateCurrentValue = 0.0;
64 if (arguments_.accrualRebateCurrent &&
65 !arguments_.accrualRebateCurrent->hasOccurred(discountCurve_->referenceDate(), includeSettlementDateFlows_)) {
66 results_.accrualRebateCurrentValue =
67 discountCurve_->discount(arguments_.accrualRebateCurrent->date()) * arguments_.accrualRebateCurrent->amount();
68 }
69
70 // Final results, not updated below.
71 // FD TODO: check again when testing tranches with existing losses.
72 const auto& basket = arguments_.basket;
73 QL_REQUIRE(basket, "IndexCdsTrancheEngine expects a non-null basket.");
74 results_.xMin = basket->attachmentAmount();
75 results_.xMax = basket->detachmentAmount();
76 results_.remainingNotional = results_.xMax - results_.xMin;
77
78 // Record the expected tranche loss up to inception and up to the end of each coupon period.
79 // FD TODO: Recheck 0's for past coupons when testing tranches with existing losses. The loss model gives
80 // accumulated losses so we should in theory be able to use these.
81 vector<Real>& etls = results_.expectedTrancheLoss;
82 etls.push_back(0.0);
83
84 // Variables used in the loop below.
85 Date today = Settings::instance().evaluationDate();
86 results_.premiumValue = 0.0;
87 results_.protectionValue = 0.0;
88 Real inceptionTrancheNotional = arguments_.basket->trancheNotional();
89
90 // Value the premium and protection leg.
91 for (Size i = 0; i < arguments_.normalizedLeg.size(); i++) {
92
93 // Zero expected loss on coupon end dates that have already occured.
94 // FD TODO: check again when testing tranches with existing losses.
95 if (arguments_.normalizedLeg[i]->hasOccurred(today)) {
96 etls.push_back(0.0);
97 continue;
98 }
99
100 QuantLib::ext::shared_ptr<Coupon> coupon = QuantLib::ext::dynamic_pointer_cast<Coupon>(arguments_.normalizedLeg[i]);
101 QL_REQUIRE(coupon, "IndexCdsTrancheEngine expects leg to have Coupon cashflow type.");
102
103 // Relevant dates with assumption that future defaults occur at midpoint of (remaining) coupon period.
104 Date paymentDate = coupon->date();
105 Date startDate = std::max(coupon->accrualStartDate(), today);
106 Date endDate = coupon->accrualEndDate();
107 Date defaultDate = startDate + (endDate - startDate) / 2;
108
109 // Expected loss on the tranche up to the end of the current period.
110 Real etl = basket->expectedTrancheLoss(endDate, arguments_.recoveryRate);
111
112 // Update protection leg value
113 results_.protectionValue += discountCurve_->discount(defaultDate) * (etl - etls.back());
114
115 // Update the premium leg value. If settling accruals, which is standard, assume that losses are evenly
116 // distributed over the coupon period, as per Andersen, Sidenius, Basu Nov 2003 paper for example. If not
117 // settling accruals, just use the tranche notional at period end.
118 Real effNtl = 0.0;
119 if (arguments_.settlesAccrual) {
120 effNtl = inceptionTrancheNotional - (etl + etls.back()) / 2.0;
121 } else {
122 effNtl = inceptionTrancheNotional - etl;
123 }
124 results_.premiumValue += (coupon->amount() / inceptionTrancheNotional) *
125 effNtl * discountCurve_->discount(paymentDate);
126
127 // Update the expected tranche loss results vector.
128 etls.push_back(etl);
129 }
130
131 // Apply correct sign to each PV'ed quantity depending on whether buying or selling protection on the tranche.
132 if (arguments_.side == Protection::Buyer) {
133 results_.premiumValue *= -1;
134 results_.upfrontPremiumValue *= -1;
135 upfrontPremiumAmount *= -1;
136 } else {
137 results_.protectionValue *= -1;
138 results_.accrualRebateValue *= -1;
139 results_.accrualRebateCurrentValue *= -1;
140 }
141
142 // Final tranche NPV.
143 results_.value = results_.premiumValue + results_.protectionValue +
144 results_.upfrontPremiumValue + results_.accrualRebateValue;
145
146 // Fair tranche spread.
147 Real fairSpread = 0.0;
148 if (results_.premiumValue != 0.0) {
149 fairSpread = -(results_.protectionValue + results_.upfrontPremiumValue) *
150 arguments_.runningRate / (results_.premiumValue + results_.accrualRebateValue);
151 }
152
153 timer.stop();
154
155 // Populate the additional results.
156 results_.additionalResults["attachment"] = arguments_.basket->attachmentRatio();
157 results_.additionalResults["detachment"] = arguments_.basket->detachmentRatio();
158 results_.additionalResults["fixedRate"] = arguments_.runningRate;
159 results_.additionalResults["fairSpread"] = fairSpread;
160 results_.additionalResults["upfrontPremium"] = upfrontPremiumAmount;
161 Real correlation = arguments_.basket->correlation();
162 if (correlation != Null<Real>())
163 results_.additionalResults["correlation"] = correlation;
164 results_.additionalResults["upfrontPremiumNPV"] = results_.upfrontPremiumValue;
165 results_.additionalResults["premiumLegNPV"] = results_.premiumValue;
166 results_.additionalResults["accrualRebateNPV"] = results_.accrualRebateValue;
167 results_.additionalResults["accrualRebateCurrentNPV"] = results_.accrualRebateCurrentValue;
168
169 results_.additionalResults["protectionLegNPV"] = results_.protectionValue;
170 results_.additionalResults["calculationTime"] = timer.elapsed().wall * 1e-9;
171}
172
173}
174
175#endif
const Instrument::results * results_
Definition: cdsoption.cpp:81
IndexCdsTrancheEngine(const QuantLib::Handle< QuantLib::YieldTermStructure > &discountCurve, boost::optional< bool > includeSettlementDateFlows=boost::none)
boost::optional< bool > includeSettlementDateFlows_
QuantLib::Handle< QuantLib::YieldTermStructure > discountCurve_
Index CDS tranche pricing engine.
Swap::arguments * arguments_