Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Public Member Functions | List of all members
CapFloorVolCurve Class Reference

#include <ored/marketdata/capfloorvolcurve.hpp>

+ Collaboration diagram for CapFloorVolCurve:

Public Member Functions

 CapFloorVolCurve ()
 Default constructor. More...
 
 CapFloorVolCurve (const QuantLib::Date &asof, const CapFloorVolatilityCurveSpec &spec, const Loader &loader, const CurveConfigurations &curveConfigs, QuantLib::ext::shared_ptr< QuantLib::IborIndex > iborIndex, QuantLib::Handle< QuantLib::YieldTermStructure > discountCurve, const QuantLib::ext::shared_ptr< IborIndex > sourceIndex, const QuantLib::ext::shared_ptr< IborIndex > targetIndex, const std::map< std::string, std::pair< QuantLib::ext::shared_ptr< ore::data::CapFloorVolCurve >, std::pair< std::string, QuantLib::Period > > > &requiredCapFloorVolCurves, const bool buildCalibrationInfo)
 Detailed constructor. More...
 

Inspectors

CapFloorVolatilityCurveSpec spec_
 
QuantLib::ext::shared_ptr< QuantLib::OptionletVolatilityStructure > capletVol_
 
QuantLib::ext::shared_ptr< IrVolCalibrationInfocalibrationInfo_
 
const CapFloorVolatilityCurveSpecspec () const
 The cap floor curve specification. More...
 
const QuantLib::ext::shared_ptr< QuantLib::OptionletVolatilityStructure > & capletVolStructure () const
 The result of building the optionlet structure that has been configured. More...
 
QuantLib::ext::shared_ptr< IrVolCalibrationInfocalibrationInfo () const
 
void termAtmOptCurve (const QuantLib::Date &asof, CapFloorVolatilityCurveConfig &config, const Loader &loader, QuantLib::ext::shared_ptr< QuantLib::IborIndex > iborIndex, QuantLib::Handle< QuantLib::YieldTermStructure > discountCurve, QuantLib::Real shift)
 Build ATM optionlet curve from term vol. More...
 
void termOptSurface (const QuantLib::Date &asof, CapFloorVolatilityCurveConfig &config, const Loader &loader, QuantLib::ext::shared_ptr< QuantLib::IborIndex > iborIndex, QuantLib::Handle< QuantLib::YieldTermStructure > discountCurve, QuantLib::Real shift)
 Build optionlet surface from term vol. More...
 
void optAtmOptCurve (const QuantLib::Date &asof, CapFloorVolatilityCurveConfig &config, const Loader &loader, QuantLib::ext::shared_ptr< QuantLib::IborIndex > iborIndex, QuantLib::Handle< QuantLib::YieldTermStructure > discountCurve, QuantLib::Real shift)
 Build ATM optionlet curve from optionlet vol. More...
 
void optOptSurface (const QuantLib::Date &asof, CapFloorVolatilityCurveConfig &config, const Loader &loader, QuantLib::ext::shared_ptr< QuantLib::IborIndex > iborIndex, QuantLib::Handle< QuantLib::YieldTermStructure > discountCurve, QuantLib::Real shift)
 Build optionlet surface from optionlet vol. More...
 
QuantLib::ext::shared_ptr< QuantExt::CapFloorTermVolSurfacecapSurface (const QuantLib::Date &asof, CapFloorVolatilityCurveConfig &config, const Loader &loader) const
 Build a cap floor term volatility surface. More...
 
QuantLib::ext::shared_ptr< QuantExt::CapFloorTermVolCurveatmCurve (const QuantLib::Date &asof, CapFloorVolatilityCurveConfig &config, const Loader &loader) const
 Build an ATM cap floor term volatility curve. More...
 
void buildProxyCurve (const CapFloorVolatilityCurveConfig &config, const QuantLib::ext::shared_ptr< IborIndex > &sourceIndex, const QuantLib::ext::shared_ptr< IborIndex > &targetIndex, const std::map< std::string, std::pair< QuantLib::ext::shared_ptr< ore::data::CapFloorVolCurve >, std::pair< std::string, QuantLib::Period > > > &requiredCapFloorVolCurves)
 Build proxy curve. More...
 
Real shiftQuote (const QuantLib::Date &asof, CapFloorVolatilityCurveConfig &config, const Loader &loader) const
 Get a shift quote value from the configured quotes. More...
 
QuantLib::ext::shared_ptr< QuantLib::StrippedOptionlet > transform (const QuantExt::OptionletStripper &os) const
 Transform QuantExt::OptionletStripper to QuantLib::StrippedOptionlet. More...
 
QuantLib::ext::shared_ptr< QuantLib::StrippedOptionlet > transform (const QuantLib::Date &asof, std::vector< QuantLib::Date > dates, const std::vector< QuantLib::Volatility > &volatilities, QuantLib::Natural settleDays, const QuantLib::Calendar &cal, QuantLib::BusinessDayConvention bdc, QuantLib::ext::shared_ptr< QuantLib::IborIndex > iborIndex, const QuantLib::DayCounter &dc, QuantLib::VolatilityType type, QuantLib::Real displacement) const
 Create a stripped optionlet curve from ATM optionlet dates and optionlet vols. More...
 
vector< Date > populateFixingDates (const QuantLib::Date &asof, CapFloorVolatilityCurveConfig &config, QuantLib::ext::shared_ptr< QuantLib::IborIndex > iborIndex, const vector< Period > &configTenors)
 Generate fixing days from end date for optionlet vol. More...
 
void buildCalibrationInfo (const Date &asof, const CurveConfigurations &curveConfigs, const QuantLib::ext::shared_ptr< CapFloorVolatilityCurveConfig > config, const QuantLib::ext::shared_ptr< IborIndex > &iborIndex)
 Build calibration info. More...
 

Detailed Description

Class for building optionlet volatility structures from cap floor configurations

Definition at line 42 of file capfloorvolcurve.hpp.

Constructor & Destructor Documentation

◆ CapFloorVolCurve() [1/2]

Default constructor.

Definition at line 46 of file capfloorvolcurve.hpp.

46{}

◆ CapFloorVolCurve() [2/2]

CapFloorVolCurve ( const QuantLib::Date &  asof,
const CapFloorVolatilityCurveSpec spec,
const Loader loader,
const CurveConfigurations curveConfigs,
QuantLib::ext::shared_ptr< QuantLib::IborIndex >  iborIndex,
QuantLib::Handle< QuantLib::YieldTermStructure >  discountCurve,
const QuantLib::ext::shared_ptr< IborIndex sourceIndex,
const QuantLib::ext::shared_ptr< IborIndex targetIndex,
const std::map< std::string, std::pair< QuantLib::ext::shared_ptr< ore::data::CapFloorVolCurve >, std::pair< std::string, QuantLib::Period > > > &  requiredCapFloorVolCurves,
const bool  buildCalibrationInfo 
)

Detailed constructor.

Member Function Documentation

◆ spec()

const CapFloorVolatilityCurveSpec & spec ( ) const

The cap floor curve specification.

Definition at line 61 of file capfloorvolcurve.hpp.

61{ return spec_; }
CapFloorVolatilityCurveSpec spec_

◆ capletVolStructure()

const QuantLib::ext::shared_ptr< QuantLib::OptionletVolatilityStructure > & capletVolStructure ( ) const

The result of building the optionlet structure that has been configured.

Definition at line 64 of file capfloorvolcurve.hpp.

64{ return capletVol_; }
QuantLib::ext::shared_ptr< QuantLib::OptionletVolatilityStructure > capletVol_

◆ calibrationInfo()

QuantLib::ext::shared_ptr< IrVolCalibrationInfo > calibrationInfo ( ) const

Definition at line 65 of file capfloorvolcurve.hpp.

65{ return calibrationInfo_; }
QuantLib::ext::shared_ptr< IrVolCalibrationInfo > calibrationInfo_

◆ termAtmOptCurve()

void termAtmOptCurve ( const QuantLib::Date &  asof,
CapFloorVolatilityCurveConfig config,
const Loader loader,
QuantLib::ext::shared_ptr< QuantLib::IborIndex >  iborIndex,
QuantLib::Handle< QuantLib::YieldTermStructure >  discountCurve,
QuantLib::Real  shift 
)
private

Build ATM optionlet curve from term vol.

Definition at line 150 of file capfloorvolcurve.cpp.

