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

source code of quantlib/ql/pricingengines/credit/midpointcdsengine.cpp