Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
creditvolcurve.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2022 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#include <ql/instruments/creditdefaultswap.hpp>
22#include <ql/pricingengines/credit/midpointcdsengine.hpp>
26
27namespace QuantExt {
28
29using namespace QuantLib;
30
31CreditVolCurve::CreditVolCurve(BusinessDayConvention bdc, const DayCounter& dc, const std::vector<Period>& terms,
32 const std::vector<Handle<CreditCurve>>& termCurves, const Type& type)
33 : VolatilityTermStructure(bdc, dc), terms_(terms), termCurves_(termCurves), type_(type) {
34 init();
35}
36
37CreditVolCurve::CreditVolCurve(const Natural settlementDays, const Calendar& cal, BusinessDayConvention bdc,
38 const DayCounter& dc, const std::vector<Period>& terms,
39 const std::vector<Handle<CreditCurve>>& termCurves, const Type& type)
40 : VolatilityTermStructure(settlementDays, cal, bdc, dc), terms_(terms), termCurves_(termCurves), type_(type) {
41 init();
42}
43
44CreditVolCurve::CreditVolCurve(const Date& referenceDate, const Calendar& cal, BusinessDayConvention bdc,
45 const DayCounter& dc, const std::vector<Period>& terms,
46 const std::vector<Handle<CreditCurve>>& termCurves, const Type& type)
47 : VolatilityTermStructure(referenceDate, cal, bdc, dc), terms_(terms), termCurves_(termCurves), type_(type) {
48 init();
49}
50
51void CreditVolCurve::init() {
52 QL_REQUIRE(terms_.size() == termCurves_.size(), "CreditVolCurve: terms size (" << terms_.size()
53 << ") must match termCurves size ("
54 << termCurves_.size());
55 // sort terms and curves
56
57 std::vector<Size> p(terms_.size());
58 std::iota(p.begin(), p.end(), 0);
59 std::sort(p.begin(), p.end(), [this](Size i, Size j) { return this->terms_[i] < this->terms_[j]; });
60
61 std::vector<Period> sortedTerms(terms_.size());
62 std::vector<Handle<CreditCurve>> sortedCurves(terms_.size());
63 std::transform(p.begin(), p.end(), sortedTerms.begin(), [this](Size i) { return this->terms_[i]; });
64 std::transform(p.begin(), p.end(), sortedCurves.begin(), [this](Size i) { return this->termCurves_[i]; });
65
66 terms_ = sortedTerms;
67 termCurves_ = sortedCurves;
68
69 // register with curves
70
71 for (const auto& c : termCurves_)
72 registerWith(c);
73}
74
75Real CreditVolCurve::volatility(const Date& exerciseDate, const Period& underlyingTerm, const Real strike,
76 const Type& targetType) const {
77 return volatility(exerciseDate, periodToTime(underlyingTerm), strike, targetType);
78}
79
80Real CreditVolCurve::volatility(const Real exerciseTime, const Real underlyingLength, const Real strike,
81 const CreditVolCurve::Type& targetType) const {
82 Date d = lowerDate(exerciseTime, referenceDate(), dayCounter());
83 Real t1 = timeFromReference(d);
84 Real t2 = timeFromReference(d + 1);
85 Real alpha = (t2 - exerciseTime) / (t2 - t1);
86 Real v1 = volatility(d, underlyingLength, strike, targetType);
87 if (close_enough(alpha, 1.0))
88 return v1;
89 return alpha * v1 + (1.0 - alpha) * volatility(d + 1, underlyingLength, strike, targetType);
90}
91
92const std::vector<Period>& CreditVolCurve::terms() const { return terms_; }
93
94const std::vector<Handle<CreditCurve>>& CreditVolCurve::termCurves() const { return termCurves_; }
95
96const CreditVolCurve::Type& CreditVolCurve::type() const { return type_; }
97
98Date CreditVolCurve::maxDate() const { return Date::maxDate(); }
99
100Real CreditVolCurve::minStrike() const { return -QL_MAX_REAL; }
101
102Real CreditVolCurve::maxStrike() const { return QL_MAX_REAL; }
103
104Real CreditVolCurve::moneyness(const Real strike, const Real atmStrike) const {
105 if (strike == Null<Real>())
106 return 0.0;
107 if (type() == Type::Spread) {
108 return strike - atmStrike;
109 } else if (type() == Type::Price) {
110 return std::log(strike / atmStrike);
111 } else {
112 QL_FAIL("InterpolatingCreditVolCurve::moneyness(): internal error, type not handled");
113 }
114}
115
116Real CreditVolCurve::strike(const Real moneyness, const Real atmStrike) const {
117 if (type() == Type::Spread) {
118 return atmStrike + moneyness;
119 } else if (type() == Type::Price) {
120 return atmStrike * std::exp(moneyness);
121 } else {
122 QL_FAIL("InterpolatingCreditVolCurve::strike(): internal error, type not handled");
123 }
124}
125
126Real CreditVolCurve::atmStrike(const Date& expiry, const Period& term) const {
127 return atmStrike(expiry, periodToTime(term));
128}
129
130Real CreditVolCurve::atmStrike(const Date& expiry, const Real underlyingLength) const {
131
132 // do we have the desired value in the cache?
133
134 calculate();
135 auto v = atmStrikeCache_.find(std::make_pair(expiry, underlyingLength));
136 if (v != atmStrikeCache_.end())
137 return v->second;
138
139 /* we need at least one term curve to compute the atm strike properly - we set it to 0 / 1 (spread, price) if
140 we don't have a term, so that we can build strike-independent curves without curves. It is the user's
141 responsibility to provide terms for strike-dependent curves. */
142 if (terms().empty())
143 return type() == Type::Price ? 1.0 : 0.0;
144
145 // interpolate in term length
146
147 std::vector<Real> termLengths;
148 for (auto const& p : terms())
149 termLengths.push_back(periodToTime(p));
150
151 Size termIndex_m, termIndex_p;
152 Real term_alpha;
153 std::tie(termIndex_m, termIndex_p, term_alpha) = interpolationIndices(termLengths, underlyingLength);
154
155 // construct cds underlying(s) for the terms we will use for the interpolation
156
157 std::map<Period, QuantLib::ext::shared_ptr<QuantExt::CreditDefaultSwap>> underlyings;
158
159 const CreditCurve::RefData& refData = termCurves_[termIndex_m]->refData();
160 QL_REQUIRE(refData.runningSpread != Null<Real>(), "CreditVolCurve: need runningSpread for ATM strike computation. "
161 "Is the running spread in the term curve configuration?");
162
163 /* Use index maturity date based on index start date. If no start date is given, assume this is a single name option
164 running from today. */
165 Date mat = std::max(cdsMaturity(refData.startDate != Null<Date>() ? refData.startDate : referenceDate(),
166 terms_[termIndex_m], refData.rule),
167 referenceDate() + 1);
168 Date effExp = std::min(mat - 1, expiry);
169 Schedule schedule(effExp, mat, refData.tenor, refData.calendar, refData.convention, refData.termConvention,
170 refData.rule, refData.endOfMonth);
171 Date protectionStartDate = (refData.rule == DateGeneration::CDS || refData.rule == DateGeneration::CDS2015)
172 ? effExp
173 : schedule.dates().front();
174 DayCounter lastPeriodDayCounter = refData.lastPeriodDayCounter.empty() && refData.dayCounter == Actual360()
175 ? Actual360(true)
176 : refData.lastPeriodDayCounter;
177 underlyings[terms_[termIndex_m]] = QuantLib::ext::make_shared<CreditDefaultSwap>(
178 Protection::Buyer, 1.0, refData.runningSpread, schedule, refData.payConvention, refData.dayCounter, true,
179 CreditDefaultSwap::atDefault, protectionStartDate, QuantLib::ext::shared_ptr<Claim>(), lastPeriodDayCounter, true,
180 effExp, refData.cashSettlementDays);
181
182 QL_REQUIRE(!termCurves_[termIndex_m]->rateCurve().empty() && !termCurves_[termIndex_p]->rateCurve().empty(),
183 "CreditVolCurve: need discounting rate curve of index for ATM strike computation.");
184 QL_REQUIRE(!termCurves_[termIndex_m]->recovery().empty() && !termCurves_[termIndex_p]->recovery().empty(),
185 "CreditVolCurve: need recovery rate of index for ATM strike computation.");
186
187 auto engine = QuantLib::ext::make_shared<MidPointCdsEngine>(termCurves_[termIndex_m]->curve(),
188 termCurves_[termIndex_m]->recovery()->value(),
189 termCurves_[termIndex_m]->rateCurve());
190
191 underlyings[terms_[termIndex_m]]->setPricingEngine(engine);
192
193 // compute (interpolated) ATM strike
194
195 Real atmStrike;
196 Real fep1, fep2, fairSpread1, fairSpread2, rpv01_1, rpv01_2, adjFairSpread1 = 0.0, adjFairSpread2 = 0.0,
197 discToExercise = 1.0, forwardPrice1, forwardPrice2,
198 adjForwardPrice1 = 1.0, adjForwardPrice2 = 1.0;
199
200 discToExercise = termCurves_[termIndex_m]->rateCurve()->discount(effExp);
201 fep1 = (1.0 - termCurves_[termIndex_m]->recovery()->value()) *
202 termCurves_[termIndex_m]->curve()->defaultProbability(effExp) * discToExercise;
203 if (type() == Type::Spread) {
204 fairSpread1 = underlyings[terms_[termIndex_m]]->fairSpreadClean();
205 rpv01_1 = std::abs(underlyings[terms_[termIndex_m]]->couponLegNPV() +
206 underlyings[terms_[termIndex_m]]->accrualRebateNPV()) /
207 underlyings[terms_[termIndex_m]]->runningSpread();
208 adjFairSpread1 = fairSpread1 + fep1 / rpv01_1;
209 atmStrike = adjFairSpread1;
210 } else if (type() == Type::Price) {
211 forwardPrice1 = 1.0 - underlyings[terms_[termIndex_m]]->NPV() / discToExercise;
212 adjForwardPrice1 = forwardPrice1 - fep1 / discToExercise;
213 atmStrike = adjForwardPrice1;
214 } else {
215 QL_FAIL("InterpolatingCreditVolCurve::atmStrike(): internal error, type not handled");
216 }
217
218 if (!close_enough(term_alpha, 1.0)) {
219 Date mat = std::max(cdsMaturity(refData.startDate != Null<Date>() ? refData.startDate : referenceDate(),
220 terms_[termIndex_p], refData.rule),
221 referenceDate() + 1);
222 Date effExp = std::min(mat - 1, expiry);
223 Schedule schedule(effExp, mat, refData.tenor, refData.calendar, refData.convention, refData.termConvention,
224 refData.rule, refData.endOfMonth);
225 Date protectionStartDate = (refData.rule == DateGeneration::CDS || refData.rule == DateGeneration::CDS2015)
226 ? effExp
227 : schedule.dates().front();
228 underlyings[terms_[termIndex_p]] = QuantLib::ext::make_shared<CreditDefaultSwap>(
229 Protection::Buyer, 1.0, refData.runningSpread, schedule, refData.payConvention, refData.dayCounter, true,
230 CreditDefaultSwap::atDefault, protectionStartDate, QuantLib::ext::shared_ptr<Claim>(), lastPeriodDayCounter, true,
231 effExp, refData.cashSettlementDays);
232 auto engine = QuantLib::ext::make_shared<MidPointCdsEngine>(termCurves_[termIndex_p]->curve(),
233 termCurves_[termIndex_p]->recovery()->value(),
234 termCurves_[termIndex_p]->rateCurve());
235 underlyings[terms_[termIndex_p]]->setPricingEngine(engine);
236
237 fep2 = (1.0 - termCurves_[termIndex_p]->recovery()->value()) *
238 termCurves_[termIndex_p]->curve()->defaultProbability(effExp) * discToExercise;
239 if (type() == Type::Spread) {
240 fairSpread2 = underlyings[terms_[termIndex_p]]->fairSpreadClean();
241 rpv01_2 = std::abs(underlyings[terms_[termIndex_p]]->couponLegNPV() +
242 underlyings[terms_[termIndex_p]]->accrualRebateNPV()) /
243 underlyings[terms_[termIndex_p]]->runningSpread();
244 adjFairSpread2 = fairSpread2 + fep2 / rpv01_2;
245 atmStrike = term_alpha * adjFairSpread1 + (1.0 - term_alpha * adjFairSpread2);
246 } else if (type() == Type::Price) {
247 forwardPrice2 = 1.0 - underlyings[terms_[termIndex_p]]->NPV() / discToExercise;
248 adjForwardPrice2 = forwardPrice2 - fep2 / discToExercise;
249 atmStrike = term_alpha * adjForwardPrice1 + (1.0 - term_alpha) * adjForwardPrice2;
250 } else {
251 QL_FAIL("InterpolatingCreditVolCurve::atmStrike(): internal error, type not handled");
252 }
253 }
254
255 // add the result to the cache and return it
256
257 atmStrikeCache_[std::make_pair(expiry, underlyingLength)] = atmStrike;
258 return atmStrike;
259}
260
261void CreditVolCurve::performCalculations() const { atmStrikeCache_.clear(); }
262
263InterpolatingCreditVolCurve::InterpolatingCreditVolCurve(
264 const Natural settlementDays, const Calendar& cal, BusinessDayConvention bdc, const DayCounter& dc,
265 const std::vector<Period>& terms, const std::vector<Handle<CreditCurve>>& termCurves,
266 const std::map<std::tuple<Date, Period, Real>, Handle<Quote>>& quotes, const Type& type)
267 : CreditVolCurve(settlementDays, cal, bdc, dc, terms, termCurves, type), quotes_(quotes) {
268 init();
269}
270
272 const Date& referenceDate, const Calendar& cal, BusinessDayConvention bdc, const DayCounter& dc,
273 const std::vector<Period>& terms, const std::vector<Handle<CreditCurve>>& termCurves,
274 const std::map<std::tuple<Date, Period, Real>, Handle<Quote>>& quotes, const Type& type)
275 : CreditVolCurve(referenceDate, cal, bdc, dc, terms, termCurves, type), quotes_(quotes) {
276 init();
277}
278
280 for (auto const& q : quotes_)
281 registerWith(q.second);
282}
283
284Real InterpolatingCreditVolCurve::volatility(const Date& expiry, const Real underlyingLength, const Real strike,
285 const Type& targetType) const {
286
287 calculate();
288
289 QL_REQUIRE(targetType == type(), "InterpolatingCreditVolCurve: Vol type conversion between strike types 'Price' "
290 "and 'Spread' is not supported. The vol "
291 "surface used to price an option must have the same strike type as the option.");
292
293 Real effStrike = strike == Null<Real>() ? atmStrike(expiry, underlyingLength) : strike;
294
295 // term interpolation
296
297 Size termIndex_m, termIndex_p;
298 Real term_alpha;
299 std::tie(termIndex_m, termIndex_p, term_alpha) = interpolationIndices(smileTermLengths_, underlyingLength);
300
301 // expiry interpolation
302
303 Size expiryIndex_m, expiryIndex_p;
304 Real expiry_alpha;
305 Real t = timeFromReference(expiry);
306 std::tie(expiryIndex_m, expiryIndex_p, expiry_alpha) = interpolationIndices(smileExpiryTimes_, t);
307
308 // smiles by expiry / term
309
310 const Smile& smile_1_1 = smiles_[std::make_pair(smileExpiries_[expiryIndex_m], smileTerms_[termIndex_m])];
311 const Smile& smile_1_2 = smiles_[std::make_pair(smileExpiries_[expiryIndex_m], smileTerms_[termIndex_p])];
312 const Smile& smile_2_1 = smiles_[std::make_pair(smileExpiries_[expiryIndex_p], smileTerms_[termIndex_m])];
313 const Smile& smile_2_2 = smiles_[std::make_pair(smileExpiries_[expiryIndex_p], smileTerms_[termIndex_p])];
314
315 // atm levels by expiry / term
316
317 Real atm_1_1 = smile_1_1.first;
318 Real atm_1_2 = smile_1_2.first;
319 Real atm_2_1 = smile_2_1.first;
320 Real atm_2_2 = smile_2_2.first;
321
322 // vols at target moneyness
323
324 Real m = moneyness(effStrike, atmStrike(expiry, underlyingLength));
325 Real vol_1_1 = smile_1_1.second->operator()(this->strike(m, atm_1_1));
326 Real vol_1_2 = smile_1_2.second->operator()(this->strike(m, atm_1_2));
327 Real vol_2_1 = smile_2_1.second->operator()(this->strike(m, atm_2_1));
328 Real vol_2_2 = smile_2_2.second->operator()(this->strike(m, atm_2_2));
329
330 // interpolate in term direction
331
332 Real vol_1 = term_alpha * vol_1_1 + (1.0 - term_alpha) * vol_1_2;
333 Real vol_2 = term_alpha * vol_2_1 + (1.0 - term_alpha) * vol_2_2;
334
335 // interpolate in expiry direction
336
337 return std::sqrt((expiry_alpha * (vol_1 * vol_1 * smileExpiryTimes_[expiryIndex_m]) +
338 (1.0 - expiry_alpha) * (vol_2 * vol_2 * smileExpiryTimes_[expiryIndex_p])) /
339 t);
340}
341
343
345
346 QL_REQUIRE(!quotes_.empty(), "InterpolatingCreditVolCurve: no quotes given, can not build a volatility curve.");
347
348 // For each term and option expiry create an interpolation object representing a vol smile.
349
350 smileTerms_.clear();
351 smileExpiries_.clear();
352 smileTermLengths_.clear();
353 smileExpiryTimes_.clear();
354 strikes_.clear();
355 vols_.clear();
356 smiles_.clear();
357
358 Period currentTerm = 0 * Days;
359 Date currentExpiry = Null<Date>();
360 std::vector<Real> currentStrikes;
361 std::vector<Real> currentVols;
362 Date expiry;
363 Period term;
364 Real strike;
365 Real vol;
366
367 auto q = quotes_.begin();
368 for (Size i = 0; i <= quotes_.size(); ++i) {
369 if (i < quotes_.size()) {
370 expiry = std::get<0>(q->first);
371 term = std::get<1>(q->first);
372 strike = std::get<2>(q->first);
373 vol = q->second->value();
374 ++q;
375 } else {
376 currentTerm = term;
377 currentExpiry = expiry;
378 }
379 if (term != currentTerm || expiry != currentExpiry || i == quotes_.size()) {
380 if (!currentStrikes.empty()) {
381 if (currentStrikes.size() == 1) {
382 currentStrikes.push_back(currentStrikes.back() + 0.01);
383 currentVols.push_back(currentVols.back());
384 }
385 auto key = std::make_pair(currentExpiry, currentTerm);
386 auto s = strikes_.insert(std::make_pair(key, currentStrikes)).first;
387 auto v = vols_.insert(std::make_pair(key, currentVols)).first;
388 auto tmp = QuantLib::ext::make_shared<FlatExtrapolation>(
389 QuantLib::ext::make_shared<LinearInterpolation>(s->second.begin(), s->second.end(), v->second.begin()));
390 tmp->enableExtrapolation();
391 smiles_[key] = std::make_pair(atmStrike(currentExpiry, currentTerm), tmp);
392 currentStrikes.clear();
393 currentVols.clear();
394 smileTerms_.push_back(currentTerm);
395 smileExpiries_.push_back(currentExpiry);
396 }
397 currentTerm = term;
398 currentExpiry = expiry;
399 }
400 if (i < quotes_.size()) {
401 currentStrikes.push_back(strike);
402 currentVols.push_back(vol);
403 }
404 }
405
406 // populate times vectors
407
408 for (auto const& p : smileTerms_)
409 smileTermLengths_.push_back(periodToTime(p));
410 for (auto const& d : smileExpiries_)
411 smileExpiryTimes_.push_back(timeFromReference(d));
412
413 /* For each term, add missing option expiries that we saw for other terms by creating an interpolated smile.
414 We interpolate in terms of
415 - absolute moneyness (Type = Spread)
416 - log-moneyness (Type = Price)
417 */
418
419 for (auto const& term : smileTerms_) {
420 for (auto const& expiry : smileExpiries_) {
421 auto key = std::make_pair(expiry, term);
422 if (smiles_.find(key) == smiles_.end()) {
423
424 // search neighboured expiries for same term
425
426 Date expiry_m = Null<Date>();
427 Date expiry_p = Null<Date>();
428 for (auto const& s : smiles_) {
429 if (s.first.second != term)
430 continue;
431 if (s.first.first >= expiry) {
432 expiry_p = s.first.first;
433 break;
434 }
435 expiry_m = s.first.first;
436 }
437
438 // if we have a smile for the expiry and term already, there is nothing to do
439
440 if (expiry_m == expiry || expiry_p == expiry)
441 continue;
442
443 // otherwise build an interpolated / extrapoalted smile
444
445 if (expiry_m == Null<Date>() && expiry_p != Null<Date>()) {
446 // expiry <= smallest expiry for that term = expiry_p
447 createSmile(expiry, term, expiry_p, expiry_m);
448 } else if (expiry_m != Null<Date>() && expiry_p != Null<Date>()) {
449 // expiry_m < expiry < expiry_p
450 createSmile(expiry, term, expiry_m, expiry_p);
451 } else if (expiry_m != Null<Date>() && expiry_p == Null<Date>()) {
452 // expiry >= largest expiry for that term = expiry_m
453 createSmile(expiry, term, expiry_p, expiry_m);
454 } else {
455 QL_FAIL("InterpolatingCreditVolCurve: internal error, expiry_m = expiry_p = null, i.e. there are "
456 "no smiles for term "
457 << term);
458 }
459 }
460 }
461 }
462}
463
464namespace {
465struct CompClose {
466 bool operator()(Real x, Real y) const { return x < y && !close_enough(x, y); }
467};
468} // namespace
469
470void InterpolatingCreditVolCurve::createSmile(const Date& expiry, const Period& term, const Date& expiry_m,
471 const Date& expiry_p) const {
472 Real thisAtm = atmStrike(expiry, term);
473 if (expiry_p == Null<Date>()) {
474 auto key = std::make_pair(expiry_m, term);
475 const Smile& smile = smiles_[key];
476 std::vector<Real> strikes;
477 std::vector<Real> vols;
478 for (auto const& k : strikes_[key]) {
479 strikes.push_back(strike(moneyness(k, smile.first), thisAtm));
480 }
481 for (auto const& k : strikes)
482 vols.push_back(smile.second->operator()(k));
483 auto s = strikes_.insert(std::make_pair(key, strikes));
484 auto v = vols_.insert(std::make_pair(key, vols));
485 auto tmp = QuantLib::ext::make_shared<FlatExtrapolation>(QuantLib::ext::make_shared<LinearInterpolation>(
486 s.first->second.begin(), s.first->second.end(), v.first->second.begin()));
487 tmp->enableExtrapolation();
488 smiles_[std::make_pair(expiry, term)] = std::make_pair(thisAtm, tmp);
489 } else if (expiry_m == Null<Date>()) {
490 auto key = std::make_pair(expiry_p, term);
491 const Smile& smile = smiles_[key];
492 std::vector<Real> strikes;
493 std::vector<Real> vols;
494 for (auto const& k : strikes_[key]) {
495 strikes.push_back(strike(moneyness(k, smile.first), thisAtm));
496 }
497 for (auto const& k : strikes)
498 vols.push_back(smile.second->operator()(k));
499 auto s = strikes_.insert(std::make_pair(key, strikes));
500 auto v = vols_.insert(std::make_pair(key, vols));
501 auto tmp = QuantLib::ext::make_shared<FlatExtrapolation>(QuantLib::ext::make_shared<LinearInterpolation>(
502 s.first->second.begin(), s.first->second.end(), v.first->second.begin()));
503 tmp->enableExtrapolation();
504 smiles_[std::make_pair(expiry, term)] = std::make_pair(thisAtm, tmp);
505 } else {
506 auto key_m = std::make_pair(expiry_m, term);
507 auto key_p = std::make_pair(expiry_p, term);
508 const Smile& smile_m = smiles_[key_m];
509 const Smile& smile_p = smiles_[key_p];
510 std::set<Real, CompClose> strikes_set;
511 for (auto const& k : strikes_[key_m]) {
512 strikes_set.insert(strike(moneyness(k, smile_m.first), thisAtm));
513 }
514 for (auto const& k : strikes_[key_p]) {
515 strikes_set.insert(strike(moneyness(k, smile_p.first), thisAtm));
516 }
517 std::vector<Real> strikes(strikes_set.begin(), strikes_set.end());
518 std::vector<Real> vols;
519 Real t = timeFromReference(expiry);
520 Real t_m = timeFromReference(expiry_m);
521 Real t_p = timeFromReference(expiry_m);
522 Real alpha = (t_p - t) / (t_p - t_m);
523 for (auto const& k : strikes) {
524 Real vol_m = smile_m.second->operator()(k);
525 Real vol_p = smile_p.second->operator()(k);
526 vols.push_back(std::sqrt((alpha * (vol_m * vol_m * t_m) + (1.0 - alpha) * (vol_p * vol_p * t_p)) / t));
527 }
528 auto s = strikes_.insert(std::make_pair(std::make_pair(expiry, term), strikes));
529 auto v = vols_.insert(std::make_pair(std::make_pair(expiry, term), vols));
530 auto tmp = QuantLib::ext::make_shared<FlatExtrapolation>(QuantLib::ext::make_shared<LinearInterpolation>(
531 s.first->second.begin(), s.first->second.end(), v.first->second.begin()));
532 tmp->enableExtrapolation();
533 smiles_[std::make_pair(expiry, term)] = std::make_pair(thisAtm, tmp);
534 }
535}
536
537ProxyCreditVolCurve::ProxyCreditVolCurve(const QuantLib::Handle<CreditVolCurve>& source,
538 const std::vector<QuantLib::Period>& terms,
539 const std::vector<QuantLib::Handle<CreditCurve>>& termCurves)
540 : CreditVolCurve(source->businessDayConvention(), source->dayCounter(), terms.empty() ? source->terms() : terms,
541 termCurves.empty() ? source->termCurves() : termCurves, source->type()),
542 source_(source) {
543 // check inputs to ctor for consistency
544 QL_REQUIRE(terms.size() == termCurves.size(), "ProxyCreditVolCurve: given terms (" << terms.size()
545 << ") do not match term curves ("
546 << termCurves.size() << ")");
547 registerWith(source);
548}
549
550QuantLib::Real ProxyCreditVolCurve::volatility(const QuantLib::Date& exerciseDate,
551 const QuantLib::Real underlyingLength, const QuantLib::Real strike,
552 const Type& targetType) const {
553
554 // we read the vol from the source surface keeping the moneyness constant (if meaningful)
555
556 Real effectiveStrike = strike;
557 if (!this->terms().empty() && !source_->terms().empty()) {
558 effectiveStrike = this->strike(this->moneyness(strike, this->atmStrike(exerciseDate, underlyingLength)),
559 source_->atmStrike(exerciseDate, underlyingLength));
560 }
561 return source_->volatility(exerciseDate, underlyingLength, effectiveStrike, type());
562}
563
564const Date& ProxyCreditVolCurve::referenceDate() const { return source_->referenceDate(); }
565
566SpreadedCreditVolCurve::SpreadedCreditVolCurve(const Handle<CreditVolCurve> baseCurve, const std::vector<Date> expiries,
567 const std::vector<Handle<Quote>> spreads, const bool stickyMoneyness,
568 const std::vector<Period>& terms,
569 const std::vector<Handle<CreditCurve>>& termCurves)
570 : CreditVolCurve(baseCurve->businessDayConvention(), baseCurve->dayCounter(), terms, termCurves, baseCurve->type()),
571 baseCurve_(baseCurve), expiries_(expiries), spreads_(spreads), stickyMoneyness_(stickyMoneyness) {
572 for (auto const& s : spreads)
573 registerWith(s);
574}
575
576const Date& SpreadedCreditVolCurve::referenceDate() const { return baseCurve_->referenceDate(); }
577
580 times_.clear();
581 spreadValues_.clear();
582 for (auto const& d : expiries_) {
583 times_.push_back(timeFromReference(d));
584 }
585 for (auto const& s : spreads_) {
586 spreadValues_.push_back(s->value());
587 }
588 interpolatedSpreads_ = QuantLib::ext::make_shared<FlatExtrapolation>(
589 QuantLib::ext::make_shared<LinearInterpolation>(times_.begin(), times_.end(), spreadValues_.begin()));
590 interpolatedSpreads_->enableExtrapolation();
591}
592
593Real SpreadedCreditVolCurve::volatility(const Date& exerciseDate, const Real underlyingLength, const Real strike,
594 const Type& targetType) const {
595 calculate();
596 Real effectiveStrike = strike;
597 if (stickyMoneyness_ && !baseCurve_->terms().empty() && !this->terms().empty()) {
598 effectiveStrike = this->strike(this->moneyness(strike, this->atmStrike(exerciseDate, underlyingLength)),
599 baseCurve_->atmStrike(exerciseDate, underlyingLength));
600 }
601 Real base = baseCurve_->volatility(exerciseDate, underlyingLength, effectiveStrike, targetType);
602 Real spread = interpolatedSpreads_->operator()(timeFromReference(exerciseDate));
603 return base + spread;
604}
605
606CreditVolCurveWrapper::CreditVolCurveWrapper(const Handle<BlackVolTermStructure>& vol)
607 : CreditVolCurve(vol->businessDayConvention(), vol->dayCounter(), {}, {}, Type::Spread), vol_(vol) {
608 registerWith(vol_);
609}
610
611Real CreditVolCurveWrapper::volatility(const Date& exerciseDate, const Real underlyingLength, const Real strike,
612 const Type& targetType) const {
613 return vol_->blackVol(exerciseDate, strike, true);
614}
615
616const Date& CreditVolCurveWrapper::referenceDate() const { return vol_->referenceDate(); }
617
618BlackVolFromCreditVolWrapper::BlackVolFromCreditVolWrapper(const Handle<QuantExt::CreditVolCurve>& vol,
619 const Real underlyingLength)
620 : BlackVolatilityTermStructure(vol->businessDayConvention(), vol->dayCounter()), vol_(vol),
621 underlyingLength_(underlyingLength) {}
622
623const Date& BlackVolFromCreditVolWrapper::referenceDate() const { return vol_->referenceDate(); }
624
625Real BlackVolFromCreditVolWrapper::minStrike() const { return vol_->minStrike(); }
626Real BlackVolFromCreditVolWrapper::maxStrike() const { return vol_->maxStrike(); }
627Date BlackVolFromCreditVolWrapper::maxDate() const { return vol_->maxDate(); }
628
629Real BlackVolFromCreditVolWrapper::blackVolImpl(Real t, Real strike) const {
630 return vol_->volatility(t, underlyingLength_, strike, vol_->type());
631}
632
633} // namespace QuantExt
QuantLib::ext::shared_ptr< SimpleQuote > vol_
Definition: cdsoption.cpp:80
QuantLib::Handle< QuantExt::CreditVolCurve > vol_
QuantLib::Real blackVolImpl(QuantLib::Real t, QuantLib::Real strike) const override
const QuantLib::Date & referenceDate() const override
QuantLib::Real minStrike() const override
BlackVolFromCreditVolWrapper(const QuantLib::Handle< QuantExt::CreditVolCurve > &vol, const QuantLib::Real underlyingLength)
QuantLib::Date maxDate() const override
QuantLib::Real maxStrike() const override
void performCalculations() const override
QuantLib::Real moneyness(const QuantLib::Real strike, const QuantLib::Real atmStrike) const
QuantLib::Real atmStrike(const QuantLib::Date &expiry, const QuantLib::Period &term) const
CreditVolCurve(QuantLib::BusinessDayConvention bdc, const QuantLib::DayCounter &dc, const std::vector< QuantLib::Period > &terms, const std::vector< QuantLib::Handle< CreditCurve > > &termCurves, const Type &type)
QuantLib::Real strike(const QuantLib::Real moneyness, const QuantLib::Real atmStrike) const
virtual const std::vector< QuantLib::Handle< CreditCurve > > & termCurves() const
const Type & type() const
virtual const std::vector< QuantLib::Period > & terms() const
CreditVolCurveWrapper(const QuantLib::Handle< QuantLib::BlackVolTermStructure > &vol)
const QuantLib::Date & referenceDate() const override
QuantLib::Real volatility(const QuantLib::Date &exerciseDate, const QuantLib::Real underlyingLength, const QuantLib::Real strike, const Type &targetType) const override
QuantLib::Handle< QuantLib::BlackVolTermStructure > vol_
std::vector< QuantLib::Date > smileExpiries_
std::map< Key, std::vector< QuantLib::Real > > strikes_
std::map< Key, std::vector< QuantLib::Real > > vols_
std::pair< QuantLib::Real, QuantLib::ext::shared_ptr< QuantLib::Interpolation > > Smile
QuantLib::Real volatility(const QuantLib::Date &exerciseDate, const QuantLib::Real underlyingLength, const QuantLib::Real strike, const Type &targetType) const override
std::vector< QuantLib::Real > smileTermLengths_
InterpolatingCreditVolCurve(const QuantLib::Natural settlementDays, const QuantLib::Calendar &cal, QuantLib::BusinessDayConvention bdc, const QuantLib::DayCounter &dc, const std::vector< QuantLib::Period > &terms, const std::vector< QuantLib::Handle< CreditCurve > > &termCurves, const std::map< std::tuple< QuantLib::Date, QuantLib::Period, QuantLib::Real >, QuantLib::Handle< QuantLib::Quote > > &quotes, const Type &type)
std::vector< QuantLib::Real > smileExpiryTimes_
std::map< std::tuple< QuantLib::Date, QuantLib::Period, QuantLib::Real >, QuantLib::Handle< QuantLib::Quote > > quotes_
std::vector< QuantLib::Period > smileTerms_
void createSmile(const QuantLib::Date &expiry, const QuantLib::Period &term, const QuantLib::Date &expiry_m, const QuantLib::Date &expiry_p) const
ProxyCreditVolCurve(const QuantLib::Handle< CreditVolCurve > &source, const std::vector< QuantLib::Period > &terms={}, const std::vector< QuantLib::Handle< CreditCurve > > &termCurves={})
QuantLib::Real volatility(const QuantLib::Date &exerciseDate, const QuantLib::Real underlyingLength, const QuantLib::Real strike, const Type &targetType) const override
const QuantLib::Date & referenceDate() const override
QuantLib::Handle< CreditVolCurve > source_
void performCalculations() const override
std::vector< QuantLib::Handle< QuantLib::Quote > > spreads_
QuantLib::ext::shared_ptr< QuantLib::Interpolation > interpolatedSpreads_
const QuantLib::Date & referenceDate() const override
QuantLib::Real volatility(const QuantLib::Date &exerciseDate, const QuantLib::Real underlyingLength, const QuantLib::Real strike, const Type &targetType) const override
std::vector< QuantLib::Real > spreadValues_
std::vector< QuantLib::Real > times_
std::vector< QuantLib::Date > expiries_
SpreadedCreditVolCurve(const QuantLib::Handle< CreditVolCurve > baseCurve, const std::vector< QuantLib::Date > expiries, const std::vector< QuantLib::Handle< QuantLib::Quote > > spreads, const bool stickyMoneyness, const std::vector< QuantLib::Period > &terms={}, const std::vector< QuantLib::Handle< CreditCurve > > &termCurves={})
QuantLib::Handle< CreditVolCurve > baseCurve_
credit vol curve
flat interpolation decorator
std::tuple< Size, Size, Real > interpolationIndices(const T &x, const Real v)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
Real periodToTime(const Period &p)
Definition: time.cpp:37
vector< Real > strikes
time related utilities.