152 {
153
154 // Get the ATM cap floor term vol curve
155 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolCurve> cftvc = atmCurve(asof, config, loader);
156
157 // Hardcode some values. Can add them to the CapFloorVolatilityCurveConfig later if needed.
158 bool flatFirstPeriod = true;
159 VolatilityType optVolType = Normal;
160 Real optDisplacement = 0.0;
161
162 // Get configuration values for bootstrap
163 Real accuracy = config.bootstrapConfig().accuracy();
164 Real globalAccuracy = config.bootstrapConfig().globalAccuracy();
165 bool dontThrow = config.bootstrapConfig().dontThrow();
166 Size maxAttempts = config.bootstrapConfig().maxAttempts();
167 Real maxFactor = config.bootstrapConfig().maxFactor();
168 Real minFactor = config.bootstrapConfig().minFactor();
169 Size dontThrowSteps = config.bootstrapConfig().dontThrowSteps();
170
171 // On optionlets is the newly added interpolation approach whereas on term volatilities is legacy
172 bool onOpt = interpOnOpt(config);
173 if (onOpt) {
174 // This is not pretty but can't think of a better way (with template functions and or classes)
175 // Note: second template argument in StrippedOptionletAdapter doesn't matter so just use Linear here.
176 if (config.timeInterpolation() == "Linear") {
177 QuantLib::ext::shared_ptr<PiecewiseAtmOptionletCurve<Linear>> tmp =
178 QuantLib::ext::make_shared<PiecewiseAtmOptionletCurve<Linear>>(
179 config.settleDays(), cftvc, index, discountCurve, flatFirstPeriod,
180 volatilityType(config.volatilityType()), shift, optVolType, optDisplacement, onOpt, Linear(),
183 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps));
184 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, Linear>>(
185 asof, transform(asof, tmp->curve()->dates(), tmp->curve()->volatilities(), tmp->settlementDays(),
186 tmp->calendar(), tmp->businessDayConvention(), index, tmp->dayCounter(),
187 tmp->volatilityType(), tmp->displacement()));
188 } else if (config.timeInterpolation() == "LinearFlat") {
189 QuantLib::ext::shared_ptr<PiecewiseAtmOptionletCurve<LinearFlat>> tmp =
190 QuantLib::ext::make_shared<PiecewiseAtmOptionletCurve<LinearFlat>>(
191 config.settleDays(), cftvc, index, discountCurve, flatFirstPeriod,
192 volatilityType(config.volatilityType()), shift, optVolType, optDisplacement, onOpt, LinearFlat(),
195 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps));
196 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, Linear>>(
197 asof, transform(asof, tmp->curve()->dates(), tmp->curve()->volatilities(), tmp->settlementDays(),
198 tmp->calendar(), tmp->businessDayConvention(), index, tmp->dayCounter(),
199 tmp->volatilityType(), tmp->displacement()));
200 } else if (config.timeInterpolation() == "BackwardFlat") {
201 QuantLib::ext::shared_ptr<PiecewiseAtmOptionletCurve<BackwardFlat>> tmp =
202 QuantLib::ext::make_shared<PiecewiseAtmOptionletCurve<BackwardFlat>>(
203 config.settleDays(), cftvc, index, discountCurve, flatFirstPeriod,
204 volatilityType(config.volatilityType()), shift, optVolType, optDisplacement, onOpt, BackwardFlat(),
207 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps));
208 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<BackwardFlat, Linear>>(
209 asof, transform(asof, tmp->curve()->dates(), tmp->curve()->volatilities(), tmp->settlementDays(),
210 tmp->calendar(), tmp->businessDayConvention(), index, tmp->dayCounter(),
211 tmp->volatilityType(), tmp->displacement()));
212 } else if (config.timeInterpolation() == "Cubic") {
213 QuantLib::ext::shared_ptr<PiecewiseAtmOptionletCurve<Cubic>> tmp =
214 QuantLib::ext::make_shared<PiecewiseAtmOptionletCurve<Cubic>>(
215 config.settleDays(), cftvc, index, discountCurve, flatFirstPeriod,
216 volatilityType(config.volatilityType()), shift, optVolType, optDisplacement, onOpt, Cubic(),
219 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps));
220 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, Linear>>(
221 asof, transform(asof, tmp->curve()->dates(), tmp->curve()->volatilities(), tmp->settlementDays(),
222 tmp->calendar(), tmp->businessDayConvention(), index, tmp->dayCounter(),
223 tmp->volatilityType(), tmp->displacement()));
224 } else if (config.timeInterpolation() == "CubicFlat") {
225 QuantLib::ext::shared_ptr<PiecewiseAtmOptionletCurve<CubicFlat>> tmp =
226 QuantLib::ext::make_shared<PiecewiseAtmOptionletCurve<CubicFlat>>(
227 config.settleDays(), cftvc, index, discountCurve, flatFirstPeriod,
228 volatilityType(config.volatilityType()), shift, optVolType, optDisplacement, onOpt, CubicFlat(),
231 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps));
232 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, Linear>>(
233 asof, transform(asof, tmp->curve()->dates(), tmp->curve()->volatilities(), tmp->settlementDays(),
234 tmp->calendar(), tmp->businessDayConvention(), index, tmp->dayCounter(),
235 tmp->volatilityType(), tmp->displacement()));
236 } else {
237 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected time interpolation "
238 << config.timeInterpolation());
239 }
240 } else {
241 // Legacy method where we interpolate on the term volatilities.
242 // We don't need time interpolation in this instance - we just use the term volatility interpolation.
243 if (config.interpolationMethod() == CftvsInterp::BicubicSpline) {
244 if (config.flatExtrapolation()) {
245 QuantLib::ext::shared_ptr<PiecewiseAtmOptionletCurve<CubicFlat>> tmp =
246 QuantLib::ext::make_shared<PiecewiseAtmOptionletCurve<CubicFlat>>(
247 config.settleDays(), cftvc, index, discountCurve, flatFirstPeriod,
248 volatilityType(config.volatilityType()), shift, optVolType, optDisplacement, onOpt, CubicFlat(),
251 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps));
252 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, Linear>>(
253 asof, transform(asof, tmp->curve()->dates(), tmp->curve()->volatilities(), tmp->settlementDays(),
254 tmp->calendar(), tmp->businessDayConvention(), index, tmp->dayCounter(),
255 tmp->volatilityType(), tmp->displacement()));
256 } else {
257 QuantLib::ext::shared_ptr<PiecewiseAtmOptionletCurve<Cubic>> tmp =
258 QuantLib::ext::make_shared<PiecewiseAtmOptionletCurve<Cubic>>(
259 config.settleDays(), cftvc, index, discountCurve, flatFirstPeriod,
260 volatilityType(config.volatilityType()), shift, optVolType, optDisplacement, onOpt, Cubic(),
263 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps));
264 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, Linear>>(
265 asof, transform(asof, tmp->curve()->dates(), tmp->curve()->volatilities(), tmp->settlementDays(),
266 tmp->calendar(), tmp->businessDayConvention(), index, tmp->dayCounter(),
267 tmp->volatilityType(), tmp->displacement()));
268 }
269 } else if (config.interpolationMethod() == CftvsInterp::Bilinear) {
270 if (config.flatExtrapolation()) {
271 QuantLib::ext::shared_ptr<PiecewiseAtmOptionletCurve<LinearFlat>> tmp =
272 QuantLib::ext::make_shared<PiecewiseAtmOptionletCurve<LinearFlat>>(
273 config.settleDays(), cftvc, index, discountCurve, flatFirstPeriod,
274 volatilityType(config.volatilityType()), shift, optVolType, optDisplacement, onOpt,
275 LinearFlat(),
278 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps));
279 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, Linear>>(
280 asof, transform(asof, tmp->curve()->dates(), tmp->curve()->volatilities(), tmp->settlementDays(),
281 tmp->calendar(), tmp->businessDayConvention(), index, tmp->dayCounter(),
282 tmp->volatilityType(), tmp->displacement()));
283 } else {
284 QuantLib::ext::shared_ptr<PiecewiseAtmOptionletCurve<Linear>> tmp =
285 QuantLib::ext::make_shared<PiecewiseAtmOptionletCurve<Linear>>(
286 config.settleDays(), cftvc, index, discountCurve, flatFirstPeriod,
287 volatilityType(config.volatilityType()), shift, optVolType, optDisplacement, onOpt, Linear(),
290 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps));
291 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, Linear>>(
292 asof, transform(asof, tmp->curve()->dates(), tmp->curve()->volatilities(), tmp->settlementDays(),
293 tmp->calendar(), tmp->businessDayConvention(), index, tmp->dayCounter(),
294 tmp->volatilityType(), tmp->displacement()));
295 }
296 } else {
297 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected interpolation method "
298 << static_cast<int>(config.interpolationMethod()));
299 }
300 }
301}
QuantLib::ext::shared_ptr< QuantLib::StrippedOptionlet > transform(const QuantExt::OptionletStripper &os) const
Transform QuantExt::OptionletStripper to QuantLib::StrippedOptionlet.
QuantLib::ext::shared_ptr< QuantExt::CapFloorTermVolCurve > atmCurve(const QuantLib::Date &asof, CapFloorVolatilityCurveConfig &config, const Loader &loader) const
Build an ATM cap floor term volatility curve.
VolatilityType volatilityType(CapFloorVolatilityCurveConfig::VolatilityType type)
Imply QuantLib::VolatilityType from CapFloorVolatilityCurveConfig::VolatilityType.
bool interpOnOpt(CapFloorVolatilityCurveConfig &config)
+ Here is the call graph for this function:

◆ termOptSurface()

void termOptSurface ( const QuantLib::Date &  asof,
CapFloorVolatilityCurveConfig config,
const Loader loader,
QuantLib::ext::shared_ptr< QuantLib::IborIndex >  iborIndex,
QuantLib::Handle< QuantLib::YieldTermStructure >  discountCurve,
QuantLib::Real  shift 
)
private

Build optionlet surface from term vol.

Definition at line 303 of file capfloorvolcurve.cpp.

