24#include <ql/experimental/inflation/interpolatedyoyoptionletstripper.hpp>
25#include <ql/math/comparison.hpp>
26#include <ql/math/interpolations/bilinearinterpolation.hpp>
28#include <ql/math/matrix.hpp>
29#include <ql/termstructures/volatility/capfloor/capfloortermvolcurve.hpp>
30#include <ql/time/daycounters/actual365fixed.hpp>
50 map<
string, QuantLib::ext::shared_ptr<YieldCurve>>& yieldCurves,
51 map<
string, QuantLib::ext::shared_ptr<InflationCurve>>& inflationCurves) {
53 const QuantLib::ext::shared_ptr<InflationCapFloorVolatilityCurveConfig>& config =
56 auto it = yieldCurves.find(config->yieldTermStructure());
57 if (it != yieldCurves.end()) {
60 QL_FAIL(
"The yield term structure, " << config->yieldTermStructure()
61 <<
", required in the building "
63 <<
spec.
name() <<
", was not found.");
66 switch (config->quoteType()) {
74 QL_FAIL(
"unexpected quote type");
77 }
catch (std::exception& e) {
78 QL_FAIL(
"inflation cap/floor vol curve building failed :" << e.what());
80 QL_FAIL(
"inflation cap/floor vol curve building failed: unknown error");
86 const QuantLib::ext::shared_ptr<InflationCapFloorVolatilityCurveConfig>& config,
87 map<
string, QuantLib::ext::shared_ptr<YieldCurve>>& yieldCurves,
88 map<
string, QuantLib::ext::shared_ptr<InflationCurve>>& inflationCurves) {
90 DLOG(
"Build InflationCapFloorVolCurve " <<
spec.
name() <<
" from vols");
94 VolatilityType quoteVolatilityType;
96 switch (config->volatilityType()) {
99 quoteVolatilityType = ShiftedLognormal;
103 quoteVolatilityType = Normal;
107 quoteVolatilityType = ShiftedLognormal;
110 QL_FAIL(
"unexpected volatility type");
114 DLOG(
"Read quotes matrix");
115 vector<Period> tenors = parseVectorOfValues<Period>(config->tenors(), &
parsePeriod);
116 vector<double>
strikes = parseVectorOfValues<Real>(config->strikes(), &
parseReal);
117 QL_REQUIRE(!
strikes.empty(),
"Strikes should not be empty - expect a cap matrix");
118 Matrix vols(tenors.size(),
strikes.size());
119 vector<vector<bool>> found(tenors.size(), vector<bool>(
strikes.size()));
120 Size remainingQuotes = tenors.size() *
strikes.size();
124 string quoteIndex = config->quoteIndex().empty() ? config->index() : config->quoteIndex();
129 std::ostringstream ss1;
132 auto data1 = loader.
get(w1, asof);
134 std::ostringstream ss2;
137 auto data2 = loader.
get(w2, asof);
141 for (
const auto& md : data1) {
142 QL_REQUIRE(md->asofDate() == asof,
"MarketDatum asofDate '" << md->asofDate() <<
"' <> asof '" << asof <<
"'");
144 QuantLib::ext::shared_ptr<InflationCapFloorQuote> q;
147 q = QuantLib::ext::dynamic_pointer_cast<ZcInflationCapFloorQuote>(md);
149 q = QuantLib::ext::dynamic_pointer_cast<YyInflationCapFloorQuote>(md);
152 if (q && q->index() == quoteIndex && q->quoteType() ==
volatilityType) {
157 Size i = std::find(tenors.begin(), tenors.end(), q->term()) - tenors.begin();
159 [&strike](
const double& s) { return close_enough(s, strike); }) -
162 if (i < tenors.size() && j <
strikes.size()) {
163 vols[i][j] = q->quote()->value();
173 LOG(
"InflationCapFloorVolCurve: read " << quotesRead <<
" vols");
176 size_t filledValues = 0;
177 if (remainingQuotes != 0) {
179 for (
size_t i = 0; i < tenors.size(); ++i) {
180 std::vector<double> xs;
181 std::vector<double> ys;
182 std::vector<size_t> missingIds;
183 for (
size_t j = 0; j <
strikes.size(); ++j) {
186 ys.push_back(vols[i][j]);
188 missingIds.push_back(j);
191 if (!missingIds.empty() && xs.size() >= 1) {
192 if (xs.size() == 1) {
193 xs.push_back(xs.front() + 0.01);
194 ys.push_back(ys.front());
197 for (
auto j : missingIds) {
200 WLOG(
"vol for cap floor price surface, strike " <<
strikes[j] <<
", term " << tenors[i]
201 <<
", not found. Replace NULL with " <<
value);
209 if (remainingQuotes - filledValues != 0){
212 m <<
"Not all quotes found for spec " <<
spec << endl;
213 if (remainingQuotes != 0) {
214 m <<
"Found vol data (*) and missing data (.):" << std::endl;
215 for (Size i = 0; i < tenors.size(); ++i) {
216 for (Size j = 0; j <
strikes.size(); ++j) {
217 m << (found[i][j] ?
"*" :
".");
219 if (i < tenors.size() - 1)
224 QL_FAIL(
"could not build inflation cap/floor vol curve");
230 QuantLib::ext::shared_ptr<QuantLib::CapFloorTermVolSurface> capVol =
231 QuantLib::ext::make_shared<QuantLib::CapFloorTermVolSurface>(0, config->calendar(), config->businessDayConvention(),
232 tenors,
strikes, vols, config->dayCounter());
234 QuantLib::ext::shared_ptr<YoYInflationIndex> index;
235 auto it2 = inflationCurves.find(config->indexCurve());
236 if (it2 != inflationCurves.end()) {
237 QuantLib::ext::shared_ptr<InflationTermStructure> ts = it2->second->inflationTermStructure();
239 QuantLib::ext::shared_ptr<YoYInflationTermStructure> yyTs =
240 QuantLib::ext::dynamic_pointer_cast<YoYInflationTermStructure>(ts);
241 QL_REQUIRE(yyTs,
"YoY Inflation curve required for vol surface " << index->name());
242 index = QuantLib::ext::make_shared<QuantExt::YoYInflationIndexWrapper>(
244 true, Handle<YoYInflationTermStructure>(yyTs));
247 QuantLib::ext::shared_ptr<YoYInflationOptionletVolStripper> volStripper =
248 QuantLib::ext::make_shared<YoYInflationOptionletVolStripper>(capVol, index,
discountCurve_, quoteVolatilityType);
253 DLOG(
"Building InflationCapFloorVolatilityCurveConfig::Type::ZC");
254 std::vector<std::vector<Handle<Quote>>> quotes(tenors.size(),
255 std::vector<Handle<Quote>>(
strikes.size(), Handle<Quote>()));
256 for (Size i = 0; i < tenors.size(); ++i) {
257 for (Size j = 0; j <
strikes.size(); ++j) {
258 QuantLib::ext::shared_ptr<SimpleQuote> q(
new SimpleQuote(vols[i][j]));
259 quotes[i][j] = Handle<Quote>(q);
263 DLOG(
"Building zero inflation index");
264 QuantLib::ext::shared_ptr<ZeroInflationIndex> index;
265 auto it2 = inflationCurves.find(config->indexCurve());
266 if (it2 == inflationCurves.end()) {
267 QL_FAIL(
"The zero inflation curve, " << config->indexCurve()
268 <<
", required in building the inflation cap floor vol surface "
269 <<
spec.
name() <<
", was not found");
271 QuantLib::ext::shared_ptr<ZeroInflationTermStructure> ts =
272 QuantLib::ext::dynamic_pointer_cast<ZeroInflationTermStructure>(it2->second->inflationTermStructure());
274 "inflation term structure " << config->indexCurve() <<
" was expected to be zero, but is not");
278 DLOG(
"Building surface");
279 Date startDate = Date();
280 const QuantLib::ext::shared_ptr<Conventions>& conventions = InstrumentConventions::instance().conventions();
281 bool interpolated =
false;
282 if (!config->conventions().empty() && conventions->has(config->conventions())) {
283 QuantLib::ext::shared_ptr<InflationSwapConvention> conv =
284 QuantLib::ext::dynamic_pointer_cast<InflationSwapConvention>(conventions->get(config->conventions()));
286 interpolated = conv->interpolated();
289 cpiVolSurface_ = QuantLib::ext::make_shared<InterpolatedCPIVolatilitySurface<Bilinear>>(
290 tenors,
strikes, quotes, index, interpolated, config->settleDays(), config->calendar(),
291 config->businessDayConvention(), config->dayCounter(), config->observationLag(), startDate, Bilinear(),
292 quoteVolatilityType, 0.0);
293 if (config->extrapolate())
295 DLOG(
"Building surface done");
297 QL_FAIL(
"InflationCapFloorVolatilityCurveConfig::Type not covered");
303 const QuantLib::ext::shared_ptr<InflationCapFloorVolatilityCurveConfig>& config,
304 map<
string, QuantLib::ext::shared_ptr<YieldCurve>>& yieldCurves,
305 map<
string, QuantLib::ext::shared_ptr<InflationCurve>>& inflationCurves) {
307 DLOG(
"Build InflationCapFloorVolCurve " <<
spec.
name() <<
" from prices");
311 "Inflation cap floor pricevolatility surfaces must be of type 'ZC' or 'YY'");
314 VolatilityType quoteVolatilityType;
316 switch (config->volatilityType()) {
318 quoteVolatilityType = ShiftedLognormal;
321 quoteVolatilityType = Normal;
324 quoteVolatilityType = ShiftedLognormal;
327 QL_FAIL(
"unexpected volatility type: '" << config->volatilityType() <<
"'");
331 std::vector<Period> terms = parseVectorOfValues<Period>(config->tenors(), &
parsePeriod);
332 std::vector<Real> capStrikes = parseVectorOfValues<Real>(config->capStrikes(), &
parseReal);
333 std::vector<Real> floorStrikes = parseVectorOfValues<Real>(config->floorStrikes(), &
parseReal);
335 Matrix cPrice(capStrikes.size(), capStrikes.size() == 0 ? 0 : terms.size(), Null<Real>()),
336 fPrice(floorStrikes.size(), floorStrikes.size() == 0 ? 0 : terms.size(), Null<Real>());
339 string quoteIndex = config->quoteIndex().empty() ? config->index() : config->quoteIndex();
343 std::ostringstream ss1;
346 auto data1 = loader.
get(w1, asof);
348 std::ostringstream ss2;
351 auto data2 = loader.
get(w2, asof);
355 for (
const auto& md : data1) {
356 QL_REQUIRE(md->asofDate() == asof,
"MarketDatum asofDate '" << md->asofDate() <<
"' <> asof '" << asof <<
"'");
358 QuantLib::ext::shared_ptr<InflationCapFloorQuote> q;
361 q = QuantLib::ext::dynamic_pointer_cast<ZcInflationCapFloorQuote>(md);
363 q = QuantLib::ext::dynamic_pointer_cast<YyInflationCapFloorQuote>(md);
367 auto it1 = std::find(terms.begin(), terms.end(), q->term());
369 Size strikeIdx = Null<Size>();
371 for (Size i = 0; i < capStrikes.size(); ++i) {
376 for (Size i = 0; i < floorStrikes.size(); ++i) {
381 if (it1 != terms.end() && strikeIdx != Null<Size>()) {
383 cPrice[strikeIdx][it1 - terms.begin()] = q->quote()->value();
385 fPrice[strikeIdx][it1 - terms.begin()] = q->quote()->value();
391 ostringstream capStrikesString, floorStrikesString;
392 for (Size i = 0; i < floorStrikes.size(); ++i)
393 floorStrikesString << floorStrikes[i] <<
",";
394 for (Size i = 0; i < capStrikes.size(); ++i)
395 capStrikesString << capStrikes[i] <<
",";
396 DLOG(
"Building inflation cap floor price surface:");
397 DLOG(
"Cap Strikes are: " << capStrikesString.str());
398 DLOG(
"Floor Strikes are: " << floorStrikesString.str());
399 DLOGGERSTREAM(
"Cap Price Matrix:\n" << cPrice <<
"Floor Price Matrix:\n" << fPrice);
403 QuantLib::ext::shared_ptr<ZeroInflationIndex> index;
404 auto it2 = inflationCurves.find(config->indexCurve());
405 if (it2 == inflationCurves.end()) {
406 QL_FAIL(
"The zero inflation curve, " << config->indexCurve()
407 <<
", required in building the inflation cap floor price surface "
408 <<
spec.
name() <<
", was not found");
410 QuantLib::ext::shared_ptr<ZeroInflationTermStructure> ts =
411 QuantLib::ext::dynamic_pointer_cast<ZeroInflationTermStructure>(it2->second->inflationTermStructure());
413 "inflation term structure " << config->indexCurve() <<
" was expected to be zero, but is not");
416 QuantLib::ext::shared_ptr<QuantExt::CPICapFloorEngine> engine;
418 bool isLogNormalVol = quoteVolatilityType == QuantLib::ShiftedLognormal;
420 if (isLogNormalVol) {
421 engine = QuantLib::ext::make_shared<QuantExt::CPIBlackCapFloorEngine>(
422 discountCurve_, QuantLib::Handle<QuantLib::CPIVolatilitySurface>(),
423 config->useLastAvailableFixingDate());
425 engine = QuantLib::ext::make_shared<QuantExt::CPIBachelierCapFloorEngine>(
426 discountCurve_, QuantLib::Handle<QuantLib::CPIVolatilitySurface>(),
427 config->useLastAvailableFixingDate());
431 const QuantLib::ext::shared_ptr<Conventions>& conventions = InstrumentConventions::instance().conventions();
432 Date startDate = Date();
433 bool interpolated =
false;
434 if (!config->conventions().empty() && conventions->has(config->conventions())) {
435 QuantLib::ext::shared_ptr<InflationSwapConvention> conv =
436 QuantLib::ext::dynamic_pointer_cast<InflationSwapConvention>(conventions->get(config->conventions()));
438 interpolated = conv->interpolated();
440 QuantLib::ext::shared_ptr<QuantExt::CPIPriceVolatilitySurface<Linear, Linear>> cpiCapFloorVolSurface;
444 bool ignoreMissingPrices =
true;
446 cpiCapFloorVolSurface = QuantLib::ext::make_shared<QuantExt::CPIPriceVolatilitySurface<Linear, Linear>>(
447 QuantExt::PriceQuotePreference::CapFloor, config->observationLag(), config->calendar(),
448 config->businessDayConvention(), config->dayCounter(), index,
discountCurve_, capStrikes, floorStrikes,
449 terms, cPrice, fPrice, engine, interpolated, startDate, ignoreMissingPrices,
true,
true,
459 for (Size i = 0; i < cpiCapFloorVolSurface->strikes().size(); ++i) {
460 for (Size j = 0; j < cpiCapFloorVolSurface->maturities().size(); ++j) {
461 DLOG(
"Implied CPI CapFloor BlackVol,strike," << cpiCapFloorVolSurface->strikes()[i] <<
",maturity,"
462 << cpiCapFloorVolSurface->maturities()[j] <<
",index,"
463 << index->name() <<
",Vol,"
464 << cpiCapFloorVolSurface->volData()[i][j]);
465 if (cpiCapFloorVolSurface->missingValues()[i][j]) {
466 WLOG(
"Implied CPI CapFloor Surface, price misisng for strike "
467 << cpiCapFloorVolSurface->strikes()[i] <<
", maturity, "
468 << cpiCapFloorVolSurface->maturities()[j] <<
",index," << index->name()
469 <<
", ignore missing point and try interpolate the missing vol.")
471 if (cpiCapFloorVolSurface->pricesFailedToConvert()[i][j]) {
472 WLOG(
"Implied CPI CapFloor Surface, couldn't imply vol from price for strike "
473 << cpiCapFloorVolSurface->strikes()[i] <<
", maturity, "
474 << cpiCapFloorVolSurface->maturities()[j] <<
",index," << index->name()
475 <<
"ignore missing point and try to interpolate the missing vol.");
480 }
catch (std::exception& e) {
481 QL_FAIL(
"Building CPIVolSurfaces failed for spec " <<
spec.
name() <<
": " << e.what());
486 for (Size j = 0; j < terms.size(); ++j) {
487 for (Size i = 0; i < capStrikes.size(); ++i)
488 QL_REQUIRE(cPrice[i][j] != Null<Real>(),
"quote for cap floor price surface, type cap, strike "
489 << capStrikes[i] <<
", term " << terms[j]
491 for (Size i = 0; i < floorStrikes.size(); ++i)
492 QL_REQUIRE(fPrice[i][j] != Null<Real>(),
"quote for cap floor price surface, type floor, strike "
493 << floorStrikes[i] <<
", term " << terms[j]
502 static const Real largeStrike = 1.0;
503 static const Real largeStrikeFactor = 0.99;
505 Size addFloor = 0, addCap = 0;
506 if (floorStrikes.size() == 0) {
507 floorStrikes.push_back(-largeStrike);
508 floorStrikes.push_back(-(largeStrike * largeStrikeFactor));
511 if (floorStrikes.size() == 1) {
512 floorStrikes.insert(floorStrikes.begin(), -largeStrike);
515 if (capStrikes.size() == 0) {
516 capStrikes.push_back(largeStrike * largeStrikeFactor);
517 capStrikes.push_back(largeStrike);
520 if (capStrikes.size() == 1) {
521 capStrikes.push_back(largeStrike);
525 Matrix tmp(fPrice.rows() + addFloor, terms.size(), 1E-10);
526 for (Size i = addFloor; i < fPrice.rows() + addFloor; ++i) {
527 for (Size j = 0; j < fPrice.columns(); ++j) {
528 tmp[i][j] = fPrice[i - addFloor][j];
534 Matrix tmp(cPrice.rows() + addCap, terms.size(), 1E-10);
535 for (Size i = 0; i < cPrice.rows(); ++i) {
536 for (Size j = 0; j < cPrice.columns(); ++j) {
537 tmp[i][j] = cPrice[i][j];
543 QuantLib::ext::shared_ptr<YoYInflationIndex> index;
544 auto it2 = inflationCurves.find(config->indexCurve());
545 if (it2 == inflationCurves.end()) {
546 QL_FAIL(
"The inflation curve, " << config->indexCurve()
547 <<
", required in building the inflation cap floor price surface "
548 <<
spec.
name() <<
", was not found");
550 QuantLib::ext::shared_ptr<InflationTermStructure> ts = it2->second->inflationTermStructure();
552 QuantLib::ext::shared_ptr<YoYInflationTermStructure> yyTs =
553 QuantLib::ext::dynamic_pointer_cast<YoYInflationTermStructure>(ts);
557 index = QuantLib::ext::make_shared<QuantExt::YoYInflationIndexWrapper>(
559 true, Handle<YoYInflationTermStructure>(yyTs));
562 QuantLib::ext::shared_ptr<ZeroInflationTermStructure> zeroTs =
563 QuantLib::ext::dynamic_pointer_cast<ZeroInflationTermStructure>(ts);
565 "Inflation term structure " << config->indexCurve() <<
"must be of type YoY or Zero");
566 index = QuantLib::ext::make_shared<QuantExt::YoYInflationIndexWrapper>(
568 true, Handle<YoYInflationTermStructure>());
572 Rate startRate = 0.0;
574 QuantLib::ext::shared_ptr<QuantExt::InterpolatedYoYCapFloorTermPriceSurface<QuantLib::Bilinear, QuantLib::Linear>>
575 yoySurface = QuantLib::ext::make_shared<
577 0, config->observationLag(), index, startRate,
discountCurve_, config->dayCounter(), config->calendar(),
578 config->businessDayConvention(), capStrikes, floorStrikes, terms, cPrice, fPrice);
580 std::vector<Period> optionletTerms = {yoySurface->maturities().front()};
581 while (optionletTerms.back() != terms.back()) {
582 optionletTerms.push_back(optionletTerms.back() + Period(1, Years));
587 QuantLib::ext::shared_ptr<InterpolatedYoYOptionletStripper<QuantLib::Linear>> yoyStripper =
588 QuantLib::ext::make_shared<InterpolatedYoYOptionletStripper<QuantLib::Linear>>();
591 QuantLib::ext::shared_ptr<QuantLib::YoYOptionletVolatilitySurface> ovs =
592 QuantLib::ext::dynamic_pointer_cast<QuantLib::YoYOptionletVolatilitySurface>(
593 QuantLib::ext::make_shared<QuantLib::ConstantYoYOptionletVolatility>(
594 0.0, yoySurface->settlementDays(), yoySurface->calendar(), yoySurface->businessDayConvention(),
595 yoySurface->dayCounter(), yoySurface->observationLag(), yoySurface->frequency(),
596 yoySurface->indexIsInterpolated()));
597 Handle<QuantLib::YoYOptionletVolatilitySurface> hovs(ovs);
600 yoyTs_ = yoySurface->YoYTS();
601 QuantLib::ext::shared_ptr<YoYInflationIndex> yoyIndex = index->clone(Handle<YoYInflationTermStructure>(
yoyTs_));
603 QuantLib::ext::shared_ptr<YoYInflationBachelierCapFloorEngine> cfEngine =
604 QuantLib::ext::make_shared<YoYInflationBachelierCapFloorEngine>(yoyIndex, hovs,
discountCurve_);
606 yoyVolSurface_ = QuantLib::ext::make_shared<QuantExt::KInterpolatedYoYOptionletVolatilitySurface<Linear>>(
607 yoySurface->settlementDays(), yoySurface->calendar(), yoySurface->businessDayConvention(),
608 yoySurface->dayCounter(), yoySurface->observationLag(), yoySurface, cfEngine, yoyStripper, 0, Linear(),
609 VolatilityType::Normal);
void setMaturities(std::vector< Period > &overrideMaturities)
Interpolation interpolate(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin) const
Container class for all Curve Configurations.
const std::string & curveConfigID() const
string name() const
returns the unique curve name
Handle< YieldTermStructure > discountCurve_
QuantLib::ext::shared_ptr< InflationTermStructure > surface_
const InflationCapFloorVolatilityCurveSpec & spec() const
void buildFromVolatilities(Date asof, InflationCapFloorVolatilityCurveSpec spec, const Loader &loader, const QuantLib::ext::shared_ptr< InflationCapFloorVolatilityCurveConfig > &config, map< string, QuantLib::ext::shared_ptr< YieldCurve > > &yieldCurves, map< string, QuantLib::ext::shared_ptr< InflationCurve > > &inflationCurves)
InflationCapFloorVolCurve()
void buildFromPrices(Date asof, InflationCapFloorVolatilityCurveSpec spec, const Loader &loader, const QuantLib::ext::shared_ptr< InflationCapFloorVolatilityCurveConfig > &config, map< string, QuantLib::ext::shared_ptr< YieldCurve > > &yieldCurves, map< string, QuantLib::ext::shared_ptr< InflationCurve > > &inflationCurves)
QuantLib::ext::shared_ptr< QuantExt::YoYOptionletVolatilitySurface > yoyVolSurface_
QuantLib::ext::shared_ptr< YoYInflationTermStructure > yoyTs_
QuantLib::ext::shared_ptr< QuantLib::CPIVolatilitySurface > cpiVolSurface_
Inflation cap floor volatility description.
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
QuoteType
Supported market quote types.
SafeStack< ValueType > value
QuantLib::ext::shared_ptr< ZeroInflationIndex > parseZeroInflationIndex(const string &s, const Handle< ZeroInflationTermStructure > &h)
Convert std::string to QuantLib::ZeroInflationIndex.
Period parsePeriod(const string &s)
Convert text to QuantLib::Period.
Real parseReal(const string &s)
Convert text to Real.
Map text representations to QuantLib/QuantExt types.
Wrapper class for building YoY Inflation CapFloor volatility structures.
Classes and functions for log message handling.
#define LOG(text)
Logging Macro (Level = Notice)
#define DLOG(text)
Logging Macro (Level = Debug)
#define WLOG(text)
Logging Macro (Level = Warning)
#define DLOGGERSTREAM(text)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
VolatilityType volatilityType(CapFloorVolatilityCurveConfig::VolatilityType type)
Imply QuantLib::VolatilityType from CapFloorVolatilityCurveConfig::VolatilityType.
std::pair< QuantLib::Date, QuantLib::Period > getStartAndLag(const QuantLib::Date &asof, const InflationSwapConvention &conv)
Serializable Credit Default Swap.
static constexpr QuantLib::Real upperVolBound
static constexpr QuantLib::Real lowerVolBound
static constexpr QuantLib::Real solverTolerance
vector< string > curveConfigs