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