305 {
306
307 // Get the cap floor term vol surface
308 QuantLib::ext::shared_ptr<QuantExt::CapFloorTermVolSurface> cftvs = capSurface(asof, config, loader);
309
310 // Get the ATM cap floor term vol curve if we are including an ATM curve
311 bool includeAtm = config.includeAtm();
312 Handle<QuantExt::CapFloorTermVolCurve> cftvc;
313 if (includeAtm) {
314 cftvc = Handle<QuantExt::CapFloorTermVolCurve>(atmCurve(asof, config, loader));
315 }
316
317 // Hardcode some values. Can add them to the CapFloorVolatilityCurveConfig later if needed.
318 bool flatFirstPeriod = true;
319 VolatilityType optVolType = Normal;
320 Real optDisplacement = 0.0;
321
322 // Get configuration values for bootstrap
323 Real accuracy = config.bootstrapConfig().accuracy();
324 Real globalAccuracy = config.bootstrapConfig().globalAccuracy();
325 bool dontThrow = config.bootstrapConfig().dontThrow();
326 Size maxAttempts = config.bootstrapConfig().maxAttempts();
327 Real maxFactor = config.bootstrapConfig().maxFactor();
328 Real minFactor = config.bootstrapConfig().minFactor();
329 Size dontThrowSteps = config.bootstrapConfig().dontThrowSteps();
330
331 // Get configuration values for parametric smile
332 std::vector<std::vector<std::pair<Real, bool>>> initialModelParameters;
333 Size maxCalibrationAttempts = 10;
334 Real exitEarlyErrorThreshold = 0.005;
335 Real maxAcceptableError = 0.05;
336 if (config.parametricSmileConfiguration()) {
337 auto alpha = config.parametricSmileConfiguration()->parameter("alpha");
338 auto beta = config.parametricSmileConfiguration()->parameter("beta");
339 auto nu = config.parametricSmileConfiguration()->parameter("nu");
340 auto rho = config.parametricSmileConfiguration()->parameter("rho");
341 QL_REQUIRE(alpha.initialValue.size() == beta.initialValue.size() &&
342 alpha.initialValue.size() == nu.initialValue.size() &&
343 alpha.initialValue.size() == rho.initialValue.size(),
344 "CapFloorVolCurve: parametric smile config: alpha size ("
345 << alpha.initialValue.size() << ") beta size (" << beta.initialValue.size() << ") nu size ("
346 << nu.initialValue.size() << ") rho size (" << rho.initialValue.size() << ") must match");
347 for (Size i = 0; i < alpha.initialValue.size(); ++i) {
348 initialModelParameters.push_back(std::vector<std::pair<Real, bool>>());
349 initialModelParameters.back().push_back(std::make_pair(alpha.initialValue[i], alpha.isFixed));
350 initialModelParameters.back().push_back(std::make_pair(beta.initialValue[i], beta.isFixed));
351 initialModelParameters.back().push_back(std::make_pair(nu.initialValue[i], nu.isFixed));
352 initialModelParameters.back().push_back(std::make_pair(rho.initialValue[i], rho.isFixed));
353 }
354 maxCalibrationAttempts = config.parametricSmileConfiguration()->calibration().maxCalibrationAttempts;
355 exitEarlyErrorThreshold = config.parametricSmileConfiguration()->calibration().exitEarlyErrorThreshold;
356 maxAcceptableError = config.parametricSmileConfiguration()->calibration().maxAcceptableError;
357 }
358
359 // On optionlets is the newly added interpolation approach whereas on term volatilities is legacy
360 QuantLib::ext::shared_ptr<QuantExt::OptionletStripper> optionletStripper;
361 VolatilityType volType = volatilityType(config.volatilityType());
362 bool onOpt = interpOnOpt(config);
363 SabrParametricVolatility::ModelVariant sabrModelVariant;
364 if (onOpt) {
365 // This is not pretty but can't think of a better way (with template functions and or classes)
366 if (config.timeInterpolation() == "Linear") {
367 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<Linear>>(
368 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
369 Linear(),
372 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
373 config.rateComputationPeriod(), config.onCapSettlementDays());
374 if (config.strikeInterpolation() == "Linear") {
375 if (includeAtm) {
376 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Linear, Linear>>(
377 optionletStripper, cftvc, discountCurve, volType, shift);
378 }
379 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, Linear>>(
380 asof, transform(*optionletStripper));
381 } else if (config.strikeInterpolation() == "LinearFlat") {
382 if (includeAtm) {
383 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Linear, LinearFlat>>(
384 optionletStripper, cftvc, discountCurve, volType, shift);
385 }
386 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, LinearFlat>>(
387 asof, transform(*optionletStripper));
388 } else if (config.strikeInterpolation() == "Cubic") {
389 if (includeAtm) {
390 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Linear, Cubic>>(
391 optionletStripper, cftvc, discountCurve, volType, shift);
392 }
393 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, Cubic>>(
394 asof, transform(*optionletStripper));
395 } else if (config.strikeInterpolation() == "CubicFlat") {
396 if (includeAtm) {
397 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Linear, CubicFlat>>(
398 optionletStripper, cftvc, discountCurve, volType, shift);
399 }
400 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, CubicFlat>>(
401 asof, transform(*optionletStripper));
402 } else if (tryParse(
403 config.strikeInterpolation(), sabrModelVariant,
404 std::function<QuantExt::SabrParametricVolatility::ModelVariant(const std::string&)>(
405 [](const std::string& s) { return parseSabrParametricVolatilityModelVariant(s); }))) {
406 if (includeAtm) {
407 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Linear, Linear>>(
408 optionletStripper, cftvc, discountCurve, volType, shift);
409 }
410 capletVol_ = QuantLib::ext::make_shared<QuantExt::SabrStrippedOptionletAdapter<Linear>>(
411 asof, transform(*optionletStripper), sabrModelVariant, Linear(), boost::none,
412 initialModelParameters, maxCalibrationAttempts, exitEarlyErrorThreshold, maxAcceptableError);
413 } else {
414 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected strike interpolation "
415 << config.strikeInterpolation());
416 }
417 } else if (config.timeInterpolation() == "LinearFlat") {
418 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<LinearFlat>>(
419 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
420 LinearFlat(),
423 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
424 config.rateComputationPeriod(), config.onCapSettlementDays());
425 if (config.strikeInterpolation() == "Linear") {
426 if (includeAtm) {
427 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<LinearFlat, Linear>>(
428 optionletStripper, cftvc, discountCurve, volType, shift);
429 }
430 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, Linear>>(
431 asof, transform(*optionletStripper));
432 } else if (config.strikeInterpolation() == "LinearFlat") {
433 if (includeAtm) {
434 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<LinearFlat, LinearFlat>>(
435 optionletStripper, cftvc, discountCurve, volType, shift);
436 }
437 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, LinearFlat>>(
438 asof, transform(*optionletStripper));
439 } else if (config.strikeInterpolation() == "Cubic") {
440 if (includeAtm) {
441 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<LinearFlat, Cubic>>(
442 optionletStripper, cftvc, discountCurve, volType, shift);
443 }
444 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, Cubic>>(
445 asof, transform(*optionletStripper));
446 } else if (config.strikeInterpolation() == "CubicFlat") {
447 if (includeAtm) {
448 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<LinearFlat, CubicFlat>>(
449 optionletStripper, cftvc, discountCurve, volType, shift);
450 }
451 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, CubicFlat>>(
452 asof, transform(*optionletStripper));
453 } else if (tryParse(
454 config.strikeInterpolation(), sabrModelVariant,
455 std::function<QuantExt::SabrParametricVolatility::ModelVariant(const std::string&)>(
456 [](const std::string& s) { return parseSabrParametricVolatilityModelVariant(s); }))) {
457 if (includeAtm) {
458 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<LinearFlat, Linear>>(
459 optionletStripper, cftvc, discountCurve, volType, shift);
460 }
461 capletVol_ = QuantLib::ext::make_shared<QuantExt::SabrStrippedOptionletAdapter<LinearFlat>>(
462 asof, transform(*optionletStripper), sabrModelVariant, LinearFlat(), boost::none,
463 initialModelParameters, maxCalibrationAttempts, exitEarlyErrorThreshold, maxAcceptableError);
464 } else {
465 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected strike interpolation "
466 << config.strikeInterpolation());
467 }
468 } else if (config.timeInterpolation() == "BackwardFlat") {
469 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<BackwardFlat>>(
470 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
471 BackwardFlat(),
474 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
475 config.rateComputationPeriod(), config.onCapSettlementDays());
476 if (config.strikeInterpolation() == "Linear") {
477 if (includeAtm) {
478 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<BackwardFlat, Linear>>(
479 optionletStripper, cftvc, discountCurve, volType, shift);
480 }
481 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<BackwardFlat, Linear>>(
482 asof, transform(*optionletStripper));
483 } else if (config.strikeInterpolation() == "LinearFlat") {
484 if (includeAtm) {
485 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<BackwardFlat, LinearFlat>>(
486 optionletStripper, cftvc, discountCurve, volType, shift);
487 }
488 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<BackwardFlat, LinearFlat>>(
489 asof, transform(*optionletStripper));
490 } else if (config.strikeInterpolation() == "Cubic") {
491 if (includeAtm) {
492 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<BackwardFlat, Cubic>>(
493 optionletStripper, cftvc, discountCurve, volType, shift);
494 }
495 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<BackwardFlat, Cubic>>(
496 asof, transform(*optionletStripper));
497 } else if (config.strikeInterpolation() == "CubicFlat") {
498 if (includeAtm) {
499 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<BackwardFlat, CubicFlat>>(
500 optionletStripper, cftvc, discountCurve, volType, shift);
501 }
502 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<BackwardFlat, CubicFlat>>(
503 asof, transform(*optionletStripper));
504 } else if (tryParse(
505 config.strikeInterpolation(), sabrModelVariant,
506 std::function<QuantExt::SabrParametricVolatility::ModelVariant(const std::string&)>(
507 [](const std::string& s) { return parseSabrParametricVolatilityModelVariant(s); }))) {
508 if (includeAtm) {
509 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<BackwardFlat, Linear>>(
510 optionletStripper, cftvc, discountCurve, volType, shift);
511 }
512 capletVol_ = QuantLib::ext::make_shared<QuantExt::SabrStrippedOptionletAdapter<Linear>>(
513 asof, transform(*optionletStripper), sabrModelVariant, Linear(), boost::none,
514 initialModelParameters, maxCalibrationAttempts, exitEarlyErrorThreshold, maxAcceptableError);
515 } else {
516 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected strike interpolation "
517 << config.strikeInterpolation());
518 }
519 } else if (config.timeInterpolation() == "Cubic") {
520 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<Cubic>>(
521 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
522 Cubic(),
525 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
526 config.rateComputationPeriod(), config.onCapSettlementDays());
527 if (config.strikeInterpolation() == "Linear") {
528 if (includeAtm) {
529 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Cubic, Linear>>(
530 optionletStripper, cftvc, discountCurve, volType, shift);
531 }
532 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, Linear>>(
533 asof, transform(*optionletStripper));
534 } else if (config.strikeInterpolation() == "LinearFlat") {
535 if (includeAtm) {
536 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Cubic, LinearFlat>>(
537 optionletStripper, cftvc, discountCurve, volType, shift);
538 }
539 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, LinearFlat>>(
540 asof, transform(*optionletStripper));
541 } else if (config.strikeInterpolation() == "Cubic") {
542 if (includeAtm) {
543 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Cubic, Cubic>>(
544 optionletStripper, cftvc, discountCurve, volType, shift);
545 }
546 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, Cubic>>(
547 asof, transform(*optionletStripper));
548 } else if (config.strikeInterpolation() == "CubicFlat") {
549 if (includeAtm) {
550 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Cubic, CubicFlat>>(
551 optionletStripper, cftvc, discountCurve, volType, shift);
552 }
553 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, CubicFlat>>(
554 asof, transform(*optionletStripper));
555 } else if (tryParse(
556 config.strikeInterpolation(), sabrModelVariant,
557 std::function<QuantExt::SabrParametricVolatility::ModelVariant(const std::string&)>(
558 [](const std::string& s) { return parseSabrParametricVolatilityModelVariant(s); }))) {
559 if (includeAtm) {
560 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Cubic, Linear>>(
561 optionletStripper, cftvc, discountCurve, volType, shift);
562 }
563 capletVol_ = QuantLib::ext::make_shared<QuantExt::SabrStrippedOptionletAdapter<Cubic>>(
564 asof, transform(*optionletStripper), sabrModelVariant, Cubic(), boost::none, initialModelParameters,
565 maxCalibrationAttempts, exitEarlyErrorThreshold, maxAcceptableError);
566 }
567 {
568 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected strike interpolation "
569 << config.strikeInterpolation());
570 }
571 } else if (config.timeInterpolation() == "CubicFlat") {
572 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<CubicFlat>>(
573 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
574 CubicFlat(),
577 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
578 config.rateComputationPeriod(), config.onCapSettlementDays());
579 if (config.strikeInterpolation() == "Linear") {
580 if (includeAtm) {
581 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<CubicFlat, Linear>>(
582 optionletStripper, cftvc, discountCurve, volType, shift);
583 }
584 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, Linear>>(
585 asof, transform(*optionletStripper));
586 } else if (config.strikeInterpolation() == "LinearFlat") {
587 if (includeAtm) {
588 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<CubicFlat, LinearFlat>>(
589 optionletStripper, cftvc, discountCurve, volType, shift);
590 }
591 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, LinearFlat>>(
592 asof, transform(*optionletStripper));
593 } else if (config.strikeInterpolation() == "Cubic") {
594 if (includeAtm) {
595 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<CubicFlat, Cubic>>(
596 optionletStripper, cftvc, discountCurve, volType, shift);
597 }
598 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, Cubic>>(
599 asof, transform(*optionletStripper));
600 } else if (config.strikeInterpolation() == "CubicFlat") {
601 if (includeAtm) {
602 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<CubicFlat, CubicFlat>>(
603 optionletStripper, cftvc, discountCurve, volType, shift);
604 }
605 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, CubicFlat>>(
606 asof, transform(*optionletStripper));
607 } else if (tryParse(
608 config.strikeInterpolation(), sabrModelVariant,
609 std::function<QuantExt::SabrParametricVolatility::ModelVariant(const std::string&)>(
610 [](const std::string& s) { return parseSabrParametricVolatilityModelVariant(s); }))) {
611 if (includeAtm) {
612 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<CubicFlat, Linear>>(
613 optionletStripper, cftvc, discountCurve, volType, shift);
614 }
615 capletVol_ = QuantLib::ext::make_shared<QuantExt::SabrStrippedOptionletAdapter<CubicFlat>>(
616 asof, transform(*optionletStripper), sabrModelVariant, CubicFlat(), boost::none,
617 initialModelParameters, maxCalibrationAttempts, exitEarlyErrorThreshold, maxAcceptableError);
618 } else {
619 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected strike interpolation "
620 << config.strikeInterpolation());
621 }
622 } else {
623 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected time interpolation "
624 << config.timeInterpolation());
625 }
626 } else {
627 // Legacy method where we interpolate on the term volatilities.
628 // We don't need time interpolation in this instance - we just use the term volatility interpolation.
629 if (config.interpolationMethod() == CftvsInterp::BicubicSpline) {
630 if (config.flatExtrapolation()) {
631 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<CubicFlat>>(
632 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
633 CubicFlat(),
636 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
637 config.rateComputationPeriod(), config.onCapSettlementDays());
638 if (includeAtm) {
639 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<CubicFlat, CubicFlat>>(
640 optionletStripper, cftvc, discountCurve, volType, shift);
641 }
642 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, CubicFlat>>(
643 asof, transform(*optionletStripper));
644 } else {
645 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<Cubic>>(
646 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
647 Cubic(),
650 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
651 config.rateComputationPeriod(), config.onCapSettlementDays());
652 if (includeAtm) {
653 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Cubic, Cubic>>(
654 optionletStripper, cftvc, discountCurve, volType, shift);
655 }
656 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, Cubic>>(
657 asof, transform(*optionletStripper));
658 }
659 } else if (config.interpolationMethod() == CftvsInterp::Bilinear) {
660 if (config.flatExtrapolation()) {
661 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<LinearFlat>>(
662 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
663 LinearFlat(),
666 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
667 config.rateComputationPeriod(), config.onCapSettlementDays());
668 if (includeAtm) {
669 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<LinearFlat, LinearFlat>>(
670 optionletStripper, cftvc, discountCurve, volType, shift);
671 }
672 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, LinearFlat>>(
673 asof, transform(*optionletStripper));
674 } else {
675 optionletStripper = QuantLib::ext::make_shared<PiecewiseOptionletStripper<Linear>>(
676 cftvs, index, discountCurve, flatFirstPeriod, volType, shift, optVolType, optDisplacement, onOpt,
677 Linear(),
680 accuracy, globalAccuracy, dontThrow, maxAttempts, maxFactor, minFactor, dontThrowSteps),
681 config.rateComputationPeriod(), config.onCapSettlementDays());
682 if (includeAtm) {
683 optionletStripper = QuantLib::ext::make_shared<OptionletStripperWithAtm<Linear, Linear>>(
684 optionletStripper, cftvc, discountCurve, volType, shift);
685 }
686 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, Linear>>(
687 asof, transform(*optionletStripper));
688 }
689 } else {
690 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected interpolation method "
691 << static_cast<int>(config.interpolationMethod()));
692 }
693 }
694}
QuantLib::ext::shared_ptr< QuantExt::CapFloorTermVolSurface > capSurface(const QuantLib::Date &asof, CapFloorVolatilityCurveConfig &config, const Loader &loader) const
Build a cap floor term volatility surface.
bool tryParse(const std::string &str, T &obj, std::function< T(const std::string &)> parser)
Definition: parsers.hpp:427
+ Here is the call graph for this function:

◆ optAtmOptCurve()

void optAtmOptCurve ( const QuantLib::Date &  asof,
CapFloorVolatilityCurveConfig config,
const Loader loader,
QuantLib::ext::shared_ptr< QuantLib::IborIndex >  iborIndex,
QuantLib::Handle< QuantLib::YieldTermStructure >  discountCurve,
QuantLib::Real  shift 
)
private

Build ATM optionlet curve from optionlet vol.

Definition at line 696 of file capfloorvolcurve.cpp.

