25#include <ql/math/interpolations/loginterpolation.hpp>
26#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
27#include <ql/termstructures/volatility/equityfx/blackvariancecurve.hpp>
28#include <ql/termstructures/yield/flatforward.hpp>
29#include <ql/time/calendars/weekendsonly.hpp>
30#include <ql/time/daycounters/actual365fixed.hpp>
42 const std::map<std::string, QuantLib::ext::shared_ptr<CDSVolCurve>>& requiredCdsVolCurves,
43 const std::map<std::string, QuantLib::ext::shared_ptr<DefaultCurve>>& requiredCdsCurves) {
50 <<
"volatility curve spec with ID "
57 strikeType_ = config.strikeType() ==
"Price" ? CreditVolCurve::Type::Price : CreditVolCurve::Type::Spread;
60 QuantLib::ext::shared_ptr<VolatilityConfig> vc = config.volatilityConfig();
61 if (
auto cvc = QuantLib::ext::dynamic_pointer_cast<ConstantVolatilityConfig>(vc)) {
63 }
else if (
auto vcc = QuantLib::ext::dynamic_pointer_cast<VolatilityCurveConfig>(vc)) {
65 }
else if (
auto vssc = QuantLib::ext::dynamic_pointer_cast<VolatilityStrikeSurfaceConfig>(vc)) {
67 }
else if (
auto vdsc = QuantLib::ext::dynamic_pointer_cast<VolatilityDeltaSurfaceConfig>(vc)) {
68 QL_FAIL(
"CDSVolCurve does not support a VolatilityDeltaSurfaceConfig yet.");
69 }
else if (
auto vmsc = QuantLib::ext::dynamic_pointer_cast<VolatilityMoneynessSurfaceConfig>(vc)) {
70 QL_FAIL(
"CDSVolCurve does not support a VolatilityMoneynessSurfaceConfig yet.");
71 }
else if (
auto vapo = QuantLib::ext::dynamic_pointer_cast<VolatilityApoFutureSurfaceConfig>(vc)) {
72 QL_FAIL(
"VolatilityApoFutureSurfaceConfig does not make sense for CDSVolCurve.");
73 }
else if (
auto vpc = QuantLib::ext::dynamic_pointer_cast<CDSProxyVolatilityConfig>(vc)) {
76 QL_FAIL(
"Unexpected VolatilityConfig in CDSVolatilityConfig");
81 }
catch (exception& e) {
82 QL_FAIL(
"CDS volatility curve building for ID " <<
spec.
curveConfigID() <<
" failed : " << e.what());
84 QL_FAIL(
"CDS volatility curve building for ID " <<
spec.
curveConfigID() <<
" failed: unknown error");
89 const ConstantVolatilityConfig& cvc,
const Loader& loader) {
91 LOG(
"CDSVolCurve: start building constant volatility structure");
93 const QuantLib::ext::shared_ptr<MarketDatum>& md = loader.get(cvc.quote(), asof);
94 QL_REQUIRE(md->asofDate() == asof,
"MarketDatum asofDate '" << md->asofDate() <<
"' <> asof '" << asof <<
"'");
96 "MarketDatum instrument type '" << md->instrumentType() <<
"' <> 'MarketDatum::InstrumentType::INDEX_CDS_OPTION'");
97 QuantLib::ext::shared_ptr<IndexCDSOptionQuote> q = QuantLib::ext::dynamic_pointer_cast<IndexCDSOptionQuote>(md);
98 QL_REQUIRE(q,
"Internal error: could not downcast MarketDatum '" << md->name() <<
"' to IndexCDSOptionQuote");
99 QL_REQUIRE(q->name() == cvc.quote(),
100 "IndexCDSOptionQuote name '" << q->name() <<
"' <> ConstantVolatilityConfig quote '" << cvc.quote() <<
"'");
101 TLOG(
"Found the constant volatility quote " << q->name());
102 Handle<Quote> quote = q->quote();
104 DLOG(
"Creating CreditVolCurve structure");
106 std::map<std::tuple<QuantLib::Date, QuantLib::Period, QuantLib::Real>, QuantLib::Handle<QuantLib::Quote>> quotes;
107 quotes[std::make_tuple(asof + 1 * Years, 5 * Years,
strikeType_ == CreditVolCurve::Type::Price ? 1.0 : 0.0)] =
109 vol_ = QuantLib::ext::make_shared<QuantExt::InterpolatingCreditVolCurve>(
111 std::vector<Handle<QuantExt::CreditCurve>>{}, quotes,
strikeType_);
113 LOG(
"CDSVolCurve: finished building constant volatility structure");
119 LOG(
"CDSVolCurve: start building 1-D volatility curve");
122 QL_REQUIRE(vcc.
quotes().size() > 0,
"No quotes specified in config " << vc.
curveID());
129 std::map<std::tuple<QuantLib::Date, QuantLib::Period, QuantLib::Real>, QuantLib::Handle<QuantLib::Quote>> quotes;
134 DLOG(
"Have single quote with pattern " << wildcard->pattern());
137 for (
const auto& md : loader.
get(*wildcard, asof)) {
139 QL_REQUIRE(md->asofDate() == asof,
"MarketDatum asofDate '" << md->asofDate() <<
"' <> asof '" << asof <<
"'");
141 auto q = QuantLib::ext::dynamic_pointer_cast<IndexCDSOptionQuote>(md);
142 QL_REQUIRE(q,
"Internal error: could not downcast MarketDatum '" << md->name() <<
"' to IndexCDSOptionQuote");
144 TLOG(
"The quote " << q->name() <<
" matched the pattern");
151 QuantLib::Period quoteTerm;
152 if (q->indexTerm().empty()) {
153 if (vc.
terms().size() > 1)
155 quoteTerm = vc.
terms().empty() ? 5 * Years : vc.
terms().front();
158 if (std::find(vc.
terms().begin(), vc.
terms().end(), quoteTerm) == vc.
terms().end() &&
163 Date expiryDate =
getExpiry(asof, q->expiry());
164 if (expiryDate > asof) {
165 quotes[std::make_tuple(expiryDate, quoteTerm,
166 strikeType_ == CreditVolCurve::Type::Price ? 1.0 : 0.0)] = q->quote();
167 TLOG(
"Added quote " << q->name() <<
": (" << io::iso_date(expiryDate) <<
"," << fixed
168 << setprecision(9) << q->quote()->value() <<
")");
173 QL_REQUIRE(quotes.size() > 0,
"No quotes found matching regular expression " << vcc.
quotes()[0]);
177 DLOG(
"Have " << vcc.
quotes().size() <<
" explicit quotes");
180 std::ostringstream ss;
183 for (
const auto& md : loader.
get(w, asof)) {
185 QL_REQUIRE(md->asofDate() == asof,
"MarketDatum asofDate '" << md->asofDate() <<
"' <> asof '" << asof <<
"'");
187 auto q = QuantLib::ext::dynamic_pointer_cast<IndexCDSOptionQuote>(md);
188 QL_REQUIRE(q,
"Internal error: could not downcast MarketDatum '" << md->name() <<
"' to IndexCDSOptionQuote");
191 auto it = find(vcc.
quotes().begin(), vcc.
quotes().end(), q->name());
192 if (it != vcc.
quotes().end()) {
193 TLOG(
"Found the configured quote " << q->name());
195 Date expiryDate =
getExpiry(asof, q->expiry());
196 QL_REQUIRE(expiryDate > asof,
"CDS volatility quote '" << q->name() <<
"' has expiry in the past ("
197 << io::iso_date(expiryDate) <<
")");
199 QuantLib::Period quoteTerm;
200 if (q->indexTerm().empty()) {
201 quoteTerm = vc.
terms().size() == 1 ? vc.
terms().front() : 5 * Years;
205 quotes[std::make_tuple(expiryDate, quoteTerm,
206 strikeType_ == CreditVolCurve::Type::Price ? 1.0 : 0.0)] = q->quote();
207 TLOG(
"Added quote " << q->name() <<
": (" << io::iso_date(expiryDate) <<
"," << fixed
208 << setprecision(9) << q->quote()->value() <<
")");
213 QL_REQUIRE(quotes.size() == vcc.
quotes().size(),
"Found " << quotes.size() <<
" quotes, but "
215 <<
" quotes were given in config.");
218 DLOG(
"Creating InterpolatingCreditVolCurve object.");
219 vol_ = QuantLib::ext::make_shared<QuantExt::InterpolatingCreditVolCurve>(
221 std::vector<Handle<QuantExt::CreditCurve>>{}, quotes,
strikeType_);
223 LOG(
"CDSVolCurve: finished building 1-D volatility curve");
228 const std::map<std::string, QuantLib::ext::shared_ptr<DefaultCurve>>& requiredCdsCurves) {
230 LOG(
"CDSVolCurve: start building 2-D volatility absolute strike surface");
242 QL_REQUIRE(vssc.
expiries().size() == 1,
"Wild card expiry specified but more expiries also specified.");
243 DLOG(
"Have expiry wildcard pattern " << vssc.
expiries()[0]);
249 QL_REQUIRE(vssc.
strikes().size() == 1,
"Wild card strike specified but more strikes also specified.");
250 DLOG(
"Have strike wildcard pattern " << vssc.
strikes()[0]);
254 vector<Real> configuredStrikes;
257 configuredStrikes = parseVectorOfValues<Real>(vssc.
strikes(), &parseReal);
258 sort(configuredStrikes.begin(), configuredStrikes.end(), [](Real x, Real y) { return !close(x, y) && x < y; });
259 QL_REQUIRE(adjacent_find(configuredStrikes.begin(), configuredStrikes.end(),
260 [](Real x, Real y) { return close(x, y); }) == configuredStrikes.end(),
261 "The configured strikes contain duplicates");
262 DLOG(
"Parsed " << configuredStrikes.size() <<
" unique configured absolute strikes");
266 vector<QuantLib::ext::shared_ptr<Expiry>> configuredExpiries;
269 for (
const string& strExpiry : vssc.
expiries()) {
270 configuredExpiries.push_back(
parseExpiry(strExpiry));
272 DLOG(
"Parsed " << configuredExpiries.size() <<
" unique configured expiries");
276 if (!expWc && !strkWc) {
281 DLOG(
"Expiries and or strikes have been configured via wildcards so building a "
282 <<
"wildcard based absolute strike surface");
285 std::map<std::tuple<QuantLib::Date, QuantLib::Period, QuantLib::Real>, QuantLib::Handle<QuantLib::Quote>> quotes;
286 Size quotesAdded = 0;
289 std::ostringstream ss;
291 Wildcard w(ss.str());
292 for (
const auto& md : loader.
get(w, asof)) {
294 QL_REQUIRE(md->asofDate() == asof,
"MarketDatum asofDate '" << md->asofDate() <<
"' <> asof '" << asof <<
"'");
297 auto q = QuantLib::ext::dynamic_pointer_cast<IndexCDSOptionQuote>(md);
298 QL_REQUIRE(q,
"Internal error: could not downcast MarketDatum '" << md->name() <<
"' to IndexCDSOptionQuote");
305 auto strike = QuantLib::ext::dynamic_pointer_cast<AbsoluteStrike>(q->strike());
309 QuantLib::Period quoteTerm;
310 if (q->indexTerm().empty()) {
311 if (vc.
terms().size() > 1)
313 quoteTerm = vc.
terms().empty() ? 5 * Years : vc.
terms().front();
316 if (std::find(vc.
terms().begin(), vc.
terms().end(), quoteTerm) == vc.
terms().end() && !vc.
terms().empty())
323 auto expiryIt = find_if(configuredExpiries.begin(), configuredExpiries.end(),
324 [&q](QuantLib::ext::shared_ptr<Expiry> e) { return *e == *q->expiry(); });
325 if (expiryIt == configuredExpiries.end())
332 auto strikeIt = find_if(configuredStrikes.begin(), configuredStrikes.end(),
333 [&strike](Real s) { return close(s, strike->strike()); });
334 if (strikeIt == configuredStrikes.end())
339 quotes[std::make_tuple(
getExpiry(asof, q->expiry()), quoteTerm, strike->strike() / vc.
strikeFactor())] =
343 TLOG(
"Added quote " << q->name() <<
": (" << q->expiry() <<
"," << fixed << setprecision(9) << strike->strike()
344 <<
"," << q->quote()->value() <<
")");
347 LOG(
"CDSVolCurve: added " << quotesAdded <<
" quotes in building wildcard based absolute strike surface.");
348 QL_REQUIRE(quotesAdded > 0,
"No quotes loaded for " << vc.
curveID());
350 DLOG(
"Creating the CreditVolCurve object");
352 std::vector<QuantLib::Period> effTerms;
353 std::vector<QuantLib::Handle<CreditCurve>> termCurves;
357 auto t = requiredCdsCurves.find(vc.
termCurves()[i]);
358 QL_REQUIRE(t != requiredCdsCurves.end(),
"CDSVolCurve: required cds curve '"
360 <<
"' was not found during vol curve building.");
361 termCurves.push_back(Handle<QuantExt::CreditCurve>(t->second->creditCurve()));
362 effTerms.push_back(vc.
terms()[i]);
365 vol_ = QuantLib::ext::make_shared<QuantExt::InterpolatingCreditVolCurve>(asof,
calendar_, Following,
dayCounter_, effTerms,
367 vol_->enableExtrapolation();
369 LOG(
"CDSVolCurve: finished building 2-D volatility absolute strike surface");
374 const std::map<std::string, QuantLib::ext::shared_ptr<CDSVolCurve>>& requiredCdsVolCurves,
375 const std::map<std::string, QuantLib::ext::shared_ptr<DefaultCurve>>& requiredCdsCurves) {
376 LOG(
"CDSVolCurve: start building proxy volatility surface");
378 QL_REQUIRE(proxyVolCurve != requiredCdsVolCurves.end(),
"CDSVolCurve: Failed to find cds vol curve '"
381 std::vector<QuantLib::Period> effTerms;
382 std::vector<QuantLib::Handle<CreditCurve>> termCurves;
386 auto t = requiredCdsCurves.find(vc.
termCurves()[i]);
387 QL_REQUIRE(t != requiredCdsCurves.end(),
"CDSVolCurve: required cds curve '"
389 <<
"' was not found during vol curve building.");
390 termCurves.push_back(Handle<QuantExt::CreditCurve>(t->second->creditCurve()));
391 effTerms.push_back(vc.
terms()[i]);
393 LOG(
"Will use " << termCurves.size()
394 <<
" term curves in target surface to determine atm levels and moneyness-adjustments");
395 vol_ = QuantLib::ext::make_shared<QuantExt::ProxyCreditVolCurve>(
396 Handle<CreditVolCurve>(proxyVolCurve->second->volTermStructure()), effTerms, termCurves);
397 LOG(
"CDSVolCurve: finished building proxy volatility surface");
402 const vector<Real>& configuredStrikes,
403 const std::map<std::string, QuantLib::ext::shared_ptr<DefaultCurve>>& requiredCdsCurves) {
405 LOG(
"CDSVolCurve: start building 2-D volatility absolute strike surface with explicit strikes and expiries");
408 std::map<std::tuple<QuantLib::Date, QuantLib::Period, QuantLib::Real>, QuantLib::Handle<QuantLib::Quote>> quotes;
411 Size quotesAdded = 0;
414 std::ostringstream ss;
417 for (
const auto& md : loader.
get(w, asof)) {
419 QL_REQUIRE(md->asofDate() == asof,
"MarketDatum asofDate '" << md->asofDate() <<
"' <> asof '" << asof <<
"'");
422 auto q = QuantLib::ext::dynamic_pointer_cast<IndexCDSOptionQuote>(md);
423 QL_REQUIRE(q,
"Internal error: could not downcast MarketDatum '" << md->name() <<
"' to IndexCDSOptionQuote");
426 auto strike = QuantLib::ext::dynamic_pointer_cast<AbsoluteStrike>(q->strike());
430 QuantLib::Period quoteTerm;
431 if (q->indexTerm().empty()) {
432 if (vc.
terms().size() > 1)
434 quoteTerm = vc.
terms().empty() ? 5 * Years : vc.
terms().front();
437 if (std::find(vc.
terms().begin(), vc.
terms().end(), quoteTerm) == vc.
terms().end() && !vc.
terms().empty())
442 quotes[std::make_tuple(
getExpiry(asof, q->expiry()), quoteTerm, strike->strike() / vc.
strikeFactor())] =
446 TLOG(
"Added quote " << q->name() <<
": (" << q->expiry() <<
"," << fixed << setprecision(9) << strike->strike()
447 <<
"," << q->quote()->value() <<
")")
450 LOG(
"CDSVolCurve: added " << quotesAdded <<
" quotes in building explicit absolute strike surface.");
452 QL_REQUIRE(vc.
quotes().size() == quotesAdded,
453 "Found " << quotesAdded <<
" quotes, but " << vc.
quotes().size() <<
" quotes required by config.");
455 DLOG(
"Creating the CreditVolCurve object");
457 std::vector<QuantLib::Period> effTerms;
458 std::vector<QuantLib::Handle<CreditCurve>> termCurves;
462 auto t = requiredCdsCurves.find(vc.
termCurves()[i]);
463 QL_REQUIRE(t != requiredCdsCurves.end(),
"CDSVolCurve: required cds curve '"
465 <<
"' was not found during vol curve building.");
466 termCurves.push_back(Handle<QuantExt::CreditCurve>(t->second->creditCurve()));
467 effTerms.push_back(vc.
terms()[i]);
470 vol_ = QuantLib::ext::make_shared<QuantExt::InterpolatingCreditVolCurve>(asof,
calendar_, Following,
dayCounter_, effTerms,
472 vol_->enableExtrapolation();
474 LOG(
"CDSVolCurve: finished building 2-D volatility absolute strike "
475 <<
"surface with explicit strikes and expiries");
482 if (
auto expiryDate = QuantLib::ext::dynamic_pointer_cast<ExpiryDate>(expiry)) {
483 result = expiryDate->expiryDate();
484 }
else if (
auto expiryPeriod = QuantLib::ext::dynamic_pointer_cast<ExpiryPeriod>(expiry)) {
486 result =
calendar_.adjust(asof + expiryPeriod->expiryPeriod());
487 }
else if (
auto fcExpiry = QuantLib::ext::dynamic_pointer_cast<FutureContinuationExpiry>(expiry)) {
488 QL_FAIL(
"CDSVolCurve::getExpiry: future continuation expiry not supported for CDS volatility quotes.");
490 QL_FAIL(
"CDSVolCurve::getExpiry: cannot determine expiry type.");
Class for building cds volatility structures.
const std::string & cdsVolatilityCurve() const
CDSVolCurve()
Default constructor.
QuantLib::Date getExpiry(const QuantLib::Date &asof, const QuantLib::ext::shared_ptr< Expiry > &expiry) const
Get an explicit expiry date from a CDS option quote's Expiry.
QuantExt::CreditVolCurve::Type strikeType_
QuantLib::Calendar calendar_
QuantLib::ext::shared_ptr< QuantExt::CreditVolCurve > vol_
void buildVolatilityExplicit(const QuantLib::Date &asof, CDSVolatilityCurveConfig &vc, const VolatilityStrikeSurfaceConfig &vssc, const Loader &loader, const std::vector< QuantLib::Real > &configuredStrikes, const std::map< std::string, QuantLib::ext::shared_ptr< DefaultCurve > > &requiredCdsCurves)
const CDSVolatilityCurveSpec & spec() const
QuantLib::DayCounter dayCounter_
void buildVolatility(const QuantLib::Date &asof, const CDSVolatilityCurveConfig &vc, const ConstantVolatilityConfig &cvc, const Loader &loader)
Build a volatility structure from a single constant volatility quote.
const std::vector< QuantLib::Period > & terms() const
QuantLib::Real strikeFactor() const
const std::string & quoteName() const
const std::vector< std::string > & termCurves() const
CDS Volatility curve description.
const string & curveID() const
virtual const vector< string > & quotes()
Return all the market quotes required for this config.
const std::string & curveConfigID() const
string name() const
returns the unique curve name
Market data loader base class.
virtual QuantLib::ext::shared_ptr< MarketDatum > get(const std::string &name, const QuantLib::Date &d) const
get quote by its unique name, throws if not existent, override in derived classes for performance
const std::vector< std::string > & quotes() const
const std::vector< std::string > & expiries() const
const std::vector< std::string > & strikes() const
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
Period parsePeriod(const string &s)
Convert text to QuantLib::Period.
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
Classes and functions for log message handling.
#define LOG(text)
Logging Macro (Level = Notice)
#define DLOG(text)
Logging Macro (Level = Debug)
#define TLOG(text)
Logging Macro (Level = Data)
Size size(const ValueType &v)
QuantLib::ext::shared_ptr< Expiry > parseExpiry(const string &strExpiry)
Parse an Expiry from its string representation, strExpiry.
boost::optional< Wildcard > getUniqueWildcard(const C &c)
checks if at most one element in C has a wild card and returns it in this case
Serializable Credit Default Swap.
Map text representations to QuantLib/QuantExt types.
Error for market data or curve.
vector< string > curveConfigs
utilities for wildcard handling