/src/quantlib/ql/pricingengines/credit/integralcdsengine.cpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2008 Roland Lichters |
5 | | Copyright (C) 2008, 2009 StatPro Italia srl |
6 | | Copyright (C) 2009 Jose Aparicio |
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 | | <https://www.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/instruments/claim.hpp> |
24 | | #include <ql/pricingengines/credit/integralcdsengine.hpp> |
25 | | #include <ql/termstructures/yieldtermstructure.hpp> |
26 | | #include <ql/optional.hpp> |
27 | | #include <utility> |
28 | | |
29 | | namespace QuantLib { |
30 | | |
31 | | IntegralCdsEngine::IntegralCdsEngine(const Period& step, |
32 | | Handle<DefaultProbabilityTermStructure> probability, |
33 | | Real recoveryRate, |
34 | | Handle<YieldTermStructure> discountCurve, |
35 | | const ext::optional<bool>& includeSettlementDateFlows) |
36 | 0 | : integrationStep_(step), probability_(std::move(probability)), recoveryRate_(recoveryRate), |
37 | 0 | discountCurve_(std::move(discountCurve)), |
38 | 0 | includeSettlementDateFlows_(includeSettlementDateFlows) { |
39 | 0 | registerWith(probability_); |
40 | 0 | registerWith(discountCurve_); |
41 | 0 | } |
42 | | |
43 | 0 | void IntegralCdsEngine::calculate() const { |
44 | 0 | QL_REQUIRE(integrationStep_ != Period(), |
45 | 0 | "null period set"); |
46 | 0 | QL_REQUIRE(!discountCurve_.empty(), |
47 | 0 | "no discount term structure set"); |
48 | 0 | QL_REQUIRE(!probability_.empty(), |
49 | 0 | "no probability term structure set"); |
50 | | |
51 | 0 | Date today = Settings::instance().evaluationDate(); |
52 | 0 | Date settlementDate = discountCurve_->referenceDate(); |
53 | | |
54 | | // Upfront amount. |
55 | 0 | Real upfPVO1 = 0.0; |
56 | 0 | results_.upfrontNPV = 0.0; |
57 | 0 | if (!arguments_.upfrontPayment->hasOccurred( |
58 | 0 | settlementDate, includeSettlementDateFlows_)) { |
59 | 0 | upfPVO1 = discountCurve_->discount(arguments_.upfrontPayment->date()); |
60 | 0 | results_.upfrontNPV = upfPVO1 * arguments_.upfrontPayment->amount(); |
61 | 0 | } |
62 | | |
63 | | // Accrual rebate. |
64 | 0 | results_.accrualRebateNPV = 0.; |
65 | | // NOLINTNEXTLINE(readability-implicit-bool-conversion) |
66 | 0 | if (arguments_.accrualRebate && |
67 | 0 | !arguments_.accrualRebate->hasOccurred(settlementDate, includeSettlementDateFlows_)) { |
68 | 0 | results_.accrualRebateNPV = |
69 | 0 | discountCurve_->discount(arguments_.accrualRebate->date()) * |
70 | 0 | arguments_.accrualRebate->amount(); |
71 | 0 | } |
72 | |
|
73 | 0 | results_.couponLegNPV = 0.0; |
74 | 0 | results_.defaultLegNPV = 0.0; |
75 | 0 | for (Size i=0; i<arguments_.leg.size(); ++i) { |
76 | 0 | if (arguments_.leg[i]->hasOccurred(settlementDate, |
77 | 0 | includeSettlementDateFlows_)) |
78 | 0 | continue; |
79 | | |
80 | 0 | ext::shared_ptr<FixedRateCoupon> coupon = |
81 | 0 | ext::dynamic_pointer_cast<FixedRateCoupon>(arguments_.leg[i]); |
82 | | |
83 | | // In order to avoid a few switches, we calculate the NPV |
84 | | // of both legs as a positive quantity. We'll give them |
85 | | // the right sign at the end. |
86 | |
|
87 | 0 | Date paymentDate = coupon->date(), |
88 | 0 | startDate = (i == 0 ? arguments_.protectionStart : |
89 | 0 | coupon->accrualStartDate()), |
90 | 0 | endDate = coupon->accrualEndDate(); |
91 | 0 | Date effectiveStartDate = |
92 | 0 | (startDate <= today && today <= endDate) ? today : startDate; |
93 | 0 | Real couponAmount = coupon->amount(); |
94 | |
|
95 | 0 | Probability S = probability_->survivalProbability(paymentDate); |
96 | | |
97 | | // On one side, we add the fixed rate payments in case of |
98 | | // survival. |
99 | 0 | results_.couponLegNPV += |
100 | 0 | S * couponAmount * discountCurve_->discount(paymentDate); |
101 | | |
102 | | // On the other side, we add the payment (and possibly the |
103 | | // accrual) in case of default. |
104 | |
|
105 | 0 | Period step = integrationStep_; |
106 | 0 | Date d0 = effectiveStartDate; |
107 | 0 | Date d1 = std::min(d0 + step, endDate); |
108 | 0 | Probability P0 = probability_->defaultProbability(d0); |
109 | 0 | DiscountFactor endDiscount = discountCurve_->discount(paymentDate); |
110 | 0 | do { |
111 | 0 | DiscountFactor B = |
112 | 0 | arguments_.paysAtDefaultTime ? |
113 | 0 | discountCurve_->discount(d1) : |
114 | 0 | endDiscount; |
115 | |
|
116 | 0 | Probability P1 = probability_->defaultProbability(d1); |
117 | 0 | Probability dP = P1 - P0; |
118 | | |
119 | | // accrual... |
120 | 0 | if (arguments_.settlesAccrual) { |
121 | 0 | if (arguments_.paysAtDefaultTime) |
122 | 0 | results_.couponLegNPV += |
123 | 0 | coupon->accruedAmount(d1) * B * dP; |
124 | 0 | else |
125 | 0 | results_.couponLegNPV += |
126 | 0 | couponAmount * B * dP; |
127 | 0 | } |
128 | | |
129 | | // ...and claim. |
130 | 0 | Real claim = arguments_.claim->amount(d1, |
131 | 0 | arguments_.notional, |
132 | 0 | recoveryRate_); |
133 | 0 | results_.defaultLegNPV += claim * B * dP; |
134 | | |
135 | | // setup for next time around the loop |
136 | 0 | P0 = P1; |
137 | 0 | d0 = d1; |
138 | 0 | d1 = std::min(d0 + step, endDate); |
139 | 0 | } while (d0 < endDate); |
140 | 0 | } |
141 | |
|
142 | 0 | Real upfrontSign = 1.0; |
143 | 0 | switch (arguments_.side) { |
144 | 0 | case Protection::Seller: |
145 | 0 | results_.defaultLegNPV *= -1.0; |
146 | 0 | results_.accrualRebateNPV *= -1.0; |
147 | 0 | break; |
148 | 0 | case Protection::Buyer: |
149 | 0 | results_.couponLegNPV *= -1.0; |
150 | 0 | results_.upfrontNPV *= -1.0; |
151 | 0 | upfrontSign = -1.0; |
152 | 0 | break; |
153 | 0 | default: |
154 | 0 | QL_FAIL("unknown protection side"); |
155 | 0 | } |
156 | | |
157 | 0 | results_.value = |
158 | 0 | results_.defaultLegNPV + results_.couponLegNPV + |
159 | 0 | results_.upfrontNPV + results_.accrualRebateNPV; |
160 | 0 | results_.errorEstimate = Null<Real>(); |
161 | |
|
162 | 0 | if (results_.couponLegNPV != 0.0) { |
163 | 0 | results_.fairSpread = |
164 | 0 | -results_.defaultLegNPV*arguments_.spread |
165 | 0 | /(results_.couponLegNPV + results_.accrualRebateNPV); |
166 | 0 | } else { |
167 | 0 | results_.fairSpread = Null<Rate>(); |
168 | 0 | } |
169 | |
|
170 | 0 | if (upfPVO1 > 0.0) { |
171 | 0 | results_.fairUpfront = |
172 | 0 | -upfrontSign*(results_.defaultLegNPV + results_.couponLegNPV + |
173 | 0 | results_.accrualRebateNPV) |
174 | 0 | / (upfPVO1 * arguments_.notional); |
175 | 0 | } else { |
176 | 0 | results_.fairUpfront = Null<Rate>(); |
177 | 0 | } |
178 | |
|
179 | 0 | static const Rate basisPoint = 1.0e-4; |
180 | |
|
181 | 0 | if (arguments_.spread != 0.0) { |
182 | 0 | results_.couponLegBPS = |
183 | 0 | results_.couponLegNPV*basisPoint/arguments_.spread; |
184 | 0 | } else { |
185 | 0 | results_.couponLegBPS = Null<Rate>(); |
186 | 0 | } |
187 | | |
188 | | // NOLINTNEXTLINE(readability-implicit-bool-conversion) |
189 | 0 | if (arguments_.upfront && *arguments_.upfront != 0.0) { |
190 | 0 | results_.upfrontBPS = |
191 | 0 | results_.upfrontNPV*basisPoint/(*arguments_.upfront); |
192 | 0 | } else { |
193 | 0 | results_.upfrontBPS = Null<Rate>(); |
194 | 0 | } |
195 | 0 | } |
196 | | |
197 | | } |