698 {
699 QL_REQUIRE(config.optionalQuotes() == false, "Optional quotes for optionlet volatilities are not supported.");
700 // Load optionlet atm vol curve
701 bool tenorRelevant = false;
702 bool wildcardTenor = false;
703
704 Period tenor = parsePeriod(config.indexTenor()); // 1D, 1M, 3M, 6M, 12M
705 string currency = config.currency();
706 vector<Period> configTenors;
707 std::map<Period, Real> capfloorVols;
708 if (config.atmTenors()[0] == "*") {
709 wildcardTenor = true;
710 } else {
711 configTenors = parseVectorOfValues<Period>(config.atmTenors(), &parsePeriod);
712 }
713 std::ostringstream ss;
714 ss << MarketDatum::InstrumentType::CAPFLOOR << "/" << config.quoteType() << "/" << currency << "/";
715 if (config.quoteIncludesIndexName())
716 ss << config.index() << "/";
717 ss << "*";
718 Wildcard w(ss.str());
719 for (const auto& md : loader.get(w, asof)) {
720 QL_REQUIRE(md->asofDate() == asof, "MarketDatum asofDate '" << md->asofDate() << "' <> asof '" << asof << "'");
721 QuantLib::ext::shared_ptr<CapFloorQuote> cfq = QuantLib::ext::dynamic_pointer_cast<CapFloorQuote>(md);
722 QL_REQUIRE(cfq, "Internal error: could not downcast MarketDatum '" << md->name() << "' to CapFloorQuote");
723 QL_REQUIRE(cfq->ccy() == currency,
724 "CapFloorQuote ccy '" << cfq->ccy() << "' <> config ccy '" << currency << "'");
725 if (cfq->underlying() == tenor && cfq->atm()) {
726 auto findTenor = std::find(configTenors.begin(), configTenors.end(), cfq->term());
727 if (wildcardTenor) {
728 tenorRelevant = true;
729 } else {
730 tenorRelevant = findTenor != configTenors.end();
731 }
732 if (tenorRelevant) {
733 // Check duplicate quotes
734 auto k = capfloorVols.find(cfq->term());
735 if (k != capfloorVols.end()) {
736 if (config.quoteIncludesIndexName())
737 QL_FAIL("Duplicate optionlet atm vol quote in config "
738 << config.curveID() << ", with underlying tenor " << tenor << " ,currency "
739 << currency << " and index " << config.index() << ", for tenor " << cfq->term());
740 else
741 QL_FAIL("Duplicate optionlet atm vol quote in config "
742 << config.curveID() << ", with underlying tenor " << tenor << " and currency "
743 << currency << ", for tenor " << cfq->term());
744 }
745 // Store the vols into a map to sort the wildcard tenors
746 capfloorVols[cfq->term()]= cfq->quote()->value();
747 }
748 }
749 }
750 vector<Real> vols_tenor;
751 auto tenor_itr = configTenors.begin();
752 for (auto const& vols_outer : capfloorVols) {
753 // Check if all tenor is available
754 if (wildcardTenor) {
755 configTenors.push_back(vols_outer.first);
756 } else {
757 QL_REQUIRE(*tenor_itr == vols_outer.first, "Quote with tenor " << *tenor_itr
758 << " not loaded for optionlet vol config "
759 << config.curveID());
760 tenor_itr++;
761 }
762 vols_tenor.push_back(vols_outer.second);
763 }
764 // Find the fixing date of the term quotes
765 vector<Date> fixingDates = populateFixingDates(asof, config, index, configTenors);
766 DLOG("Found " << capfloorVols.size() << " quotes for optionlet vol surface " << config.curveID());
767 // This is not pretty but can't think of a better way (with template functions and or classes)
768 // Note: second template argument in StrippedOptionletAdapter doesn't matter so just use Linear here.
769 if (config.timeInterpolation() == "Linear") {
770 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, Linear>>(
771 asof, transform(asof, fixingDates, vols_tenor, config.settleDays(),
772 config.calendar(), config.businessDayConvention(), index, config.dayCounter(),
773 volatilityType(config.volatilityType()), shift));
774 } else if (config.timeInterpolation() == "LinearFlat") {
775 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, Linear>>(
776 asof, transform(asof, fixingDates, vols_tenor, config.settleDays(), config.calendar(),
777 config.businessDayConvention(), index, config.dayCounter(),
778 volatilityType(config.volatilityType()), shift));
779 } else if (config.timeInterpolation() == "BackwardFlat") {
780 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<BackwardFlat, Linear>>(
781 asof, transform(asof, fixingDates, vols_tenor, config.settleDays(), config.calendar(),
782 config.businessDayConvention(), index, config.dayCounter(),
783 volatilityType(config.volatilityType()), shift));
784 } else if (config.timeInterpolation() == "Cubic") {
785 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, Linear>>(
786 asof, transform(asof, fixingDates, vols_tenor, config.settleDays(), config.calendar(),
787 config.businessDayConvention(), index, config.dayCounter(),
788 volatilityType(config.volatilityType()), shift));
789 } else if (config.timeInterpolation() == "CubicFlat") {
790 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, Linear>>(
791 asof, transform(asof, fixingDates, vols_tenor, config.settleDays(), config.calendar(),
792 config.businessDayConvention(), index, config.dayCounter(),
793 volatilityType(config.volatilityType()), shift));
794 } else {
795 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected time interpolation "
796 << config.timeInterpolation());
797 }
798}
vector< Date > populateFixingDates(const QuantLib::Date &asof, CapFloorVolatilityCurveConfig &config, QuantLib::ext::shared_ptr< QuantLib::IborIndex > iborIndex, const vector< Period > &configTenors)
Generate fixing days from end date for optionlet vol.
Period parsePeriod(const string &s)
Convert text to QuantLib::Period.
Definition: parsers.cpp:171
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
+ Here is the call graph for this function:

◆ optOptSurface()

void optOptSurface ( const QuantLib::Date &  asof,
CapFloorVolatilityCurveConfig config,
const Loader loader,
QuantLib::ext::shared_ptr< QuantLib::IborIndex >  iborIndex,
QuantLib::Handle< QuantLib::YieldTermStructure >  discountCurve,
QuantLib::Real  shift 
)
private

Build optionlet surface from optionlet vol.

Definition at line 800 of file capfloorvolcurve.cpp.

