/src/quantlib/ql/pricingengines/swap/cvaswapengine.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) 2015 Jose Aparicio |
5 | | |
6 | | This file is part of QuantLib, a free-software/open-source library |
7 | | for financial quantitative analysts and developers - http://quantlib.org/ |
8 | | |
9 | | QuantLib is free software: you can redistribute it and/or modify it |
10 | | under the terms of the QuantLib license. You should have received a |
11 | | copy of the license along with this program; if not, please email |
12 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
13 | | <http://quantlib.org/license.shtml>. |
14 | | |
15 | | This program is distributed in the hope that it will be useful, but WITHOUT |
16 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
17 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
18 | | */ |
19 | | |
20 | | #include <ql/pricingengines/swap/cvaswapengine.hpp> |
21 | | #include <ql/cashflows/fixedratecoupon.hpp> |
22 | | #include <ql/cashflows/floatingratecoupon.hpp> |
23 | | #include <ql/indexes/iborindex.hpp> |
24 | | #include <ql/instruments/makevanillaswap.hpp> |
25 | | #include <ql/exercise.hpp> |
26 | | #include <ql/pricingengines/swap/discountingswapengine.hpp> |
27 | | #include <ql/termstructures/credit/flathazardrate.hpp> |
28 | | #include <ql/pricingengines/swaption/blackswaptionengine.hpp> |
29 | | #include <ql/time/calendars/nullcalendar.hpp> |
30 | | |
31 | | namespace QuantLib { |
32 | | |
33 | | CounterpartyAdjSwapEngine::CounterpartyAdjSwapEngine( |
34 | | const Handle<YieldTermStructure>& discountCurve, |
35 | | const Handle<PricingEngine>& swaptionEngine, |
36 | | const Handle<DefaultProbabilityTermStructure>& ctptyDTS, |
37 | | Real ctptyRecoveryRate, |
38 | | const Handle<DefaultProbabilityTermStructure>& invstDTS, |
39 | | Real invstRecoveryRate) |
40 | 0 | : baseSwapEngine_(Handle<PricingEngine>( |
41 | 0 | ext::make_shared<DiscountingSwapEngine>(discountCurve))), |
42 | 0 | swaptionletEngine_(swaptionEngine), |
43 | 0 | discountCurve_(discountCurve), |
44 | 0 | defaultTS_(ctptyDTS), |
45 | 0 | ctptyRecoveryRate_(ctptyRecoveryRate), |
46 | 0 | invstDTS_(invstDTS.empty() ? Handle<DefaultProbabilityTermStructure>( |
47 | 0 | ext::make_shared<FlatHazardRate>(0, NullCalendar(), 1.e-12, |
48 | 0 | ctptyDTS->dayCounter()) ) : invstDTS ), |
49 | 0 | invstRecoveryRate_(invstRecoveryRate) |
50 | 0 | { |
51 | 0 | registerWith(discountCurve); |
52 | 0 | registerWith(ctptyDTS); |
53 | 0 | registerWith(invstDTS_); |
54 | 0 | registerWith(swaptionEngine); |
55 | 0 | } |
56 | | |
57 | | CounterpartyAdjSwapEngine::CounterpartyAdjSwapEngine( |
58 | | const Handle<YieldTermStructure>& discountCurve, |
59 | | const Volatility blackVol, |
60 | | const Handle<DefaultProbabilityTermStructure>& ctptyDTS, |
61 | | Real ctptyRecoveryRate, |
62 | | const Handle<DefaultProbabilityTermStructure>& invstDTS, |
63 | | Real invstRecoveryRate) |
64 | 0 | : baseSwapEngine_(Handle<PricingEngine>( |
65 | 0 | ext::make_shared<DiscountingSwapEngine>(discountCurve))), |
66 | 0 | swaptionletEngine_(Handle<PricingEngine>( |
67 | 0 | ext::make_shared<BlackSwaptionEngine>(discountCurve, |
68 | 0 | blackVol))), |
69 | 0 | discountCurve_(discountCurve), |
70 | 0 | defaultTS_(ctptyDTS), |
71 | 0 | ctptyRecoveryRate_(ctptyRecoveryRate), |
72 | 0 | invstDTS_(invstDTS.empty() ? Handle<DefaultProbabilityTermStructure>( |
73 | 0 | ext::make_shared<FlatHazardRate>(0, NullCalendar(), 1.e-12, |
74 | 0 | ctptyDTS->dayCounter()) ) : invstDTS ), |
75 | 0 | invstRecoveryRate_(invstRecoveryRate) |
76 | 0 | { |
77 | 0 | registerWith(discountCurve); |
78 | 0 | registerWith(ctptyDTS); |
79 | 0 | registerWith(invstDTS_); |
80 | 0 | } |
81 | | |
82 | | CounterpartyAdjSwapEngine::CounterpartyAdjSwapEngine( |
83 | | const Handle<YieldTermStructure>& discountCurve, |
84 | | const Handle<Quote>& blackVol, |
85 | | const Handle<DefaultProbabilityTermStructure>& ctptyDTS, |
86 | | Real ctptyRecoveryRate, |
87 | | const Handle<DefaultProbabilityTermStructure>& invstDTS, |
88 | | Real invstRecoveryRate) |
89 | 0 | : baseSwapEngine_(Handle<PricingEngine>( |
90 | 0 | ext::make_shared<DiscountingSwapEngine>(discountCurve))), |
91 | 0 | swaptionletEngine_(Handle<PricingEngine>( |
92 | 0 | ext::make_shared<BlackSwaptionEngine>(discountCurve, |
93 | 0 | blackVol))), |
94 | 0 | discountCurve_(discountCurve), |
95 | 0 | defaultTS_(ctptyDTS), |
96 | 0 | ctptyRecoveryRate_(ctptyRecoveryRate), |
97 | 0 | invstDTS_(invstDTS.empty() ? Handle<DefaultProbabilityTermStructure>( |
98 | 0 | ext::make_shared<FlatHazardRate>(0, NullCalendar(), 1.e-12, |
99 | 0 | ctptyDTS->dayCounter()) ) : invstDTS ), |
100 | 0 | invstRecoveryRate_(invstRecoveryRate) |
101 | 0 | { |
102 | 0 | registerWith(discountCurve); |
103 | 0 | registerWith(ctptyDTS); |
104 | 0 | registerWith(invstDTS_); |
105 | 0 | registerWith(blackVol); |
106 | 0 | } |
107 | | |
108 | 0 | void CounterpartyAdjSwapEngine::calculate() const { |
109 | | /* both DTS, YTS ref dates and pricing date consistency |
110 | | checks? settlement... */ |
111 | 0 | QL_REQUIRE(!discountCurve_.empty(), |
112 | 0 | "no discount term structure set"); |
113 | 0 | QL_REQUIRE(!defaultTS_.empty(), |
114 | 0 | "no ctpty default term structure set"); |
115 | 0 | QL_REQUIRE(!swaptionletEngine_.empty(), |
116 | 0 | "no swap option engine set"); |
117 | | |
118 | 0 | QL_REQUIRE(arguments_.nominal != Null<Real>(), |
119 | 0 | "non-constant nominals are not supported yet"); |
120 | | |
121 | 0 | Date priceDate = defaultTS_->referenceDate(); |
122 | |
|
123 | 0 | Real cumOptVal = 0., |
124 | 0 | cumPutVal = 0.; |
125 | | // Vanilla swap so 0 leg is floater |
126 | |
|
127 | 0 | auto nextFD = |
128 | 0 | arguments_.fixedPayDates.begin(); |
129 | 0 | Date swapletStart = priceDate; |
130 | 0 | while (*nextFD < priceDate) ++nextFD; |
131 | | |
132 | | // Compute fair spread for strike value: |
133 | | // copy args into the non risky engine |
134 | 0 | auto* noCVAArgs = dynamic_cast<Swap::arguments*>(baseSwapEngine_->getArguments()); |
135 | 0 | QL_REQUIRE(noCVAArgs != nullptr, "wrong argument type"); |
136 | | |
137 | 0 | noCVAArgs->legs = this->arguments_.legs; |
138 | 0 | noCVAArgs->payer = this->arguments_.payer; |
139 | |
|
140 | 0 | baseSwapEngine_->calculate(); |
141 | |
|
142 | 0 | ext::shared_ptr<FixedRateCoupon> coupon = ext::dynamic_pointer_cast<FixedRateCoupon>(arguments_.legs[0][0]); |
143 | 0 | QL_REQUIRE(coupon,"dynamic cast of fixed leg coupon failed."); |
144 | 0 | Rate baseSwapRate = coupon->rate(); |
145 | |
|
146 | 0 | const auto* vSResults = dynamic_cast<const Swap::results*>(baseSwapEngine_->getResults()); |
147 | 0 | QL_REQUIRE(vSResults != nullptr, "wrong result type"); |
148 | | |
149 | 0 | Rate baseSwapFairRate = -baseSwapRate * vSResults->legNPV[1] / |
150 | 0 | vSResults->legNPV[0]; |
151 | 0 | Real baseSwapNPV = vSResults->value; |
152 | |
|
153 | 0 | Swap::Type reversedType = arguments_.type == Swap::Payer ? Swap::Receiver : Swap::Payer; |
154 | | |
155 | | // Swaplet options summatory: |
156 | 0 | while(nextFD != arguments_.fixedPayDates.end()) { |
157 | | // iFD coupon not fixed, create swaptionlet: |
158 | 0 | ext::shared_ptr<FloatingRateCoupon> floatCoupon = ext::dynamic_pointer_cast<FloatingRateCoupon>(arguments_.legs[1][0]); |
159 | 0 | QL_REQUIRE(floatCoupon,"dynamic cast of floating leg coupon failed."); |
160 | 0 | ext::shared_ptr<IborIndex> swapIndex = ext::dynamic_pointer_cast<IborIndex>(floatCoupon->index()); |
161 | 0 | QL_REQUIRE(swapIndex,"dynamic cast of floating leg index failed."); |
162 | | |
163 | | // Alternatively one could cap this period to, say, 1M |
164 | | // Period swapPeriod = ext::dynamic_pointer_cast<FloatingRateCoupon>( |
165 | | // arguments_.legs[1][0])->index()->tenor(); |
166 | | |
167 | 0 | Period baseSwapsTenor(arguments_.fixedPayDates.back().serialNumber() |
168 | 0 | - swapletStart.serialNumber(), Days); |
169 | 0 | ext::shared_ptr<VanillaSwap> swaplet = MakeVanillaSwap( |
170 | 0 | baseSwapsTenor, |
171 | 0 | swapIndex, |
172 | 0 | baseSwapFairRate // strike |
173 | 0 | ) |
174 | 0 | .withType(arguments_.type) |
175 | 0 | .withNominal(arguments_.nominal) |
176 | | //////// .withSettlementDays(2) |
177 | 0 | .withEffectiveDate(swapletStart) |
178 | 0 | .withTerminationDate(arguments_.fixedPayDates.back()); |
179 | 0 | ext::shared_ptr<VanillaSwap> revSwaplet = MakeVanillaSwap( |
180 | 0 | baseSwapsTenor, |
181 | 0 | swapIndex, |
182 | 0 | baseSwapFairRate // strike |
183 | 0 | ) |
184 | 0 | .withType(reversedType) |
185 | 0 | .withNominal(arguments_.nominal) |
186 | | ///////// .withSettlementDays(2) |
187 | 0 | .withEffectiveDate(swapletStart) |
188 | 0 | .withTerminationDate(arguments_.fixedPayDates.back()); |
189 | |
|
190 | 0 | Swaption swaptionlet(swaplet, |
191 | 0 | ext::make_shared<EuropeanExercise>(swapletStart)); |
192 | 0 | Swaption putSwaplet(revSwaplet, |
193 | 0 | ext::make_shared<EuropeanExercise>(swapletStart)); |
194 | 0 | swaptionlet.setPricingEngine(swaptionletEngine_.currentLink()); |
195 | 0 | putSwaplet.setPricingEngine(swaptionletEngine_.currentLink()); |
196 | | |
197 | | // atm underlying swap means that the value of put = value |
198 | | // call so this double pricing is not needed |
199 | 0 | cumOptVal += swaptionlet.NPV() * defaultTS_->defaultProbability( |
200 | 0 | swapletStart, *nextFD); |
201 | 0 | cumPutVal += putSwaplet.NPV() * invstDTS_->defaultProbability( |
202 | 0 | swapletStart, *nextFD); |
203 | |
|
204 | 0 | swapletStart = *nextFD; |
205 | 0 | ++nextFD; |
206 | 0 | } |
207 | | |
208 | 0 | results_.value = baseSwapNPV - (1.-ctptyRecoveryRate_) * cumOptVal |
209 | 0 | + (1.-invstRecoveryRate_) * cumPutVal; |
210 | |
|
211 | 0 | results_.fairRate = -baseSwapRate * (vSResults->legNPV[1] |
212 | 0 | - (1.-ctptyRecoveryRate_) * cumOptVal + |
213 | 0 | (1.-invstRecoveryRate_) * cumPutVal ) |
214 | 0 | / vSResults->legNPV[0]; |
215 | |
|
216 | 0 | } |
217 | | |
218 | | |
219 | | } |