803 {
804 QL_REQUIRE(config.optionalQuotes() == false, "Optional quotes for optionlet volatilities are not supported.");
805
806 // Get configuration values for parametric smile
807 std::vector<std::vector<std::pair<Real,bool>>> initialModelParameters;
808 Size maxCalibrationAttempts = 10;
809 Real exitEarlyErrorThreshold = 0.005;
810 Real maxAcceptableError = 0.05;
811 if (config.parametricSmileConfiguration()) {
812 auto alpha = config.parametricSmileConfiguration()->parameter("alpha");
813 auto beta = config.parametricSmileConfiguration()->parameter("beta");
814 auto nu = config.parametricSmileConfiguration()->parameter("nu");
815 auto rho = config.parametricSmileConfiguration()->parameter("rho");
816 QL_REQUIRE(alpha.initialValue.size() == beta.initialValue.size() &&
817 alpha.initialValue.size() == nu.initialValue.size() &&
818 alpha.initialValue.size() == rho.initialValue.size(),
819 "CapFloorVolCurve: parametric smile config: alpha size ("
820 << alpha.initialValue.size() << ") beta size (" << beta.initialValue.size() << ") nu size ("
821 << nu.initialValue.size() << ") rho size (" << rho.initialValue.size() << ") must match");
822 for (Size i = 0; i < alpha.initialValue.size(); ++i) {
823 initialModelParameters.push_back(std::vector<std::pair<Real, bool>>());
824 initialModelParameters.back().push_back(std::make_pair(alpha.initialValue[i], alpha.isFixed));
825 initialModelParameters.back().push_back(std::make_pair(beta.initialValue[i], beta.isFixed));
826 initialModelParameters.back().push_back(std::make_pair(nu.initialValue[i], nu.isFixed));
827 initialModelParameters.back().push_back(std::make_pair(rho.initialValue[i], rho.isFixed));
828 }
829 maxCalibrationAttempts = config.parametricSmileConfiguration()->calibration().maxCalibrationAttempts;
830 exitEarlyErrorThreshold = config.parametricSmileConfiguration()->calibration().exitEarlyErrorThreshold;
831 maxAcceptableError = config.parametricSmileConfiguration()->calibration().maxAcceptableError;
832 }
833
834 // Load optionlet vol surface
835 Size quoteCounter = 0;
836 bool quoteRelevant = false;
837 bool tenorRelevant = false;
838 bool strikeRelevant = false;
839 bool wildcardTenor = false;
840 bool atmTenorRelevant = false;
841 bool atmWildcardTenor = false;
842
843 Period tenor = parsePeriod(config.indexTenor()); // 1D, 1M, 3M, 6M, 12M
844 bool includeAtm = config.includeAtm();
845 string currency = config.currency();
846 vector<Period> configTenors;
847 std::map<Period, std::map<Real, Real>> capfloorVols;
848 vector<Period> atmConfigTenors;
849 std::map<Period, Real> atmCapFloorVols;
850 VolatilityType volType = volatilityType(config.volatilityType());
851 bool isOis = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(iborIndex) != nullptr;
852
853 if (config.tenors()[0] == "*") {
854 wildcardTenor = true;
855 } else {
856 configTenors = parseVectorOfValues<Period>(config.tenors(), &parsePeriod);
857 }
858 if (includeAtm) {
859 if (config.atmTenors()[0] == "*") {
860 atmWildcardTenor = true;
861 } else {
862 atmConfigTenors = parseVectorOfValues<Period>(config.atmTenors(), &parsePeriod);
863 }
864 }
865 std::ostringstream ss;
866 ss << MarketDatum::InstrumentType::CAPFLOOR << "/" << config.quoteType() << "/" << currency << "/";
867 if (config.quoteIncludesIndexName())
868 ss << config.index() << "/";
869 ss << "*";
870 Wildcard w(ss.str());
871 for (const auto& md : loader.get(w, asof)) {
872 QL_REQUIRE(md->asofDate() == asof, "MarketDatum asofDate '" << md->asofDate() << "' <> asof '" << asof << "'");
873 QuantLib::ext::shared_ptr<CapFloorQuote> cfq = QuantLib::ext::dynamic_pointer_cast<CapFloorQuote>(md);
874 QL_REQUIRE(cfq, "Internal error: could not downcast MarketDatum '" << md->name() << "' to CapFloorQuote");
875 QL_REQUIRE(cfq->ccy() == currency,
876 "CapFloorQuote ccy '" << cfq->ccy() << "' <> config ccy '" << currency << "'");
877 if (cfq->underlying() == tenor) {
878 // Surface quotes
879 if (!cfq->atm()) {
880 auto findTenor = std::find(configTenors.begin(), configTenors.end(), cfq->term());
881 if (wildcardTenor) {
882 tenorRelevant = true;
883 } else {
884 tenorRelevant = findTenor != configTenors.end();
885 }
886
887 Real strike = cfq->strike();
888 auto i = std::find_if(config.strikes().begin(), config.strikes().end(),
889 [&strike](const std::string& x) { return close_enough(parseReal(x), strike); });
890 strikeRelevant = i != config.strikes().end();
891
892 quoteRelevant = strikeRelevant && tenorRelevant;
893
894 if (quoteRelevant) {
895 quoteCounter++;
896 // Check duplicate quotes
897 auto k = capfloorVols.find(cfq->term());
898 if (k != capfloorVols.end()) {
899 if (k->second.find(cfq->strike()) != k->second.end()) {
900 if (config.quoteIncludesIndexName())
901 QL_FAIL("Duplicate optionlet vol quote in config "
902 << config.curveID() << ", with underlying tenor " << tenor << " ,currency "
903 << currency << " and index " << config.index() << ", for tenor " << cfq->term()
904 << " and strike " << cfq->strike());
905 else
906 QL_FAIL("Duplicate optionlet vol quote in config "
907 << config.curveID() << ", with underlying tenor " << tenor << " and currency "
908 << currency << ", for tenor " << cfq->term() << " and strike "
909 << cfq->strike());
910 }
911 }
912 // Store the vols into a map to sort the wildcard tenors
913 capfloorVols[cfq->term()][cfq->strike()] = cfq->quote()->value();
914 }
915 } else if (includeAtm) {
916 // atm quotes
917 auto findTenor = std::find(atmConfigTenors.begin(), atmConfigTenors.end(), cfq->term());
918 if (atmWildcardTenor) {
919 atmTenorRelevant = true;
920 } else {
921 atmTenorRelevant = findTenor != atmConfigTenors.end();
922 }
923 if (atmTenorRelevant) {
924 quoteCounter++;
925 // Check duplicate quotes
926 auto k = atmCapFloorVols.find(cfq->term());
927 if (k != atmCapFloorVols.end()) {
928 if (config.quoteIncludesIndexName())
929 QL_FAIL("Duplicate optionlet atm vol quote in config "
930 << config.curveID() << ", with underlying tenor " << tenor << " ,currency "
931 << currency << " and index " << config.index() << ", for tenor " << cfq->term());
932 else
933 QL_FAIL("Duplicate optionlet atm vol quote in config "
934 << config.curveID() << ", with underlying tenor " << tenor << " and currency "
935 << currency << ", for tenor " << cfq->term());
936 }
937 // Store the vols into a map to sort the wildcard tenors
938 atmCapFloorVols[cfq->term()] = cfq->quote()->value();
939 }
940 }
941 }
942 }
943 vector<Rate> strikes = parseVectorOfValues<Real>(config.strikes(), &parseReal);
944 auto tenor_itr = configTenors.begin();
945 for (auto const& vols_outer: capfloorVols) {
946 // Check if all tenors are available
947 if (!wildcardTenor) {
948 QL_REQUIRE(*tenor_itr == vols_outer.first, "Quote with tenor " << *tenor_itr
949 << " not loaded for optionlet vol config "
950 << config.curveID());
951 tenor_itr++;
952 }
953 // Check if all strikes are available
954 for (Size j = 0; j < strikes.size(); j++) {
955 auto it = vols_outer.second.find(strikes[j]);
956 QL_REQUIRE(it != vols_outer.second.end(),
957 "Quote with tenor " << vols_outer.first << " and strike " << strikes[j]
958 << " not loaded for optionlet vol config "
959 << config.curveID());
960 }
961 }
962 std::map<Date, Date> tenorMap;
963 if (includeAtm) {
964 // Check if all tenor for atm quotes exists
965 if (!atmWildcardTenor) {
966 auto tenor_itr = atmConfigTenors.begin();
967 for (auto const& vols_outer : atmCapFloorVols) {
968 QL_REQUIRE(*tenor_itr == vols_outer.first, "Quote with tenor "
969 << *tenor_itr << " not loaded for optionlet vol config "
970 << config.curveID());
971 tenor_itr++;
972 }
973 }
974 // Add tenor to the mapping
975 for (auto const& vols_outer : atmCapFloorVols) {
976 capfloorVols[vols_outer.first];
977 }
978 }
979 // Find the fixing date of the term quotes
980 vector<Period> tenors;
981 for (auto const& vols_outer : capfloorVols) {
982 tenors.push_back(vols_outer.first);
983 }
984 // Find the fixing date of the term quotes
985 vector<Date> fixingDates = populateFixingDates(asof, config, iborIndex, tenors);
986
987 // populate strikes for atm quotes
988 if (includeAtm){
989 Rate atmStrike;
990 for (auto const& vols_outer : atmCapFloorVols) {
991 auto it = find(tenors.begin(), tenors.end(), vols_outer.first);
992 Size ind = it - tenors.begin();
993 if (isOis) {
994 atmStrike = getOisAtmLevel(QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(iborIndex), fixingDates[ind],
995 config.rateComputationPeriod());
996 } else {
997 atmStrike = iborIndex->forecastFixing(fixingDates[ind]);
998 }
999 if (capfloorVols[vols_outer.first].find(atmStrike) == capfloorVols[vols_outer.first].end()) {
1000 capfloorVols[vols_outer.first][atmStrike] = vols_outer.second;
1001 }
1002 }
1003 }
1004 vector<vector<Rate>> strikes_vec;
1005 vector<Rate> strikes_tenor;
1006 vector<vector<Handle<Quote>>> vols_vec;
1007 vector<Handle<Quote>> vols_tenor;
1008 for (auto const& vols_outer : capfloorVols) {
1009 for (auto const& vols_inner : vols_outer.second) {
1010 vols_tenor.push_back(Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(vols_inner.second)));
1011 strikes_tenor.push_back(vols_inner.first);
1012 }
1013 tenors.push_back(vols_outer.first);
1014 strikes_vec.push_back(strikes_tenor);
1015 strikes_tenor.clear();
1016 vols_vec.push_back(vols_tenor);
1017 vols_tenor.clear();
1018 }
1019 DLOG("Found " << quoteCounter << " quotes for optionlet vol surface " << config.curveID());
1020 QuantLib::ext::shared_ptr<StrippedOptionlet> optionletSurface;
1021
1022 // Return for the cap floor term volatility surface
1023 optionletSurface = QuantLib::ext::make_shared<StrippedOptionlet>(
1024 config.settleDays(), config.calendar(), config.businessDayConvention(), iborIndex, fixingDates, strikes_vec,
1025 vols_vec, config.dayCounter(), volType, shift);
1026
1027 SabrParametricVolatility::ModelVariant sabrModelVariant;
1028
1029 // This is not pretty but can't think of a better way (with template functions and or classes)
1030 if (config.timeInterpolation() == "Linear") {
1031 if (config.strikeInterpolation() == "Linear") {
1032 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, Linear>>(asof, optionletSurface);
1033 } else if (config.strikeInterpolation() == "LinearFlat") {
1034 capletVol_ =
1035 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, LinearFlat>>(asof, optionletSurface);
1036 } else if (config.strikeInterpolation() == "Cubic") {
1037 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, Cubic>>(asof, optionletSurface);
1038 } else if (config.strikeInterpolation() == "CubicFlat") {
1039 capletVol_ =
1040 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Linear, CubicFlat>>(asof, optionletSurface);
1041 } else if (tryParse(config.strikeInterpolation(), sabrModelVariant,
1042 std::function<QuantExt::SabrParametricVolatility::ModelVariant(const std::string&)>(
1043 [](const std::string& s) { return parseSabrParametricVolatilityModelVariant(s); }))) {
1044 capletVol_ = QuantLib::ext::make_shared<QuantExt::SabrStrippedOptionletAdapter<Linear>>(
1045 asof, optionletSurface, sabrModelVariant, Linear(), boost::none, initialModelParameters,
1046 maxCalibrationAttempts, exitEarlyErrorThreshold, maxAcceptableError);
1047 } else {
1048 QL_FAIL("Optionlet vol config " << config.curveID() << " has unexpected strike interpolation "
1049 << config.strikeInterpolation());
1050 }
1051 } else if (config.timeInterpolation() == "LinearFlat") {
1052 if (config.strikeInterpolation() == "Linear") {
1053 capletVol_ =
1054 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, Linear>>(asof, optionletSurface);
1055 } else if (config.strikeInterpolation() == "LinearFlat") {
1056 capletVol_ =
1057 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, LinearFlat>>(asof, optionletSurface);
1058 } else if (config.strikeInterpolation() == "Cubic") {
1059 capletVol_ =
1060 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, Cubic>>(asof, optionletSurface);
1061 } else if (config.strikeInterpolation() == "CubicFlat") {
1062 capletVol_ =
1063 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<LinearFlat, CubicFlat>>(asof, optionletSurface);
1064 } else if (tryParse(config.strikeInterpolation(), sabrModelVariant,
1065 std::function<QuantExt::SabrParametricVolatility::ModelVariant(const std::string&)>(
1066 [](const std::string& s) { return parseSabrParametricVolatilityModelVariant(s); }))) {
1067 capletVol_ = QuantLib::ext::make_shared<QuantExt::SabrStrippedOptionletAdapter<LinearFlat>>(
1068 asof, optionletSurface, sabrModelVariant, LinearFlat(), boost::none, initialModelParameters,
1069 maxCalibrationAttempts, exitEarlyErrorThreshold, maxAcceptableError);
1070 } else {
1071 QL_FAIL("Optionlet vol config " << config.curveID() << " has unexpected strike interpolation "
1072 << config.strikeInterpolation());
1073 }
1074 } else if (config.timeInterpolation() == "BackwardFlat") {
1075 if (config.strikeInterpolation() == "Linear") {
1076 capletVol_ =
1077 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<BackwardFlat, Linear>>(asof, optionletSurface);
1078 } else if (config.strikeInterpolation() == "LinearFlat") {
1079 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<BackwardFlat, LinearFlat>>(
1080 asof, optionletSurface);
1081 } else if (config.strikeInterpolation() == "Cubic") {
1082 capletVol_ =
1083 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<BackwardFlat, Cubic>>(asof, optionletSurface);
1084 } else if (config.strikeInterpolation() == "CubicFlat") {
1085 capletVol_ =
1086 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<BackwardFlat, CubicFlat>>(asof, optionletSurface);
1087 } else if (tryParse(config.strikeInterpolation(), sabrModelVariant,
1088 std::function<QuantExt::SabrParametricVolatility::ModelVariant(const std::string&)>(
1089 [](const std::string& s) { return parseSabrParametricVolatilityModelVariant(s); }))) {
1090 capletVol_ = QuantLib::ext::make_shared<QuantExt::SabrStrippedOptionletAdapter<BackwardFlat>>(
1091 asof, optionletSurface, sabrModelVariant, BackwardFlat(), boost::none, initialModelParameters,
1092 maxCalibrationAttempts, exitEarlyErrorThreshold, maxAcceptableError);
1093 } else {
1094 QL_FAIL("Optionlet vol config " << config.curveID() << " has unexpected strike interpolation "
1095 << config.strikeInterpolation());
1096 }
1097 } else if (config.timeInterpolation() == "Cubic") {
1098 if (config.strikeInterpolation() == "Linear") {
1099 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, Linear>>(asof, optionletSurface);
1100 } else if (config.strikeInterpolation() == "LinearFlat") {
1101 capletVol_ =
1102 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, LinearFlat>>(asof, optionletSurface);
1103 } else if (config.strikeInterpolation() == "Cubic") {
1104 capletVol_ = QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, Cubic>>(asof, optionletSurface);
1105 } else if (config.strikeInterpolation() == "CubicFlat") {
1106 capletVol_ =
1107 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<Cubic, CubicFlat>>(asof, optionletSurface);
1108 } else if (tryParse(config.strikeInterpolation(), sabrModelVariant,
1109 std::function<QuantExt::SabrParametricVolatility::ModelVariant(const std::string&)>(
1110 [](const std::string& s) { return parseSabrParametricVolatilityModelVariant(s); }))) {
1111 capletVol_ = QuantLib::ext::make_shared<QuantExt::SabrStrippedOptionletAdapter<Cubic>>(
1112 asof, optionletSurface, sabrModelVariant, Cubic(), boost::none, initialModelParameters,
1113 maxCalibrationAttempts, exitEarlyErrorThreshold, maxAcceptableError);
1114 } else {
1115 QL_FAIL("Optionlet vol config " << config.curveID() << " has unexpected strike interpolation "
1116 << config.strikeInterpolation());
1117 }
1118 } else if (config.timeInterpolation() == "CubicFlat") {
1119 if (config.strikeInterpolation() == "Linear") {
1120 capletVol_ =
1121 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, Linear>>(asof, optionletSurface);
1122 } else if (config.strikeInterpolation() == "LinearFlat") {
1123 capletVol_ =
1124 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, LinearFlat>>(asof, optionletSurface);
1125 } else if (config.strikeInterpolation() == "Cubic") {
1126 capletVol_ =
1127 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, Cubic>>(asof, optionletSurface);
1128 } else if (config.strikeInterpolation() == "CubicFlat") {
1129 capletVol_ =
1130 QuantLib::ext::make_shared<QuantExt::StrippedOptionletAdapter<CubicFlat, CubicFlat>>(asof, optionletSurface);
1131 } else if (tryParse(config.strikeInterpolation(), sabrModelVariant,
1132 std::function<QuantExt::SabrParametricVolatility::ModelVariant(const std::string&)>(
1133 [](const std::string& s) { return parseSabrParametricVolatilityModelVariant(s); }))) {
1134 capletVol_ = QuantLib::ext::make_shared<QuantExt::SabrStrippedOptionletAdapter<CubicFlat>>(
1135 asof, optionletSurface, sabrModelVariant, CubicFlat(), boost::none, initialModelParameters,
1136 maxCalibrationAttempts, exitEarlyErrorThreshold, maxAcceptableError);
1137 } else {
1138 QL_FAIL("Optionlet vol config " << config.curveID() << " has unexpected strike interpolation "
1139 << config.strikeInterpolation());
1140 }
1141 } else {
1142 QL_FAIL("Optionlet vol config " << config.curveID() << " has unexpected time interpolation "
1143 << config.timeInterpolation());
1144 }
1145}
SabrParametricVolatility::ModelVariant parseSabrParametricVolatilityModelVariant(const std::string &s)
Parse SabrParametricVolatility::ModelVariant.
Definition: parsers.cpp:1458
Real getOisAtmLevel(const boost::shared_ptr< OvernightIndex > &on, const Date &fixingDate, const Period &rateComputationPeriod)
vector< Real > strikes
+ Here is the call graph for this function:

◆ capSurface()

QuantLib::ext::shared_ptr< QuantExt::CapFloorTermVolSurface > capSurface ( const QuantLib::Date &  asof,
CapFloorVolatilityCurveConfig config,
const Loader loader 
) const
private

Build a cap floor term volatility surface.

Definition at line 1148 of file capfloorvolcurve.cpp.

1148 {
1149
1150 // Map to store the quote values that we load with key (period, strike) where strike
1151 // needs a custom comparator to avoid == with double
1152 auto comp = [](const pair<Period, Rate>& a, const pair<Period, Rate>& b) {
1153 return (a.first < b.first) || (!(b.first < a.first) && (a.second < b.second && !close(a.second, b.second)));
1154 };
1155 map<pair<Period, Rate>, Real, decltype(comp)> volQuotes(comp);
1156
1157 bool optionalQuotes = config.optionalQuotes();
1158 Size quoteCounter = 0;
1159 bool quoteRelevant = false;
1160 bool tenorRelevant = false;
1161 bool strikeRelevant = false;
1162
1163 vector<Real> qtStrikes;
1164 vector<Real> qtData;
1165 vector<Period> qtTenors;
1166 Period tenor = parsePeriod(config.indexTenor()); // 1D, 1M, 3M, 6M, 12M
1167 string currency = config.currency();
1168 vector<Period> configTenors = parseVectorOfValues<Period>(config.tenors(), &parsePeriod);
1169
1170 std::ostringstream ss;
1171 ss << MarketDatum::InstrumentType::CAPFLOOR << "/" << config.quoteType() << "/" << currency << "/";
1172 if (config.quoteIncludesIndexName())
1173 ss << config.index() << "/";
1174 ss << "*";
1175 Wildcard w(ss.str());
1176 for (const auto& md : loader.get(w, asof)) {
1177 QL_REQUIRE(md->asofDate() == asof, "MarketDatum asofDate '" << md->asofDate() << "' <> asof '" << asof << "'");
1178 QuantLib::ext::shared_ptr<CapFloorQuote> cfq = QuantLib::ext::dynamic_pointer_cast<CapFloorQuote>(md);
1179 QL_REQUIRE(cfq, "Internal error: could not downcast MarketDatum '" << md->name() << "' to CapFloorQuote");
1180 QL_REQUIRE(cfq->ccy() == currency, "CapFloorQuote ccy '" << cfq->ccy() << "' <> config ccy '" << currency << "'");
1181 if (cfq->underlying() == tenor && !cfq->atm()) {
1182 auto j = std::find(configTenors.begin(), configTenors.end(), cfq->term());
1183 tenorRelevant = j != configTenors.end();
1184
1185 Real strike = cfq->strike();
1186 auto i = std::find_if(config.strikes().begin(), config.strikes().end(),
1187 [&strike](const std::string& x) {
1188 return close_enough(parseReal(x), strike);
1189 });
1190 strikeRelevant = i != config.strikes().end();
1191
1192 quoteRelevant = strikeRelevant && tenorRelevant;
1193
1194 if (quoteRelevant) {
1195 // if we have optional quotes we just make vectors of all quotes and let the sparse surface
1196 // handle them
1197 quoteCounter++;
1198 if (optionalQuotes) {
1199 qtStrikes.push_back(cfq->strike());
1200 qtTenors.push_back(cfq->term());
1201 qtData.push_back(cfq->quote()->value());
1202 }
1203 auto key = make_pair(cfq->term(), cfq->strike());
1204 auto r = volQuotes.insert(make_pair(key, cfq->quote()->value()));
1205 if (config.quoteIncludesIndexName())
1206 QL_REQUIRE(r.second, "Duplicate cap floor quote in config " << config.curveID() << ", with underlying tenor " << tenor <<
1207 " ,currency " << currency << " and index " << config.index() << ", for tenor " << key.first << " and strike " << key.second);
1208 else
1209 QL_REQUIRE(r.second, "Duplicate cap floor quote in config " << config.curveID() << ", with underlying tenor " << tenor <<
1210 " and currency " << currency << ", for tenor " << key.first << " and strike " << key.second);
1211 }
1212 }
1213 }
1214
1215 Size totalQuotes = config.tenors().size() * config.strikes().size();
1216 if (quoteCounter < totalQuotes) {
1217 WLOG("Found only " << quoteCounter << " out of " << totalQuotes << " quotes for CapFloor surface " << config.curveID());
1218 }
1219
1220 vector<Period> tenors = parseVectorOfValues<Period>(config.tenors(), &parsePeriod);
1221 vector<Rate> strikes = parseVectorOfValues<Real>(config.strikes(), &parseReal);
1222 Matrix vols(tenors.size(), strikes.size());
1223 for (Size i = 0; i < tenors.size(); i++) {
1224 for (Size j = 0; j < strikes.size(); j++) {
1225 auto key = make_pair(tenors[i], strikes[j]);
1226 auto it = volQuotes.find(key);
1227 if (!optionalQuotes) {
1228 QL_REQUIRE(it != volQuotes.end(), "Quote with tenor " << key.first << " and strike " << key.second
1229 << " not loaded for cap floor config "
1230 << config.curveID());
1231 // Organise the values in to a square matrix
1232 vols[i][j] = it->second;
1233 } else {
1234 if (it == volQuotes.end()) {
1235 DLOG("Could not find quote with tenor " << key.first << " and strike " << key.second <<
1236 " for cap floor config " << config.curveID());
1237 }
1238 }
1239 }
1240 }
1241
1242 DLOG("Found " << quoteCounter << " quotes for capfloor surface " << config.curveID());
1243 if (optionalQuotes) {
1244 QL_REQUIRE(quoteCounter > 0, "No Quotes provided for CapFloor surface " << config.curveID());
1245 if (config.interpolationMethod() == CapFloorTermVolSurfaceExact::Bilinear) {
1246 return QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceSparse<Linear, Linear>>(
1247 config.settleDays(), config.calendar(), config.businessDayConvention(), config.dayCounter(), qtTenors,
1248 qtStrikes, qtData, true, true);
1249 } else if (config.interpolationMethod() == CapFloorTermVolSurfaceExact::BicubicSpline) {
1250 return QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceSparse<Cubic, Cubic>>(
1251 config.settleDays(), config.calendar(), config.businessDayConvention(), config.dayCounter(), qtTenors,
1252 qtStrikes, qtData, true, true);
1253 } else {
1254 QL_FAIL("Invalid Interpolation method for capfloor surface " << config.curveID() << ", must be either "
1256 }
1257 } else {
1258 // Return for the cap floor term volatility surface
1259 return QuantLib::ext::make_shared<QuantExt::CapFloorTermVolSurfaceExact>(config.settleDays(), config.calendar(),
1260 config.businessDayConvention(), tenors, strikes, vols,
1261 config.dayCounter(), config.interpolationMethod());
1262 }
1263}
#define WLOG(text)
Logging Macro (Level = Warning)
Definition: log.hpp:550
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ atmCurve()

QuantLib::ext::shared_ptr< QuantExt::CapFloorTermVolCurve > atmCurve ( const QuantLib::Date &  asof,
CapFloorVolatilityCurveConfig config,
const Loader loader 
) const
private

Build an ATM cap floor term volatility curve.

Definition at line 1266 of file capfloorvolcurve.cpp.

1266 {
1267
1268 // Map to store the quote values
1269 map<Period, Handle<Quote>> volQuotes;
1270
1271 bool optionalQuotes = config.optionalQuotes();
1272 Period tenor = parsePeriod(config.indexTenor()); // 1D, 1M, 3M, 6M, 12M
1273 string currency = config.currency();
1274 // Load the relevant quotes
1275 std::ostringstream ss;
1276 ss << MarketDatum::InstrumentType::CAPFLOOR << "/" << config.quoteType() << "/" << currency << "/*";
1277 Wildcard w(ss.str());
1278 for (const auto& md : loader.get(w, asof)) {
1279 QL_REQUIRE(md->asofDate() == asof, "MarketDatum asofDate '" << md->asofDate() << "' <> asof '" << asof << "'");
1280 QuantLib::ext::shared_ptr<CapFloorQuote> cfq = QuantLib::ext::dynamic_pointer_cast<CapFloorQuote>(md);
1281 QL_REQUIRE(cfq, "Internal error: could not downcast MarketDatum '" << md->name() << "' to CapFloorQuote");
1282 QL_REQUIRE(cfq->ccy() == currency, "CapFloorQuote ccy '" << cfq->ccy() << "' <> config ccy '" << currency << "'");
1283 if (cfq->underlying() == tenor && cfq->atm()) {
1284 auto j = std::find(config.atmTenors().begin(), config.atmTenors().end(), to_string(cfq->term()));
1285 if (j != config.atmTenors().end()) {
1286 auto r = volQuotes.insert(make_pair(cfq->term(), cfq->quote()));
1287 QL_REQUIRE(r.second, "Duplicate ATM cap floor quote in config " << config.curveID() << " for tenor "
1288 << cfq->term());
1289 }
1290 }
1291 }
1292
1293 // Check that the loaded quotes cover all of the configured ATM tenors
1294 vector<Period> tenors = parseVectorOfValues<Period>(config.atmTenors(), &parsePeriod);
1295 vector<Handle<Quote>> vols;
1296 vector<Period> quoteTenors;
1297 if (!optionalQuotes) {
1298 vols.resize(tenors.size());
1299 quoteTenors = tenors;
1300 }
1301 for (Size i = 0; i < tenors.size(); i++) {
1302 auto it = volQuotes.find(tenors[i]);
1303 if (!optionalQuotes) {
1304 QL_REQUIRE(it != volQuotes.end(),
1305 "ATM cap floor quote in config " << config.curveID() << " for tenor " << tenors[i] << " not found ");
1306 vols[i] = it->second;
1307 } else {
1308 if (it == volQuotes.end()) {
1309 DLOG("Could not find ATM cap floor quote with tenor " << tenors[i] << " for cap floor config " << config.curveID());
1310 } else {
1311 vols.push_back(it->second);
1312 quoteTenors.push_back(it->first);
1313 }
1314 }
1315 }
1316
1317 if (optionalQuotes) {
1318 QL_REQUIRE(vols.size() > 0, "No ATM cap floor quotes found for cap floor config " << config.curveID());
1319 if (vols.size() == 1)
1320 WLOG("Only one ATM cap floor quote found for cap floor config " << config.curveID() << ", using constant volatility");
1321 }
1322
1323 // Return for the cap floor ATM term volatility curve
1324 // The interpolation here is also based on the interpolation method parameter in the configuration
1325 // Flat first period is true by default (see ctor)
1326 if (config.interpolationMethod() == CftvsInterp::BicubicSpline) {
1327 if (config.flatExtrapolation()) {
1328 return QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<CubicFlat>>(
1329 config.settleDays(), config.calendar(), config.businessDayConvention(), quoteTenors, vols,
1330 config.dayCounter());
1331 } else {
1332 return QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<Cubic>>(config.settleDays(), config.calendar(),
1333 config.businessDayConvention(), quoteTenors,
1334 vols, config.dayCounter());
1335 }
1336 } else if (config.interpolationMethod() == CftvsInterp::Bilinear) {
1337 if (config.flatExtrapolation()) {
1338 return QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<LinearFlat>>(
1339 config.settleDays(), config.calendar(), config.businessDayConvention(), quoteTenors, vols,
1340 config.dayCounter());
1341 } else {
1342 return QuantLib::ext::make_shared<InterpolatedCapFloorTermVolCurve<Linear>>(config.settleDays(), config.calendar(),
1343 config.businessDayConvention(), quoteTenors,
1344 vols, config.dayCounter());
1345 }
1346 } else {
1347 QL_FAIL("Cap floor config " << config.curveID() << " has unexpected interpolation method "
1348 << static_cast<int>(config.interpolationMethod()));
1349 }
1350}
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ buildProxyCurve()

void buildProxyCurve ( const CapFloorVolatilityCurveConfig config,
const QuantLib::ext::shared_ptr< IborIndex > &  sourceIndex,
const QuantLib::ext::shared_ptr< IborIndex > &  targetIndex,
const std::map< std::string, std::pair< QuantLib::ext::shared_ptr< ore::data::CapFloorVolCurve >, std::pair< std::string, QuantLib::Period > > > &  requiredCapFloorVolCurves 
)
private

Build proxy curve.

Definition at line 133 of file capfloorvolcurve.cpp.

138 {
139
140 auto sourceVol = requiredCapFloorVolCurves.find(config.proxySourceCurveId());
141 QL_REQUIRE(sourceVol != requiredCapFloorVolCurves.end(),
142 "CapFloorVolCurve::buildProxyCurve(): required source cap vol curve '" << config.proxySourceCurveId()
143 << "' not found.");
144
145 capletVol_ = QuantLib::ext::make_shared<ProxyOptionletVolatility>(
146 Handle<OptionletVolatilityStructure>(sourceVol->second.first->capletVolStructure()), sourceIndex, targetIndex,
147 config.proxySourceRateComputationPeriod(), config.proxyTargetRateComputationPeriod());
148}
+ Here is the call graph for this function:

◆ shiftQuote()

Real shiftQuote ( const QuantLib::Date &  asof,
CapFloorVolatilityCurveConfig config,
const Loader loader 
) const
private

Get a shift quote value from the configured quotes.

Definition at line 1352 of file capfloorvolcurve.cpp.

1353 {
1354
1355 QL_REQUIRE(config.volatilityType() == CfgVolType::ShiftedLognormal,
1356 "Method shiftQuote should not be called with a config who's volatility type is not ShiftedLognormal");
1357
1358 // Search for the shift quote in the configured quotes
1359 for (const string& quoteId : config.quotes()) {
1360
1361 QuantLib::ext::shared_ptr<MarketDatum> md = loader.get(quoteId, asof);
1362
1363 // If it is a shift quote
1364 if (QuantLib::ext::shared_ptr<CapFloorShiftQuote> sq = QuantLib::ext::dynamic_pointer_cast<CapFloorShiftQuote>(md)) {
1365 return sq->quote()->value();
1366 }
1367 }
1368
1369 QL_FAIL("Could not find a shift quote for cap floor config " << config.curveID());
1370}
+ Here is the call graph for this function:

◆ transform() [1/2]

QuantLib::ext::shared_ptr< StrippedOptionlet > transform ( const QuantExt::OptionletStripper os) const
private

Transform QuantExt::OptionletStripper to QuantLib::StrippedOptionlet.

Definition at line 1375 of file capfloorvolcurve.cpp.

1375 {
1376
1377 vector<vector<Handle<Quote>>> vols(os.optionletFixingDates().size());
1378 for (Size i = 0; i < os.optionletFixingDates().size(); i++) {
1379 for (Size j = 0; j < os.optionletVolatilities(i).size(); j++) {
1380 vols[i].push_back(Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(os.optionletVolatilities(i)[j])));
1381 }
1382 }
1383
1384 vector<vector<Real>> optionletStrikes;
1385 for (Size i = 0; i < os.optionletFixingDates().size(); i++) {
1386 optionletStrikes.push_back(os.optionletStrikes(i));
1387 }
1388
1389 QuantLib::ext::shared_ptr<StrippedOptionlet> res = QuantLib::ext::make_shared<StrippedOptionlet>(
1391 optionletStrikes, vols, os.dayCounter(), os.volatilityType(), os.displacement(), os.atmOptionletRates());
1392
1393 res->unregisterWithAll();
1394
1395 return res;
1396}
Calendar calendar() const override
const std::vector< Date > & optionletFixingDates() const override
const std::vector< Rate > & optionletStrikes(Size i) const override
const std::vector< Volatility > & optionletVolatilities(Size i) const override
const std::vector< Rate > & atmOptionletRates() const override
VolatilityType volatilityType() const override
Natural settlementDays() const override
DayCounter dayCounter() const override
BusinessDayConvention businessDayConvention() const override
Real displacement() const override
ext::shared_ptr< IborIndex > index() const
Size size(const ValueType &v)
Definition: value.cpp:145
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ transform() [2/2]

QuantLib::ext::shared_ptr< QuantLib::StrippedOptionlet > transform ( const QuantLib::Date &  asof,
std::vector< QuantLib::Date >  dates,
const std::vector< QuantLib::Volatility > &  volatilities,
QuantLib::Natural  settleDays,
const QuantLib::Calendar &  cal,
QuantLib::BusinessDayConvention  bdc,
QuantLib::ext::shared_ptr< QuantLib::IborIndex >  iborIndex,
const QuantLib::DayCounter &  dc,
QuantLib::VolatilityType  type,
QuantLib::Real  displacement 
) const
private

Create a stripped optionlet curve from ATM optionlet dates and optionlet vols.

◆ populateFixingDates()

vector< Date > populateFixingDates ( const QuantLib::Date &  asof,
CapFloorVolatilityCurveConfig config,
QuantLib::ext::shared_ptr< QuantLib::IborIndex >  iborIndex,
const vector< Period > &  configTenors 
)
private

Generate fixing days from end date for optionlet vol.

Definition at line 1424 of file capfloorvolcurve.cpp.

1425 {
1426 // Find the fixing date of the term quotes
1427 vector<Date> fixingDates;
1428 bool isOis = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(iborIndex) != nullptr;
1429 QuantLib::ext::shared_ptr<BlackCapFloorEngine> dummyEngine =
1430 QuantLib::ext::make_shared<BlackCapFloorEngine>(iborIndex->forwardingTermStructure(), 0.20, config.dayCounter());
1431 for (Size i = 0; i < configTenors.size(); i++) {
1432 if (isOis) {
1433 Leg dummyCap =
1434 MakeOISCapFloor(CapFloor::Cap, configTenors[i], QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(iborIndex),
1435 config.rateComputationPeriod(), 0.04)
1437 .withSettlementDays(config.settleDays());
1438 auto lastCoupon = QuantLib::ext::dynamic_pointer_cast<CappedFlooredOvernightIndexedCoupon>(dummyCap.back());
1439 QL_REQUIRE(lastCoupon, "OptionletStripper::populateDates(): expected CappedFlooredOvernightIndexedCoupon");
1440 fixingDates.push_back(std::max(asof + 1, lastCoupon->underlying()->fixingDates().front()));
1441 } else {
1442 CapFloor dummyCap =
1443 MakeCapFloor(CapFloor::Cap, configTenors[i], iborIndex, 0.04, 0 * Days).withPricingEngine(dummyEngine);
1444 QuantLib::ext::shared_ptr<FloatingRateCoupon> lastCoupon = dummyCap.lastFloatingRateCoupon();
1445 fixingDates.push_back(std::max(asof + 1, lastCoupon->fixingDate()));
1446 }
1447 }
1448 return fixingDates;
1449}
MakeOISCapFloor & withSettlementDays(Natural settlementDays)
MakeOISCapFloor & withTelescopicValueDates(bool telescopicValueDates)
CapFloor
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ buildCalibrationInfo()

void buildCalibrationInfo ( const Date &  asof,
const CurveConfigurations curveConfigs,
const QuantLib::ext::shared_ptr< CapFloorVolatilityCurveConfig config,
const QuantLib::ext::shared_ptr< IborIndex > &  iborIndex 
)
private

Build calibration info.

Definition at line 1450 of file capfloorvolcurve.cpp.

1452 {
1453 DLOG("Building calibration info for cap floor vols");
1454
1455 ReportConfig rc = effectiveReportConfig(curveConfigs.reportConfigIrCapFloorVols(), config->reportConfig());
1456 bool reportOnStrikeGrid = *rc.reportOnStrikeGrid();
1457 bool reportOnStrikeSpreadGrid = *rc.reportOnStrikeSpreadGrid();
1458 std::vector<Real> strikes = *rc.strikes();
1459 std::vector<Real> strikeSpreads = *rc.strikeSpreads();
1460 std::vector<Period> expiries = *rc.expiries();
1461 std::vector<Period> underlyingTenorsReport(1, index->tenor());
1462
1463 calibrationInfo_ = QuantLib::ext::make_shared<IrVolCalibrationInfo>();
1464
1465 calibrationInfo_->dayCounter = config->dayCounter().empty() ? "na" : config->dayCounter().name();
1466 calibrationInfo_->calendar = config->calendar().empty() ? "na" : config->calendar().name();
1467 calibrationInfo_->volatilityType = ore::data::to_string(capletVol_->volatilityType());
1468 calibrationInfo_->underlyingTenors = underlyingTenorsReport;
1469
1470 bool isOis = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(index) != nullptr;
1471
1472 Size onSettlementDays = 0;
1473 if (isOis) {
1474 onSettlementDays = config->onCapSettlementDays();
1475 }
1476
1477 std::vector<Real> times; // fixing times of caplets
1478 std::vector<std::vector<Real>> forwards; // fair rates of caplets
1479 for (auto const& p : expiries) {
1480 Date fixingDate;
1481 Real forward;
1482 if (isOis) {
1483 Leg dummyCap = MakeOISCapFloor(CapFloor::Cap, p, QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(index),
1484 config->rateComputationPeriod(), 0.04)
1486 .withSettlementDays(onSettlementDays);
1487 if (dummyCap.empty())
1488 continue;
1489 auto lastCoupon = QuantLib::ext::dynamic_pointer_cast<CappedFlooredOvernightIndexedCoupon>(dummyCap.back());
1490 QL_REQUIRE(lastCoupon, "OptionletStripper::populateDates(): expected CappedFlooredOvernightIndexedCoupon");
1491 fixingDate = std::max(asof + 1, lastCoupon->underlying()->fixingDates().front());
1492 forward = lastCoupon->underlying()->rate();
1493 } else {
1494 CapFloor dummyCap = MakeCapFloor(CapFloor::Cap, p, index, 0.04, 0 * Days);
1495 if (dummyCap.floatingLeg().empty())
1496 continue;
1497 QuantLib::ext::shared_ptr<FloatingRateCoupon> lastCoupon = dummyCap.lastFloatingRateCoupon();
1498 fixingDate = lastCoupon->fixingDate();
1499 forward = index->fixing(fixingDate);
1500 }
1501 calibrationInfo_->expiryDates.push_back(fixingDate);
1502 times.push_back(capletVol_->dayCounter().empty() ? Actual365Fixed().yearFraction(asof, fixingDate)
1503 : capletVol_->timeFromReference(fixingDate));
1504 forwards.push_back(std::vector<Real>(1, forward));
1505 }
1506
1507 calibrationInfo_->times = times;
1508 calibrationInfo_->forwards = forwards;
1509
1510 std::vector<std::vector<std::vector<Real>>> callPricesStrike(
1511 times.size(),
1512 std::vector<std::vector<Real>>(underlyingTenorsReport.size(), std::vector<Real>(strikes.size(), 0.0)));
1513 std::vector<std::vector<std::vector<Real>>> callPricesStrikeSpread(
1514 times.size(),
1515 std::vector<std::vector<Real>>(underlyingTenorsReport.size(), std::vector<Real>(strikeSpreads.size(), 0.0)));
1516
1517 calibrationInfo_->isArbitrageFree = true;
1518
1519 if (reportOnStrikeGrid) {
1520 calibrationInfo_->strikes = strikes;
1521 calibrationInfo_->strikeGridStrikes = std::vector<std::vector<std::vector<Real>>>(
1522 times.size(),
1523 std::vector<std::vector<Real>>(underlyingTenorsReport.size(), std::vector<Real>(strikes.size(), 0.0)));
1524 calibrationInfo_->strikeGridProb = std::vector<std::vector<std::vector<Real>>>(
1525 times.size(),
1526 std::vector<std::vector<Real>>(underlyingTenorsReport.size(), std::vector<Real>(strikes.size(), 0.0)));
1527 calibrationInfo_->strikeGridImpliedVolatility = std::vector<std::vector<std::vector<Real>>>(
1528 times.size(),
1529 std::vector<std::vector<Real>>(underlyingTenorsReport.size(), std::vector<Real>(strikes.size(), 0.0)));
1530 calibrationInfo_->strikeGridCallSpreadArbitrage = std::vector<std::vector<std::vector<bool>>>(
1531 times.size(),
1532 std::vector<std::vector<bool>>(underlyingTenorsReport.size(), std::vector<bool>(strikes.size(), true)));
1533 calibrationInfo_->strikeGridButterflyArbitrage = std::vector<std::vector<std::vector<bool>>>(
1534 times.size(),
1535 std::vector<std::vector<bool>>(underlyingTenorsReport.size(), std::vector<bool>(strikes.size(), true)));
1536 TLOG("Strike surface arbitrage analysis result:");
1537 for (Size u = 0; u < underlyingTenorsReport.size(); ++u) {
1538 TLOG("Underlying tenor " << underlyingTenorsReport[u]);
1539 for (Size i = 0; i < times.size(); ++i) {
1540 Real t = times[i];
1541 Real shift = capletVol_->volatilityType() == Normal ? 0.0 : capletVol_->displacement();
1542 bool validSlice = true;
1543 for (Size j = 0; j < strikes.size(); ++j) {
1544 try {
1545 Real stddev = 0.0;
1546 if (capletVol_->volatilityType() == ShiftedLognormal) {
1547 if ((strikes[j] > -shift || close_enough(strikes[j], -shift)) &&
1548 (forwards[i][u] > -shift || close_enough(forwards[i][u], -shift))) {
1549 stddev =
1550 std::sqrt(capletVol_->blackVariance(t, strikes[j]));
1551 callPricesStrike[i][u][j] =
1552 blackFormula(Option::Type::Call, strikes[j], forwards[i][u], stddev);
1553 }
1554 } else {
1555 stddev = std::sqrt(capletVol_->blackVariance(t, strikes[j]));
1556 callPricesStrike[i][u][j] =
1557 bachelierBlackFormula(Option::Type::Call, strikes[j], forwards[i][u], stddev);
1558 }
1559 calibrationInfo_->strikeGridStrikes[i][u][j] = strikes[j];
1560 calibrationInfo_->strikeGridImpliedVolatility[i][u][j] = stddev / std::sqrt(t);
1561 } catch (const std::exception& e) {
1562 validSlice = false;
1563 TLOG("error for time " << t << " strike " << strikes[j] << ": " << e.what());
1564 }
1565 }
1566 if (validSlice) {
1567 try {
1569 forwards[i][u], callPricesStrike[i][u],
1570 capletVol_->volatilityType(), shift);
1571 calibrationInfo_->strikeGridCallSpreadArbitrage[i][u] = cm.callSpreadArbitrage();
1572 calibrationInfo_->strikeGridButterflyArbitrage[i][u] = cm.butterflyArbitrage();
1573 if (!cm.arbitrageFree())
1574 calibrationInfo_->isArbitrageFree = false;
1575 calibrationInfo_->strikeGridProb[i][u] = cm.density();
1577 } catch (const std::exception& e) {
1578 TLOG("error for time " << t << ": " << e.what());
1579 calibrationInfo_->isArbitrageFree = false;
1580 TLOGGERSTREAM("..(invalid slice)..");
1581 }
1582 } else {
1583 TLOGGERSTREAM("..(invalid slice)..");
1584 }
1585 }
1586 }
1587 TLOG("Strike cube arbitrage analysis completed.");
1588 }
1589
1590 if (reportOnStrikeSpreadGrid) {
1591 calibrationInfo_->strikeSpreads = strikeSpreads;
1592 calibrationInfo_->strikeSpreadGridStrikes = std::vector<std::vector<std::vector<Real>>>(
1593 times.size(), std::vector<std::vector<Real>>(underlyingTenorsReport.size(),
1594 std::vector<Real>(strikeSpreads.size(), 0.0)));
1595 calibrationInfo_->strikeSpreadGridProb = std::vector<std::vector<std::vector<Real>>>(
1596 times.size(), std::vector<std::vector<Real>>(underlyingTenorsReport.size(),
1597 std::vector<Real>(strikeSpreads.size(), 0.0)));
1598 calibrationInfo_->strikeSpreadGridImpliedVolatility = std::vector<std::vector<std::vector<Real>>>(
1599 times.size(), std::vector<std::vector<Real>>(underlyingTenorsReport.size(),
1600 std::vector<Real>(strikeSpreads.size(), 0.0)));
1601 calibrationInfo_->strikeSpreadGridCallSpreadArbitrage = std::vector<std::vector<std::vector<bool>>>(
1602 times.size(), std::vector<std::vector<bool>>(underlyingTenorsReport.size(),
1603 std::vector<bool>(strikeSpreads.size(), true)));
1604 calibrationInfo_->strikeSpreadGridButterflyArbitrage = std::vector<std::vector<std::vector<bool>>>(
1605 times.size(), std::vector<std::vector<bool>>(underlyingTenorsReport.size(),
1606 std::vector<bool>(strikeSpreads.size(), true)));
1607 TLOG("Strike Spread surface arbitrage analysis result:");
1608 for (Size u = 0; u < underlyingTenorsReport.size(); ++u) {
1609 TLOG("Underlying tenor " << underlyingTenorsReport[u]);
1610 for (Size i = 0; i < times.size(); ++i) {
1611 Real t = times[i];
1612 Real shift = capletVol_->volatilityType() == Normal ? 0.0 : capletVol_->displacement();
1613 bool validSlice = true;
1614 for (Size j = 0; j < strikeSpreads.size(); ++j) {
1615 Real strike = forwards[i][u] + strikeSpreads[j];
1616 try {
1617 Real stddev = 0.0;
1618 if (capletVol_->volatilityType() == ShiftedLognormal) {
1619 if ((strike > -shift || close_enough(strike, -shift)) &&
1620 (forwards[i][u] > -shift || close_enough(forwards[i][u], -shift))) {
1621 stddev = std::sqrt(capletVol_->blackVariance(t, strike));
1622 callPricesStrikeSpread[i][u][j] =
1623 blackFormula(Option::Type::Call, strike, forwards[i][u], stddev);
1624 }
1625 } else {
1626 stddev = std::sqrt(capletVol_->blackVariance(t, strike));
1627 callPricesStrikeSpread[i][u][j] =
1628 bachelierBlackFormula(Option::Type::Call, strike, forwards[i][u], stddev);
1629 }
1630 calibrationInfo_->strikeSpreadGridStrikes[i][u][j] = strike;
1631 calibrationInfo_->strikeSpreadGridImpliedVolatility[i][u][j] = stddev / std::sqrt(t);
1632 } catch (const std::exception& e) {
1633 validSlice = false;
1634 TLOG("error for time " << t << " strike spread " << strikeSpreads[j] << " strike " << strike
1635 << ": " << e.what());
1636 }
1637 }
1638 if (validSlice) {
1639 try {
1641 calibrationInfo_->strikeSpreadGridStrikes[i][u], forwards[i][u],
1642 callPricesStrikeSpread[i][u], capletVol_->volatilityType(), shift);
1643 calibrationInfo_->strikeSpreadGridCallSpreadArbitrage[i][u] = cm.callSpreadArbitrage();
1644 calibrationInfo_->strikeSpreadGridButterflyArbitrage[i][u] = cm.butterflyArbitrage();
1645 if (!cm.arbitrageFree())
1646 calibrationInfo_->isArbitrageFree = false;
1647 calibrationInfo_->strikeSpreadGridProb[i][u] = cm.density();
1649 } catch (const std::exception& e) {
1650 TLOG("error for time " << t << ": " << e.what());
1651 calibrationInfo_->isArbitrageFree = false;
1652 TLOGGERSTREAM("..(invalid slice)..");
1653 }
1654 } else {
1655 TLOGGERSTREAM("..(invalid slice)..");
1656 }
1657 }
1658 }
1659 TLOG("Strike Spread cube arbitrage analysis completed.");
1660 }
1661
1662 // output SABR calibration to log, if SABR was used
1663
1664 QuantLib::ext::shared_ptr<QuantExt::SabrParametricVolatility> p;
1665 if (auto s = QuantLib::ext::dynamic_pointer_cast<SabrStrippedOptionletAdapter<Linear>>(capletVol_))
1666 p = QuantExt::ext::dynamic_pointer_cast<SabrParametricVolatility>(s->parametricVolatility());
1667 else if (auto s = QuantLib::ext::dynamic_pointer_cast<SabrStrippedOptionletAdapter<LinearFlat>>(capletVol_))
1668 p = QuantExt::ext::dynamic_pointer_cast<SabrParametricVolatility>(s->parametricVolatility());
1669 else if (auto s = QuantLib::ext::dynamic_pointer_cast<SabrStrippedOptionletAdapter<Cubic>>(capletVol_))
1670 p = QuantExt::ext::dynamic_pointer_cast<SabrParametricVolatility>(s->parametricVolatility());
1671 else if (auto s = QuantLib::ext::dynamic_pointer_cast<SabrStrippedOptionletAdapter<CubicFlat>>(capletVol_))
1672 p = QuantExt::ext::dynamic_pointer_cast<SabrParametricVolatility>(s->parametricVolatility());
1673 else if (auto s = QuantLib::ext::dynamic_pointer_cast<SabrStrippedOptionletAdapter<BackwardFlat>>(capletVol_))
1674 p = QuantExt::ext::dynamic_pointer_cast<SabrParametricVolatility>(s->parametricVolatility());
1675
1676 if (p) {
1677 DLOG("SABR parameters:");
1678 DLOG("alpha (pls ignore second row, this is present for technical reasons):");
1679 DLOGGERSTREAM(p->alpha());
1680 DLOG("beta (pls ignore second row, this is present for technical reasons):");
1681 DLOGGERSTREAM(p->beta());
1682 DLOG("nu (pls ignore second row, this is present for technical reasons):");
1683 DLOGGERSTREAM(p->nu());
1684 DLOG("rho (pls ignore second row, this is present for technical reasons):");
1685 DLOGGERSTREAM(p->rho());
1686 DLOG("lognormal shift (pls ignore second row, this is present for technical reasons):");
1687 DLOGGERSTREAM(p->lognormalShift());
1688 DLOG("calibration attempts:");
1689 DLOGGERSTREAM(p->numberOfCalibrationAttempts());
1690 DLOG("calibration error:");
1691 DLOGGERSTREAM(p->calibrationError());
1692 DLOG("isInterpolated (1 means calibration failed and point is interpolated):");
1693 DLOGGERSTREAM(p->isInterpolated());
1694 }
1695
1696 DLOG("Building calibration info cap floor vols completed.");
1697}
#define TLOGGERSTREAM(text)
Definition: log.hpp:633
#define TLOG(text)
Logging Macro (Level = Data)
Definition: log.hpp:556
#define DLOGGERSTREAM(text)
Definition: log.hpp:632
QuantLib::Date fixingDate(const QuantLib::Date &d, const QuantLib::Period obsLag, const QuantLib::Frequency freq, bool interpolated)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
std::string arbitrageAsString(const CarrMadanMarginalProbabilityClass &cm)
ReportConfig effectiveReportConfig(const ReportConfig &globalConfig, const ReportConfig &localConfig)
vector< string > curveConfigs
+ Here is the call graph for this function:

Member Data Documentation

◆ spec_

Definition at line 69 of file capfloorvolcurve.hpp.

◆ capletVol_

QuantLib::ext::shared_ptr<QuantLib::OptionletVolatilityStructure> capletVol_
private

Definition at line 70 of file capfloorvolcurve.hpp.

◆ calibrationInfo_

QuantLib::ext::shared_ptr<IrVolCalibrationInfo> calibrationInfo_
private

Definition at line 71 of file capfloorvolcurve.hpp.