Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
List of all members
CommodityVolCurve Class Reference

Wrapper class for building commodity volatility structures. More...

#include <ored/marketdata/commodityvolcurve.hpp>

+ Collaboration diagram for CommodityVolCurve:

Public Member Functions

Constructors
 CommodityVolCurve ()
 Default constructor. More...
 
 CommodityVolCurve (const QuantLib::Date &asof, const CommodityVolatilityCurveSpec &spec, const Loader &loader, const CurveConfigurations &curveConfigs, const std::map< std::string, QuantLib::ext::shared_ptr< YieldCurve > > &yieldCurves={}, const std::map< std::string, QuantLib::ext::shared_ptr< CommodityCurve > > &commodityCurves={}, const std::map< std::string, QuantLib::ext::shared_ptr< CommodityVolCurve > > &commodityVolCurves={}, const map< string, QuantLib::ext::shared_ptr< FXVolCurve > > &fxVolCurves={}, const map< string, QuantLib::ext::shared_ptr< CorrelationCurve > > &correlationCurves={}, const Market *fxIndices=nullptr, const bool buildCalibrationInfo=true)
 Detailed constructor. More...
 

Inspectors

CommodityVolatilityCurveSpec spec_
 
QuantLib::ext::shared_ptr< QuantLib::BlackVolTermStructure > volatility_
 
QuantLib::ext::shared_ptr< QuantExt::FutureExpiryCalculatorexpCalc_
 
QuantLib::ext::shared_ptr< CommodityFutureConventionconvention_
 
QuantLib::Calendar calendar_
 
QuantLib::DayCounter dayCounter_
 
QuantLib::ext::shared_ptr< FxEqCommVolCalibrationInfocalibrationInfo_
 
QuantLib::Date maxExpiry_
 
QuantLib::Handle< QuantExt::PriceTermStructurepts_
 
QuantLib::Handle< QuantLib::YieldTermStructure > yts_
 
const CommodityVolatilityCurveSpecspec () const
 
const QuantLib::ext::shared_ptr< QuantLib::BlackVolTermStructure > & volatility ()
 
void buildVolCalibrationInfo (const Date &asof, QuantLib::ext::shared_ptr< VolatilityConfig > &volatilityConfig, const CurveConfigurations &curveConfigs, const CommodityVolatilityConfig &config)
 Build the calibration info. More...
 
const QuantLib::ext::shared_ptr< FxEqCommVolCalibrationInfo > & calibrationInfo () const
 
void buildVolatility (const QuantLib::Date &asof, const CommodityVolatilityConfig &vc, const ConstantVolatilityConfig &cvc, const Loader &loader)
 Build a volatility structure from a single constant volatility quote. More...
 
void buildVolatility (const QuantLib::Date &asof, const CommodityVolatilityConfig &vc, const VolatilityCurveConfig &vcc, const Loader &loader)
 Build a volatility curve from a 1-D curve of volatility quotes. More...
 
void buildVolatility (const QuantLib::Date &asof, CommodityVolatilityConfig &vc, const VolatilityStrikeSurfaceConfig &vssc, const Loader &loader)
 Build a volatility surface from a collection of expiry and absolute strike pairs. More...
 
void buildVolatilityExplicit (const QuantLib::Date &asof, CommodityVolatilityConfig &vc, const VolatilityStrikeSurfaceConfig &vssc, const Loader &loader, const std::vector< QuantLib::Real > &configuredStrikes)
 
void buildVolatility (const QuantLib::Date &asof, CommodityVolatilityConfig &vc, const VolatilityDeltaSurfaceConfig &vdsc, const Loader &loader)
 
void buildVolatility (const QuantLib::Date &asof, CommodityVolatilityConfig &vc, const VolatilityMoneynessSurfaceConfig &vmsc, const Loader &loader)
 
void buildVolatility (const QuantLib::Date &asof, CommodityVolatilityConfig &vc, const VolatilityApoFutureSurfaceConfig &vapo, const QuantLib::Handle< QuantLib::BlackVolTermStructure > &baseVts, const QuantLib::Handle< QuantExt::PriceTermStructure > &basePts)
 
void buildVolatility (const QuantLib::Date &asof, const CommodityVolatilityCurveSpec &spec, const CurveConfigurations &curveConfigs, const ProxyVolatilityConfig &pvc, const map< string, QuantLib::ext::shared_ptr< CommodityCurve > > &comCurves, const map< string, QuantLib::ext::shared_ptr< CommodityVolCurve > > &volCurves, const map< string, QuantLib::ext::shared_ptr< FXVolCurve > > &fxVolCurves, const map< string, QuantLib::ext::shared_ptr< CorrelationCurve > > &correlationCurves, const Market *fxIndices=nullptr)
 Build a volatility surface as a proxy from another volatility surface. More...
 
QuantLib::Handle< QuantExt::PriceTermStructurecorrectFuturePriceCurve (const QuantLib::Date &asof, const std::string &contractName, const QuantLib::ext::shared_ptr< QuantExt::PriceTermStructure > &pts, const std::vector< QuantLib::Date > &optionExpiries) const
 
QuantLib::Date getExpiry (const QuantLib::Date &asof, const QuantLib::ext::shared_ptr< Expiry > &expiry, const std::string &name, QuantLib::Natural rollDays) const
 Get an explicit expiry date from a commodity option quote's Expiry. More...
 
void populateCurves (const CommodityVolatilityConfig &config, const std::map< std::string, QuantLib::ext::shared_ptr< YieldCurve > > &yieldCurves, const std::map< std::string, QuantLib::ext::shared_ptr< CommodityCurve > > &commodityCurves, bool searchYield, bool dontThrow=false)
 Populate price curve, pts_, and yield curve, yts_. More...
 
std::vector< QuantLib::Real > checkMoneyness (const std::vector< std::string > &moneynessLevels) const
 Check and return moneyness levels. More...
 

Detailed Description

Wrapper class for building commodity volatility structures.

Definition at line 42 of file commodityvolcurve.hpp.

Constructor & Destructor Documentation

◆ CommodityVolCurve() [1/2]

Default constructor.

Definition at line 47 of file commodityvolcurve.hpp.

47{}

◆ CommodityVolCurve() [2/2]

CommodityVolCurve ( const QuantLib::Date &  asof,
const CommodityVolatilityCurveSpec spec,
const Loader loader,
const CurveConfigurations curveConfigs,
const std::map< std::string, QuantLib::ext::shared_ptr< YieldCurve > > &  yieldCurves = {},
const std::map< std::string, QuantLib::ext::shared_ptr< CommodityCurve > > &  commodityCurves = {},
const std::map< std::string, QuantLib::ext::shared_ptr< CommodityVolCurve > > &  commodityVolCurves = {},
const map< string, QuantLib::ext::shared_ptr< FXVolCurve > > &  fxVolCurves = {},
const map< string, QuantLib::ext::shared_ptr< CorrelationCurve > > &  correlationCurves = {},
const Market fxIndices = nullptr,
const bool  buildCalibrationInfo = true 
)

Detailed constructor.

Member Function Documentation

◆ spec()

const CommodityVolatilityCurveSpec & spec ( ) const

Definition at line 62 of file commodityvolcurve.hpp.

62{ return spec_; }
CommodityVolatilityCurveSpec spec_
+ Here is the caller graph for this function:

◆ volatility()

const QuantLib::ext::shared_ptr< QuantLib::BlackVolTermStructure > & volatility ( )

Definition at line 63 of file commodityvolcurve.hpp.

63{ return volatility_; }
QuantLib::ext::shared_ptr< QuantLib::BlackVolTermStructure > volatility_

◆ buildVolCalibrationInfo()

void buildVolCalibrationInfo ( const Date &  asof,
QuantLib::ext::shared_ptr< VolatilityConfig > &  volatilityConfig,
const CurveConfigurations curveConfigs,
const CommodityVolatilityConfig config 
)

Build the calibration info.

Definition at line 1742 of file commodityvolcurve.cpp.

1744 {
1745 DLOG("CommodityVolCurve: building volatility calibration info");
1746 try{
1747
1748 ReportConfig rc = effectiveReportConfig(curveConfigs.reportConfigCommVols(), config.reportConfig());
1749 bool reportOnDeltaGrid = *rc.reportOnDeltaGrid();
1750 bool reportOnMoneynessGrid = *rc.reportOnMoneynessGrid();
1751 std::vector<Real> moneyness = *rc.moneyness();
1752 std::vector<std::string> deltas = *rc.deltas();
1753 std::vector<Period> expiries = *rc.expiries();
1754
1755
1756 auto info = QuantLib::ext::make_shared<FxEqCommVolCalibrationInfo>();
1757
1758 DeltaVolQuote::AtmType atmType = DeltaVolQuote::AtmType::AtmDeltaNeutral;
1759 DeltaVolQuote::DeltaType deltaType = DeltaVolQuote::DeltaType::Fwd;
1760
1761 if (auto vdsc = QuantLib::ext::dynamic_pointer_cast<VolatilityDeltaSurfaceConfig>(vc)) {
1762 atmType = parseAtmType(vdsc->atmType());
1763 deltaType = parseDeltaType(vdsc->deltaType());
1764 }
1765
1766 info->dayCounter = to_string(dayCounter_);
1767 info->calendar = to_string(calendar_).empty() ? "na" : calendar_.name();
1768 info->atmType = ore::data::to_string(atmType);
1769 info->deltaType = ore::data::to_string(deltaType);
1770 info->longTermAtmType = ore::data::to_string(atmType);
1771 info->longTermDeltaType = ore::data::to_string(deltaType);
1772 info->switchTenor = "na";
1773 info->riskReversalInFavorOf = "na";
1774 info->butterflyStyle = "na";
1775
1776 std::vector<Real> times, forwards;
1777 for (auto const& p : expiries) {
1778 auto d = volatility_->optionDateFromTenor(p);
1779 info->expiryDates.push_back(d);
1780 times.push_back(volatility_->dayCounter().empty() ? Actual365Fixed().yearFraction(asof, d)
1781 : volatility_->timeFromReference(d));
1782 forwards.push_back(pts_->price(d));
1783 }
1784
1785 info->times = times;
1786 info->forwards = forwards;
1787 std::vector<std::vector<Real>> callPricesDelta(times.size(), std::vector<Real>(deltas.size(), 0.0));
1788 if (reportOnDeltaGrid) {
1789 info->deltas = deltas;
1790 info->deltaCallPrices =
1791 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(deltas.size(), 0.0));
1792 info->deltaPutPrices =
1793 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(deltas.size(), 0.0));
1794 info->deltaGridStrikes =
1795 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(deltas.size(), 0.0));
1796 info->deltaGridProb = std::vector<std::vector<Real>>(times.size(), std::vector<Real>(deltas.size(), 0.0));
1797 info->deltaGridImpliedVolatility =
1798 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(deltas.size(), 0.0));
1799 info->deltaGridCallSpreadArbitrage =
1800 std::vector<std::vector<bool>>(times.size(), std::vector<bool>(deltas.size(), true));
1801 info->deltaGridButterflyArbitrage =
1802 std::vector<std::vector<bool>>(times.size(), std::vector<bool>(deltas.size(), true));
1803
1804 Real maxTime = QL_MAX_REAL;
1805 if (maxExpiry_ != Date()) {
1806 if (volatility_->dayCounter().empty())
1807 maxTime = Actual365Fixed().yearFraction(asof, maxExpiry_);
1808 else
1809 maxTime = volatility_->timeFromReference(maxExpiry_);
1810 }
1811
1812 DeltaVolQuote::AtmType at;
1813 DeltaVolQuote::DeltaType dt;
1814 for (Size i = 0; i < times.size(); ++i) {
1815 Real t = times[i];
1816 at = atmType;
1817 dt = deltaType;
1818 // for times after the last quoted expiry we use artificial conventions to avoid problems with strike (as in equities)
1819 // from delta conversions: we use fwd delta always and ATM DNS
1820 if (t > maxTime) {
1821 at = DeltaVolQuote::AtmDeltaNeutral;
1822 dt = DeltaVolQuote::Fwd;
1823 }
1824 bool validSlice = true;
1825 for (Size j = 0; j < deltas.size(); ++j) {
1826 DeltaString d(deltas[j]);
1827 try {
1828 Real strike;
1829 if (d.isAtm()) {
1830 strike =
1831 QuantExt::getAtmStrike(dt, at, pts_->price(asof,true), yts_->discount(t), 1., volatility_, t);
1832 } else if (d.isCall()) {
1833 strike = QuantExt::getStrikeFromDelta(Option::Call, d.delta(), dt, pts_->price(asof,true),
1834 yts_->discount(t), 1., volatility_, t);
1835 } else {
1836 strike = QuantExt::getStrikeFromDelta(Option::Put, d.delta(), dt, pts_->price(asof,true),
1837 yts_->discount(t), 1., volatility_, t);
1838 }
1839 Real stddev = std::sqrt(volatility_->blackVariance(t, strike));
1840 callPricesDelta[i][j] = QuantExt::blackFormula(Option::Call, strike, forwards[i], stddev);
1841
1842 if (d.isPut()) {
1843 info->deltaPutPrices[i][j] = blackFormula(Option::Put, strike, forwards[i], stddev, yts_->discount(t));
1844 } else {
1845 info->deltaCallPrices[i][j] = blackFormula(Option::Call, strike, forwards[i], stddev, yts_->discount(t));
1846 }
1847
1848 info->deltaGridStrikes[i][j] = strike;
1849 info->deltaGridImpliedVolatility[i][j] = stddev / std::sqrt(t);
1850 } catch (const std::exception& e) {
1851 validSlice = false;
1852 TLOG("CommodityVolCurve: error for time " << t << " delta " << deltas[j] << ": " << e.what());
1853 }
1854 }
1855 if (validSlice) {
1856 try {
1857 QuantExt::CarrMadanMarginalProbability cm(info->deltaGridStrikes[i], forwards[i],
1858 callPricesDelta[i]);
1859 info->deltaGridCallSpreadArbitrage[i] = cm.callSpreadArbitrage();
1860 info->deltaGridButterflyArbitrage[i] = cm.butterflyArbitrage();
1861 if (!cm.arbitrageFree())
1862 info->isArbitrageFree = false;
1863 info->deltaGridProb[i] = cm.density();
1865 } catch (const std::exception& e) {
1866 TLOG("error for time " << t << ": " << e.what());
1867 info->isArbitrageFree = false;
1868 TLOGGERSTREAM("..(invalid slice)..");
1869 }
1870 } else {
1871 info->isArbitrageFree = false;
1872 TLOGGERSTREAM("..(invalid slice)..");
1873 }
1874 }
1875 TLOG("CommodityVolCurve: Delta surface arbitrage analysis completed.");
1876 }
1877 std::vector<std::vector<Real>> callPricesMoneyness(times.size(), std::vector<Real>(moneyness.size(), 0.0));
1878 if (reportOnMoneynessGrid) {
1879 info->moneyness = moneyness;
1880 info->moneynessCallPrices =
1881 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(moneyness.size(), 0.0));
1882 info->moneynessPutPrices =
1883 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(moneyness.size(), 0.0));
1884 info->moneynessGridStrikes =
1885 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(moneyness.size(), 0.0));
1886 info->moneynessGridProb =
1887 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(moneyness.size(), 0.0));
1888 info->moneynessGridImpliedVolatility =
1889 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(moneyness.size(), 0.0));
1890 info->moneynessGridCallSpreadArbitrage =
1891 std::vector<std::vector<bool>>(times.size(), std::vector<bool>(moneyness.size(), true));
1892 info->moneynessGridButterflyArbitrage =
1893 std::vector<std::vector<bool>>(times.size(), std::vector<bool>(moneyness.size(), true));
1894 info->moneynessGridCalendarArbitrage =
1895 std::vector<std::vector<bool>>(times.size(), std::vector<bool>(moneyness.size(), true));
1896 for (Size i = 0; i < times.size(); ++i) {
1897 Real t = times[i];
1898 for (Size j = 0; j < moneyness.size(); ++j) {
1899 try {
1900 Real strike = moneyness[j] * forwards[i];
1901 info->moneynessGridStrikes[i][j] = strike;
1902 Real stddev = std::sqrt(volatility_->blackVariance(t, strike));
1903 callPricesMoneyness[i][j] = blackFormula(Option::Call, strike, forwards[i], stddev);
1904 info->moneynessGridImpliedVolatility[i][j] = stddev / std::sqrt(t);
1905 if (moneyness[j] >= 1) {
1906 calibrationInfo_->moneynessCallPrices[i][j] = blackFormula(Option::Call, strike, forwards[i], stddev, yts_->discount(t));
1907 } else {
1908 calibrationInfo_->moneynessPutPrices[i][j] = blackFormula(Option::Put, strike, forwards[i], stddev, yts_->discount(t));
1909 };
1910 } catch (const std::exception& e) {
1911 TLOG("CommodityVolCurve: error for time " << t << " moneyness " << moneyness[j] << ": " << e.what());
1912 }
1913 }
1914 }
1915 if (!times.empty() && !moneyness.empty()) {
1916 try {
1917 QuantExt::CarrMadanSurface cm(times, moneyness, pts_->price(asof,true), forwards,
1918 callPricesMoneyness);
1919 for (Size i = 0; i < times.size(); ++i) {
1920 info->moneynessGridProb[i] = cm.timeSlices()[i].density();
1921 }
1922 info->moneynessGridCallSpreadArbitrage = cm.callSpreadArbitrage();
1923 info->moneynessGridButterflyArbitrage = cm.butterflyArbitrage();
1924 info->moneynessGridCalendarArbitrage = cm.calendarArbitrage();
1925 if (!cm.arbitrageFree())
1926 info->isArbitrageFree = false;
1927 TLOG("CommodityVolCurve: Moneyness surface Arbitrage analysis result:");
1929 } catch (const std::exception& e) {
1930 TLOG("CommodityVolCurve: error: " << e.what());
1931 info->isArbitrageFree = false;
1932 }
1933 TLOG("CommodityVolCurve: Moneyness surface Arbitrage analysis completed:");
1934 }
1935 }
1936 calibrationInfo_ = info;
1937 }
1938 catch (std::exception& e){
1939 QL_FAIL("CommodityVolCurve: calibration info building failed: " << e.what());
1940 } catch (...) {
1941 QL_FAIL("CommodityVolCurve: calibration info building failed: unknown error");
1942 }
1943 }
QuantLib::Handle< QuantExt::PriceTermStructure > pts_
QuantLib::ext::shared_ptr< FxEqCommVolCalibrationInfo > calibrationInfo_
QuantLib::Handle< QuantLib::YieldTermStructure > yts_
QuantLib::DayCounter dayCounter_
DeltaVolQuote::AtmType parseAtmType(const std::string &s)
Convert text to QuantLib::DeltaVolQuote::AtmType.
Definition: parsers.cpp:746
DeltaVolQuote::DeltaType parseDeltaType(const std::string &s)
Convert text to QuantLib::DeltaVolQuote::DeltaType.
Definition: parsers.cpp:763
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
#define TLOGGERSTREAM(text)
Definition: log.hpp:633
#define TLOG(text)
Logging Macro (Level = Data)
Definition: log.hpp:556
Real getAtmStrike(DeltaVolQuote::DeltaType dt, DeltaVolQuote::AtmType at, Real spot, Real domDiscount, Real forDiscount, boost::shared_ptr< BlackVolTermStructure > vol, Real t, Real accuracy, Size maxIterations)
std::string arbitrageAsString(const CarrMadanMarginalProbabilityClass &cm)
Real getStrikeFromDelta(Option::Type optionType, Real delta, DeltaVolQuote::DeltaType dt, Real spot, Real domDiscount, Real forDiscount, boost::shared_ptr< BlackVolTermStructure > vol, Real t, Real accuracy, Size maxIterations)
ReportConfig effectiveReportConfig(const ReportConfig &globalConfig, const ReportConfig &localConfig)
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
vector< string > curveConfigs
+ Here is the call graph for this function:

◆ calibrationInfo()

const QuantLib::ext::shared_ptr< FxEqCommVolCalibrationInfo > & calibrationInfo ( ) const

Definition at line 68 of file commodityvolcurve.hpp.

68{ return calibrationInfo_; }

◆ buildVolatility() [1/7]

void buildVolatility ( const QuantLib::Date &  asof,
const CommodityVolatilityConfig vc,
const ConstantVolatilityConfig cvc,
const Loader loader 
)
private

Build a volatility structure from a single constant volatility quote.

◆ buildVolatility() [2/7]

void buildVolatility ( const QuantLib::Date &  asof,
const CommodityVolatilityConfig vc,
const VolatilityCurveConfig vcc,
const Loader loader 
)
private

Build a volatility curve from a 1-D curve of volatility quotes.

Definition at line 310 of file commodityvolcurve.cpp.

311 {
312
313 LOG("CommodityVolCurve: start building 1-D volatility curve");
314
315 // Must have at least one quote
316 QL_REQUIRE(vcc.quotes().size() > 0, "No quotes specified in config " << vc.curveID());
317
318 QL_REQUIRE(vcc.quoteType() == MarketDatum::QuoteType::RATE_LNVOL, "CommodityVolCurve: only quote type" <<
319 " RATE_LNVOL is currently supported for 1-D commodity volatility curves.");
320
321 // Check if we are using a regular expression to select the quotes for the curve. If we are, the quotes should
322 // contain exactly one element.
323 auto wildcard = getUniqueWildcard(vcc.quotes());
324
325 // curveData will be populated with the expiry dates and volatility values.
326 map<Date, Real> curveData;
327
328 // Different approaches depending on whether we are using a regex or searching for a list of explicit quotes.
329 if (wildcard) {
330
331 DLOG("Have single quote with pattern " << wildcard->pattern());
332
333 // Loop over quotes and process commodity option quotes matching pattern on asof
334 for (const auto& md : loader.get(*wildcard, asof)) {
335
336 // Go to next quote if the market data point's date does not equal our asof
337 if (md->asofDate() != asof)
338 continue;
339
340 auto q = QuantLib::ext::dynamic_pointer_cast<CommodityOptionQuote>(md);
341 if (!q)
342 continue;
343 QL_REQUIRE(q->quoteType() == vcc.quoteType(),
344 "CommodityOptionQuote type '" << q->quoteType() << "' <> VolatilityCurveConfig quote type '" << vcc.quoteType() << "'");
345
346 TLOG("The quote " << q->name() << " matched the pattern");
347
348 Date expiryDate = getExpiry(asof, q->expiry(), vc.futureConventionsId(), vc.optionExpiryRollDays());
349 if (expiryDate > asof) {
350 // Add the quote to the curve data
351 auto it = curveData.find(expiryDate);
352 if (it != curveData.end()) {
353 LOG("Already added quote " << fixed << setprecision(9) << it->second << " for the expiry" <<
354 " date " << io::iso_date(expiryDate) << " for commodity curve " << vc.curveID() <<
355 " so not adding " << q->name());
356 } else {
357 curveData[expiryDate] = q->quote()->value();
358 TLOG("Added quote " << q->name() << ": (" << io::iso_date(expiryDate) << "," <<
359 fixed << setprecision(9) << q->quote()->value() << ")");
360 }
361 }
362 }
363
364 // Check that we have quotes in the end
365 QL_REQUIRE(curveData.size() > 0, "No quotes found matching regular expression " << vcc.quotes()[0]);
366
367 } else {
368
369 DLOG("Have " << vcc.quotes().size() << " explicit quotes");
370
371 // Loop over quotes and process commodity option quotes that are explicitly specified in the config
372 Size quotesAdded = 0;
373 Size skippedExpiredQuotes = 0;
374 for (const auto& quoteName : vcc.quotes()) {
375 auto md = loader.get(quoteName, asof);
376 QL_REQUIRE(md, "Unable to load quote '" << quoteName << "'");
377
378 QL_REQUIRE(md->asofDate() == asof, "MarketDatum asofDate '" << md->asofDate() << "' <> asof '" << asof << "'");
379
380 auto q = QuantLib::ext::dynamic_pointer_cast<CommodityOptionQuote>(md);
381 QL_REQUIRE(q, "Internal error: could not downcast MarketDatum '" << md->name() << "' to CommodityOptionQuote");
382
383 TLOG("Found the configured quote " << q->name());
384
385 Date expiryDate = getExpiry(asof, q->expiry(), vc.futureConventionsId(), vc.optionExpiryRollDays());
386 if (expiryDate > asof) {
387 QL_REQUIRE(expiryDate > asof, "Commodity volatility quote '"
388 << q->name() << "' has expiry in the past ("
389 << io::iso_date(expiryDate) << ")");
390 QL_REQUIRE(curveData.count(expiryDate) == 0, "Duplicate quote for the date "
391 << io::iso_date(expiryDate)
392 << " provided by commodity volatility config "
393 << vc.curveID());
394 curveData[expiryDate] = q->quote()->value();
395 quotesAdded++;
396 TLOG("Added quote " << q->name() << ": (" << io::iso_date(expiryDate) << "," << fixed
397 << setprecision(9) << q->quote()->value() << ")");
398 } else {
399 skippedExpiredQuotes++;
400 WLOG("Skipped quote " << q->name() << ": (" << io::iso_date(expiryDate) << "," << fixed
401 << setprecision(9) << q->quote()->value() << ")");
402 }
403 }
404
405 // Check that we have found all of the explicitly configured quotes
406 QL_REQUIRE((quotesAdded + skippedExpiredQuotes) == vcc.quotes().size(),
407 "Found " << quotesAdded << " live quotes and " << skippedExpiredQuotes << " expired quotes"
408 << " but " << vcc.quotes().size() << " quotes were given in config.");
409 }
410
411 // Create the dates and volatility vector
412 vector<Date> dates;
413 vector<Volatility> volatilities;
414 for (const auto& datum : curveData) {
415 dates.push_back(datum.first);
416 volatilities.push_back(datum.second);
417 TLOG("Added data point (" << io::iso_date(dates.back()) << "," << fixed << setprecision(9)
418 << volatilities.back() << ")");
419 }
420 // set max expiry date (used in buildCalibrationInfo())
421 if (!dates.empty())
422 maxExpiry_ = dates.back();
423 DLOG("Creating BlackVarianceCurve object.");
424 auto tmp = QuantLib::ext::make_shared<BlackVarianceCurve>(asof, dates, volatilities, dayCounter_);
425
426 // Set the interpolation.
427 if (vcc.interpolation() == "Linear") {
428 DLOG("Interpolation set to Linear.");
429 } else if (vcc.interpolation() == "Cubic") {
430 DLOG("Setting interpolation to Cubic.");
431 tmp->setInterpolation<Cubic>();
432 } else if (vcc.interpolation() == "LogLinear") {
433 DLOG("Setting interpolation to LogLinear.");
434 tmp->setInterpolation<LogLinear>();
435 } else {
436 DLOG("Interpolation " << vcc.interpolation() << " not recognised so leaving it Linear.");
437 }
438
439 // Set the volatility_ member after we have possibly updated the interpolation.
440 volatility_ = tmp;
441
442 // Set the extrapolation
443 if (parseExtrapolation(vcc.extrapolation()) == Extrapolation::Flat) {
444 DLOG("Enabling BlackVarianceCurve flat volatility extrapolation.");
445 volatility_->enableExtrapolation();
446 } else if (parseExtrapolation(vcc.extrapolation()) == Extrapolation::None) {
447 DLOG("Disabling BlackVarianceCurve extrapolation.");
448 volatility_->disableExtrapolation();
449 } else if (parseExtrapolation(vcc.extrapolation()) == Extrapolation::UseInterpolator) {
450 DLOG("BlackVarianceCurve does not support using interpolator for extrapolation "
451 << "so default to flat volatility extrapolation.");
452 volatility_->enableExtrapolation();
453 } else {
454 DLOG("Unexpected extrapolation so default to flat volatility extrapolation.");
455 volatility_->enableExtrapolation();
456 }
457
458 LOG("CommodityVolCurve: finished building 1-D volatility curve");
459}
QuantLib::Date getExpiry(const QuantLib::Date &asof, const QuantLib::ext::shared_ptr< Expiry > &expiry, const std::string &name, QuantLib::Natural rollDays) const
Get an explicit expiry date from a commodity option quote's Expiry.
#define LOG(text)
Logging Macro (Level = Notice)
Definition: log.hpp:552
#define WLOG(text)
Logging Macro (Level = Warning)
Definition: log.hpp:550
Extrapolation parseExtrapolation(const string &s)
Parse Extrapolation from string.
Definition: parsers.cpp:778
boost::optional< Wildcard > getUniqueWildcard(const C &c)
checks if at most one element in C has a wild card and returns it in this case
Definition: wildcard.hpp:65
+ Here is the call graph for this function:

◆ buildVolatility() [3/7]

void buildVolatility ( const QuantLib::Date &  asof,
CommodityVolatilityConfig vc,
const VolatilityStrikeSurfaceConfig vssc,
const Loader loader 
)
private

Build a volatility surface from a collection of expiry and absolute strike pairs.

◆ buildVolatilityExplicit()

void buildVolatilityExplicit ( const QuantLib::Date &  asof,
CommodityVolatilityConfig vc,
const VolatilityStrikeSurfaceConfig vssc,
const Loader loader,
const std::vector< QuantLib::Real > &  configuredStrikes 
)
private

Build a volatility surface from a collection of expiry and absolute strike pairs where the strikes and expiries are both explicitly configured i.e. where wild cards are not used for either the strikes or the expiries.

Definition at line 732 of file commodityvolcurve.cpp.

734 {
735
736 LOG("CommodityVolCurve: start building 2-D volatility absolute strike surface with explicit strikes and expiries");
737
738 QL_REQUIRE(vssc.quoteType() == MarketDatum::QuoteType::RATE_LNVOL, "CommodityVolCurve: only quote type" <<
739 " RATE_LNVOL is currently supported for 2-D volatility strike surface with explicit strikes and expiries.");
740
741 // Map to hold the rows of the commodity volatility matrix. The keys are the expiry dates and the values are the
742 // vectors of volatilities, one for each configured strike.
743 map<Date, vector<Real>> surfaceData;
744
745 // Count the number of quotes added. We check at the end that we have added all configured quotes.
746 Size quotesAdded = 0;
747 Size skippedExpiredQuotes = 0;
748 // Loop over quotes and process commodity option quotes that have been requested
749 std::ostringstream ss;
750 ss << MarketDatum::InstrumentType::COMMODITY_OPTION << "/" << vssc.quoteType() << "/" << vc.curveID() << "/"
751 << vc.currency() << "/*";
752 Wildcard w(ss.str());
753 for (const auto& md : loader.get(w, asof)) {
754
755 QL_REQUIRE(md->asofDate() == asof, "MarketDatum asofDate '" << md->asofDate() << "' <> asof '" << asof << "'");
756
757 auto q = QuantLib::ext::dynamic_pointer_cast<CommodityOptionQuote>(md);
758 QL_REQUIRE(q, "Internal error: could not downcast MarketDatum '" << md->name() << "' to CommodityOptionQuote");
759
760 // This surface is for absolute strikes only.
761 auto strike = QuantLib::ext::dynamic_pointer_cast<AbsoluteStrike>(q->strike());
762 if (!strike)
763 continue;
764
765 // If the quote is not in the configured quotes continue
766 auto it = find(vc.quotes().begin(), vc.quotes().end(), q->name());
767 if (it == vc.quotes().end())
768 continue;
769
770 // Process the quote
771 Date eDate = getExpiry(asof, q->expiry(), vc.futureConventionsId(), vc.optionExpiryRollDays());
772
773 if (eDate > asof) {
774
775 // Position of quote in vector of strikes
776 auto strikeIt = find_if(configuredStrikes.begin(), configuredStrikes.end(),
777 [&strike](Real s) { return close(s, strike->strike()); });
778 Size pos = distance(configuredStrikes.begin(), strikeIt);
779 QL_REQUIRE(pos < configuredStrikes.size(),
780 "The quote '"
781 << q->name()
782 << "' is in the list of configured quotes but does not match any of the configured strikes");
783
784 // Add quote to surface
785 if (surfaceData.count(eDate) == 0)
786 surfaceData[eDate] = vector<Real>(configuredStrikes.size(), Null<Real>());
787
788 QL_REQUIRE(surfaceData[eDate][pos] == Null<Real>(),
789 "Quote " << q->name() << " provides a duplicate quote for the date " << io::iso_date(eDate)
790 << " and the strike " << configuredStrikes[pos]);
791 surfaceData[eDate][pos] = q->quote()->value();
792 quotesAdded++;
793
794 TLOG("Added quote " << q->name() << ": (" << io::iso_date(eDate) << "," << fixed << setprecision(9)
795 << configuredStrikes[pos] << "," << q->quote()->value() << ")");
796 } else {
797 skippedExpiredQuotes++;
798 TLOG("Skipped quote " << q->name() << ": (" << io::iso_date(eDate) << "," << fixed << setprecision(9)
799 << q->quote()->value() << ")");
800 }
801 }
802
803 LOG("CommodityVolCurve: added " << quotesAdded << " quotes in building explicit absolute strike surface.");
804
805 QL_REQUIRE(vc.quotes().size() == quotesAdded + skippedExpiredQuotes,
806 "Found " << quotesAdded << " live quotes and " << skippedExpiredQuotes << "expired quotes, but " << vc.quotes().size()
807 << " quotes required by config.");
808
809 // Set up the BlackVarianceSurface. Note that the rows correspond to strikes and that the columns correspond to
810 // the expiry dates in the matrix that is fed to the BlackVarianceSurface ctor.
811 vector<Date> expiryDates;
812 Matrix volatilities = Matrix(configuredStrikes.size(), surfaceData.size());
813 Size expiryIdx = 0;
814 for (const auto& expiryRow : surfaceData) {
815 expiryDates.push_back(expiryRow.first);
816 for (Size i = 0; i < configuredStrikes.size(); i++) {
817 volatilities[i][expiryIdx] = expiryRow.second[i];
818 }
819 expiryIdx++;
820 }
821
822 // set max expiry date (used in buildCalibrationInfo())
823 if (!expiryDates.empty())
824 maxExpiry_ = *max_element(expiryDates.begin(), expiryDates.end());
825
826 // Trace log the surface
827 TLOG("Explicit strike surface grid points:");
828 TLOG("expiry,strike,volatility");
829 for (Size i = 0; i < volatilities.rows(); i++) {
830 for (Size j = 0; j < volatilities.columns(); j++) {
831 TLOG(io::iso_date(expiryDates[j])
832 << "," << fixed << setprecision(9) << configuredStrikes[i] << "," << volatilities[i][j]);
833 }
834 }
835
836 // Set the strike extrapolation which only matters if extrapolation is turned on for the whole surface.
837 // BlackVarianceSurface time extrapolation is hard-coded to constant in volatility.
838 BlackVarianceSurface::Extrapolation strikeExtrap = BlackVarianceSurface::ConstantExtrapolation;
839 if (vssc.extrapolation()) {
840
841 auto strikeExtrapType = parseExtrapolation(vssc.strikeExtrapolation());
842 if (strikeExtrapType == Extrapolation::UseInterpolator) {
843 DLOG("Strike extrapolation switched to using interpolator.");
844 strikeExtrap = BlackVarianceSurface::InterpolatorDefaultExtrapolation;
845 } else if (strikeExtrapType == Extrapolation::None) {
846 DLOG("Strike extrapolation cannot be turned off on its own so defaulting to flat.");
847 } else if (strikeExtrapType == Extrapolation::Flat) {
848 DLOG("Strike extrapolation has been set to flat.");
849 } else {
850 DLOG("Strike extrapolation " << strikeExtrapType << " not expected so default to flat.");
851 }
852
853 auto timeExtrapType = parseExtrapolation(vssc.timeExtrapolation());
854 if (timeExtrapType != Extrapolation::Flat) {
855 DLOG("BlackVarianceSurface only supports flat volatility extrapolation in the time direction");
856 }
857 } else {
858 DLOG("Extrapolation is turned off for the whole surface so the time and"
859 << " strike extrapolation settings are ignored");
860 }
861
862 DLOG("Creating BlackVarianceSurface object");
863 auto tmp = QuantLib::ext::make_shared<BlackVarianceSurface>(asof, calendar_, expiryDates, configuredStrikes, volatilities,
864 dayCounter_, strikeExtrap, strikeExtrap);
865
866 // Set the interpolation if configured properly. The default is Bilinear.
867 if (!(vssc.timeInterpolation() == "Linear" && vssc.strikeInterpolation() == "Linear")) {
868 if (vssc.timeInterpolation() != vssc.strikeInterpolation()) {
869 DLOG("Time and strike interpolation must be the same for BlackVarianceSurface but we got strike "
870 << "interpolation " << vssc.strikeInterpolation() << " and time interpolation "
871 << vssc.timeInterpolation());
872 } else if (vssc.timeInterpolation() == "Cubic") {
873 DLOG("Setting interpolation to BiCubic");
874 tmp->setInterpolation<Bicubic>();
875 } else {
876 DLOG("Interpolation type " << vssc.timeInterpolation() << " not supported in 2 dimensions");
877 }
878 }
879
880 // Set the volatility_ member after we have possibly updated the interpolation.
881 volatility_ = tmp;
882
883 DLOG("Setting BlackVarianceSurface extrapolation to " << to_string(vssc.extrapolation()));
884 volatility_->enableExtrapolation(vssc.extrapolation());
885
886 LOG("CommodityVolCurve: finished building 2-D volatility absolute strike "
887 << "surface with explicit strikes and expiries");
888}
+ Here is the call graph for this function:

◆ buildVolatility() [4/7]

void buildVolatility ( const QuantLib::Date &  asof,
CommodityVolatilityConfig vc,
const VolatilityDeltaSurfaceConfig vdsc,
const Loader loader 
)
private

Build a volatility surface from a collection of expiry and strike pairs where the strikes are defined in terms of option delta and ATM values.

◆ buildVolatility() [5/7]

void buildVolatility ( const QuantLib::Date &  asof,
CommodityVolatilityConfig vc,
const VolatilityMoneynessSurfaceConfig vmsc,
const Loader loader 
)
private

Build a volatility surface from a collection of expiry and strike pairs where the strikes are defined in terms of moneyness levels.

◆ buildVolatility() [6/7]

void buildVolatility ( const QuantLib::Date &  asof,
CommodityVolatilityConfig vc,
const VolatilityApoFutureSurfaceConfig vapo,
const QuantLib::Handle< QuantLib::BlackVolTermStructure > &  baseVts,
const QuantLib::Handle< QuantExt::PriceTermStructure > &  basePts 
)
private

Build a future price APO surface using a given price term structure. Use the natural expiries of the future options as the expiry pillar points and use configured moneyness levels in the strike dimension.

◆ buildVolatility() [7/7]

void buildVolatility ( const QuantLib::Date &  asof,
const CommodityVolatilityCurveSpec spec,
const CurveConfigurations curveConfigs,
const ProxyVolatilityConfig pvc,
const map< string, QuantLib::ext::shared_ptr< CommodityCurve > > &  comCurves,
const map< string, QuantLib::ext::shared_ptr< CommodityVolCurve > > &  volCurves,
const map< string, QuantLib::ext::shared_ptr< FXVolCurve > > &  fxVolCurves,
const map< string, QuantLib::ext::shared_ptr< CorrelationCurve > > &  correlationCurves,
const Market fxIndices = nullptr 
)
private

Build a volatility surface as a proxy from another volatility surface.

Definition at line 1442 of file commodityvolcurve.cpp.

1447 {
1448
1449 DLOG("Build Proxy Vol surface");
1450 // get all the configurations and the curve needed for proxying
1451 auto config = *curveConfigs.commodityVolatilityConfig(spec.curveConfigID());
1452
1453 auto proxy = pvc.proxyVolatilityCurve();
1454 auto comConfig = *curveConfigs.commodityCurveConfig(spec.curveConfigID());
1455 auto proxyConfig = *curveConfigs.commodityCurveConfig(proxy);
1456 auto proxyVolConfig = *curveConfigs.commodityVolatilityConfig(proxy);
1457
1458 // create dummy specs to look up the required curves
1459 CommodityCurveSpec comSpec(comConfig.currency(), spec.curveConfigID());
1460 CommodityCurveSpec proxySpec(proxyConfig.currency(), proxy);
1461 CommodityVolatilityCurveSpec proxyVolSpec(proxyVolConfig.currency(), proxy);
1462
1463 // Get all necessary curves
1464 auto curve = comCurves.find(comSpec.name());
1465 QL_REQUIRE(curve != comCurves.end(),
1466 "CommodityVolCurve: Failed to find commodity curve, when building commodity vol curve " << spec.name());
1467 auto proxyCurve = comCurves.find(proxySpec.name());
1468 QL_REQUIRE(proxyCurve != comCurves.end(), "currency: Failed to find commodity curve for proxy "
1469 << proxySpec.name() << ", when building commodity vol curve "
1470 << spec.name());
1471 auto proxyVolCurve = volCurves.find(proxyVolSpec.name());
1472 QL_REQUIRE(proxyVolCurve != volCurves.end(), "CommodityVolCurve: Failed to find commodity vol curve for proxy "
1473 << proxyVolSpec.name() << ", when building currency vol curve "
1474 << spec.name());
1475
1476 // check the currency against the proxy surface currrency
1477
1478 QuantLib::ext::shared_ptr<BlackVolTermStructure> fxSurface;
1479 QuantLib::ext::shared_ptr<FxIndex> fxIndex;
1480 QuantLib::ext::shared_ptr<QuantExt::CorrelationTermStructure> correlation;
1481 if (config.currency() != proxyVolConfig.currency() && fxIndices != nullptr) {
1482 QL_REQUIRE(!pvc.fxVolatilityCurve().empty(),
1483 "CommodityVolCurve: FXVolatilityCurve must be provided for commodity vol config "
1484 << spec.curveConfigID() << " as proxy currencies if different from commodity currency.");
1485 QL_REQUIRE(!pvc.correlationCurve().empty(),
1486 "CommodityVolCurve: CorrelationCurve must be provided for commodity vol config "
1487 << spec.curveConfigID() << " as proxy currencies if different from commodity currency.");
1488
1489 // get the fx vol surface
1490 QL_REQUIRE(pvc.fxVolatilityCurve().size() == 6, "CommodityVolCurve: FXVolatilityCurve provided "
1491 << pvc.fxVolatilityCurve() << " for commodity vol config "
1492 << spec.curveConfigID()
1493 << " must be of length 6, and of form CC1CCY2 e.g EURUSD");
1494 string proxyVolForCcy = pvc.fxVolatilityCurve().substr(0, 3);
1495 string proxyVolDomCcy = pvc.fxVolatilityCurve().substr(3, 3);
1496 FXVolatilityCurveSpec fxSpec(proxyVolForCcy, proxyVolDomCcy, pvc.fxVolatilityCurve());
1497 auto volIt = fxVolCurves.find(fxSpec.name());
1498 if (volIt == fxVolCurves.end())
1499 QL_FAIL("CommodityVolCurve: cannot find required Fx volatility surface "
1500 << fxSpec.name() << " to build proxy vol surface for " << comSpec.name());
1501 fxSurface = volIt->second->volTermStructure();
1502
1503 // check if the fx vol surface needs to be inverted
1504 if (proxyVolForCcy != proxyVolConfig.currency()) {
1505 Handle<BlackVolTermStructure> hFx(fxSurface);
1506 fxSurface = QuantLib::ext::make_shared<QuantExt::BlackInvertedVolTermStructure>(hFx);
1507 fxSurface->enableExtrapolation();
1508 }
1509
1510 fxIndex = fxIndices->fxIndex(proxyVolConfig.currency() + config.currency()).currentLink();
1511 FXSpotSpec spotSpec(proxyVolConfig.currency(), config.currency());
1512
1513 CorrelationCurveSpec corrSpec(pvc.correlationCurve());
1514 auto corrIt = requiredCorrelationCurves.find(corrSpec.name());
1515 if (corrIt == requiredCorrelationCurves.end())
1516 QL_FAIL("CommodityVolCurve: cannot find required correlation curve "
1517 << pvc.correlationCurve() << " to build proxy vol surface for " << comSpec.name());
1518 correlation = corrIt->second->corrTermStructure();
1519 }
1520
1521 volatility_ = QuantLib::ext::make_shared<BlackVolatilitySurfaceProxy>(
1522 proxyVolCurve->second->volatility(), curve->second->commodityIndex(), proxyCurve->second->commodityIndex(),
1523 fxSurface, fxIndex, correlation);
1524}
const CommodityVolatilityCurveSpec & spec() const
const std::string & curveConfigID() const
Definition: curvespec.hpp:83
string name() const
returns the unique curve name
Definition: curvespec.hpp:78
+ Here is the call graph for this function:

◆ correctFuturePriceCurve()

Handle< PriceTermStructure > correctFuturePriceCurve ( const QuantLib::Date &  asof,
const std::string &  contractName,
const QuantLib::ext::shared_ptr< QuantExt::PriceTermStructure > &  pts,
const std::vector< QuantLib::Date > &  optionExpiries 
) const
private

Assume that the input price curve pts is a future price curve giving the price of a sequence of future contracts at the contract expiry. Create a copy of this input curve with additional pillar points at the future option contract expiries. The price returned when this curve is queried at the option contract expiry or the future contract expiry is the expected future contract price i.e. the quoted future price. We need this to allow option surface creation with our current infrastructure for options on futures in particular where the surface creation relies on knowing the "forward" price.

Definition at line 1526 of file commodityvolcurve.cpp.

1528 {
1529
1530 LOG("CommodityVolCurve: start adding future price correction at option expiry.");
1531
1532 // Gather curve dates and prices
1533 map<Date, Real> curveData;
1534
1535 // Get existing curve dates and prices. Messy but can't think of another way.
1536 vector<Date> ptsDates;
1537 if (auto ipc = QuantLib::ext::dynamic_pointer_cast<InterpolatedPriceCurve<Linear>>(pts)) {
1538 ptsDates = ipc->pillarDates();
1539 } else if (auto ipc = QuantLib::ext::dynamic_pointer_cast<InterpolatedPriceCurve<LogLinear>>(pts)) {
1540 ptsDates = ipc->pillarDates();
1541 } else if (auto ipc = QuantLib::ext::dynamic_pointer_cast<InterpolatedPriceCurve<Cubic>>(pts)) {
1542 ptsDates = ipc->pillarDates();
1543 } else if (auto ipc = QuantLib::ext::dynamic_pointer_cast<InterpolatedPriceCurve<LinearFlat>>(pts)) {
1544 ptsDates = ipc->pillarDates();
1545 } else if (auto ipc = QuantLib::ext::dynamic_pointer_cast<InterpolatedPriceCurve<LogLinearFlat>>(pts)) {
1546 ptsDates = ipc->pillarDates();
1547 } else if (auto ipc = QuantLib::ext::dynamic_pointer_cast<InterpolatedPriceCurve<CubicFlat>>(pts)) {
1548 ptsDates = ipc->pillarDates();
1549 } else {
1550 DLOG("Could not cast the price term structure so do not have its pillar dates.");
1551 }
1552
1553 // Add existing dates and prices to data.
1554 DLOG("Got " << ptsDates.size() << " pillar dates from the price curve.");
1555 for (const Date& d : ptsDates) {
1556 curveData[d] = pts->price(d);
1557 TLOG("Added (" << io::iso_date(d) << "," << fixed << setprecision(9) << curveData.at(d) << ").");
1558 }
1559
1560 // Add future price at option expiry to the curve i.e. override any interpolation between future option
1561 // expiry (oed) and future expiry (fed).
1562 auto futureExpiryCalculator = expCalc_;
1563 if (convention_ && !convention_->optionUnderlyingFutureConvention().empty()) {
1564 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
1565 auto [found, conv] =
1566 conventions->get(convention_->optionUnderlyingFutureConvention(), Convention::Type::CommodityFuture);
1567 if (found) {
1568 futureExpiryCalculator = QuantLib::ext::make_shared<ConventionsBasedFutureExpiry>(
1569 *QuantLib::ext::dynamic_pointer_cast<CommodityFutureConvention>(conv));
1570 }
1571 }
1572 for (const Date& oed : optionExpiries) {
1573 Date fed = futureExpiryCalculator->nextExpiry(true, oed, 0, false);
1574 // In general, expect that the future expiry date will already be in curveData but maybe not.
1575 auto it = curveData.find(fed);
1576 if (it != curveData.end()) {
1577 curveData[oed] = it->second;
1578 TLOG("Found future expiry in existing data. (Option Expiry,Future Expiry,Future Price) is ("
1579 << io::iso_date(oed) << "," << io::iso_date(fed) << "," << fixed << setprecision(9)
1580 << curveData.at(oed) << ").");
1581 } else {
1582 curveData[oed] = pts->price(fed);
1583 curveData[fed] = pts->price(fed);
1584 TLOG("Future expiry not found in existing data. (Option Expiry,Future Expiry,Future Price) is ("
1585 << io::iso_date(oed) << "," << io::iso_date(fed) << "," << fixed << setprecision(9)
1586 << curveData.at(oed) << ").");
1587 }
1588 }
1589
1590 // Gather data for building the "corrected" curve.
1591 vector<Date> curveDates;
1592 vector<Real> curvePrices;
1593 for (const auto& kv : curveData) {
1594 curveDates.push_back(kv.first);
1595 curvePrices.push_back(kv.second);
1596 }
1597 DayCounter dc = pts->dayCounter();
1598
1599 // Create the "corrected" curve. Again messy but can't think of another way.
1600 QuantLib::ext::shared_ptr<PriceTermStructure> cpts;
1601 if (auto ipc = QuantLib::ext::dynamic_pointer_cast<InterpolatedPriceCurve<Linear>>(pts)) {
1602 cpts = QuantLib::ext::make_shared<InterpolatedPriceCurve<Linear>>(asof, curveDates, curvePrices, dc, pts->currency());
1603 } else if (auto ipc = QuantLib::ext::dynamic_pointer_cast<InterpolatedPriceCurve<LogLinear>>(pts)) {
1604 cpts =
1605 QuantLib::ext::make_shared<InterpolatedPriceCurve<LogLinear>>(asof, curveDates, curvePrices, dc, pts->currency());
1606 } else if (auto ipc = QuantLib::ext::dynamic_pointer_cast<InterpolatedPriceCurve<Cubic>>(pts)) {
1607 cpts = QuantLib::ext::make_shared<InterpolatedPriceCurve<Cubic>>(asof, curveDates, curvePrices, dc, pts->currency());
1608 } else if (auto ipc = QuantLib::ext::dynamic_pointer_cast<InterpolatedPriceCurve<LinearFlat>>(pts)) {
1609 cpts =
1610 QuantLib::ext::make_shared<InterpolatedPriceCurve<LinearFlat>>(asof, curveDates, curvePrices, dc, pts->currency());
1611 } else if (auto ipc = QuantLib::ext::dynamic_pointer_cast<InterpolatedPriceCurve<LogLinearFlat>>(pts)) {
1612 cpts = QuantLib::ext::make_shared<InterpolatedPriceCurve<LogLinearFlat>>(asof, curveDates, curvePrices, dc,
1613 pts->currency());
1614 } else if (auto ipc = QuantLib::ext::dynamic_pointer_cast<InterpolatedPriceCurve<CubicFlat>>(pts)) {
1615 cpts =
1616 QuantLib::ext::make_shared<InterpolatedPriceCurve<CubicFlat>>(asof, curveDates, curvePrices, dc, pts->currency());
1617 } else {
1618 DLOG("Could not cast the price term structure so corrected curve is a linear InterpolatedPriceCurve.");
1619 cpts = QuantLib::ext::make_shared<InterpolatedPriceCurve<Linear>>(asof, curveDates, curvePrices, dc, pts->currency());
1620 }
1621 cpts->enableExtrapolation(pts->allowsExtrapolation());
1622
1623 LOG("CommodityVolCurve: finished adding future price correction at option expiry.");
1624
1625 return Handle<PriceTermStructure>(cpts);
1626}
QuantLib::ext::shared_ptr< QuantExt::FutureExpiryCalculator > expCalc_
QuantLib::ext::shared_ptr< CommodityFutureConvention > convention_

◆ getExpiry()

Date getExpiry ( const QuantLib::Date &  asof,
const QuantLib::ext::shared_ptr< Expiry > &  expiry,
const std::string &  name,
QuantLib::Natural  rollDays 
) const
private

Get an explicit expiry date from a commodity option quote's Expiry.

Definition at line 1628 of file commodityvolcurve.cpp.

1629 {
1630
1631 Date result;
1632
1633 if (auto expiryDate = QuantLib::ext::dynamic_pointer_cast<ExpiryDate>(expiry)) {
1634 result = expiryDate->expiryDate();
1635 } else if (auto expiryPeriod = QuantLib::ext::dynamic_pointer_cast<ExpiryPeriod>(expiry)) {
1636 // We may need more conventions here eventually.
1637 result = calendar_.adjust(asof + expiryPeriod->expiryPeriod());
1638 } else if (auto fcExpiry = QuantLib::ext::dynamic_pointer_cast<FutureContinuationExpiry>(expiry)) {
1639
1640 QL_REQUIRE(expCalc_, "CommodityVolCurve::getExpiry: need a future expiry calculator for continuation quotes.");
1641 QL_REQUIRE(convention_, "CommodityVolCurve::getExpiry: need a future convention for continuation quotes.");
1642 DLOG("Future option continuation expiry is " << *fcExpiry);
1643
1644 // Firstly, get the next option expiry on or after the asof date
1645 result = expCalc_->nextExpiry(true, asof, 0, true);
1646 TLOG("CommodityVolCurve::getExpiry: next option expiry relative to " << io::iso_date(asof) << " is "
1647 << io::iso_date(result) << ".");
1648
1649 // Market quotes may be delivered with a given number of roll days.
1650 if (rollDays > 0) {
1651 Date roll;
1652 roll = calendar_.advance(result, -static_cast<Integer>(rollDays), Days);
1653 TLOG("CommodityVolCurve::getExpiry: roll days is " << rollDays << " giving a roll date "
1654 << io::iso_date(roll) << ".");
1655 // Take the next option expiry if the roll days means the roll date is before asof.
1656 if (roll < asof) {
1657 result = expCalc_->nextExpiry(true, asof, 1, true);
1658 roll = calendar_.advance(result, -static_cast<Integer>(rollDays), Days);
1659 QL_REQUIRE(roll > asof, "CommodityVolCurve::getExpiry: expected roll to be greater than asof.");
1660 TLOG("CommodityVolCurve::getExpiry: roll date " << io::iso_date(roll) << " is less than asof "
1661 << io::iso_date(asof) << " so take next option expiry "
1662 << io::iso_date(result));
1663 }
1664 }
1665
1666 // At this stage, 'result' should hold the next option expiry on or after the asof date accounting for roll
1667 // days.
1668 TLOG("CommodityVolCurve::getExpiry: first option expiry is " << io::iso_date(result) << ".");
1669
1670 // If the continuation index is greater than 1 get the corresponding expiry.
1671 Natural fcIndex = fcExpiry->expiryIndex();
1672
1673 // The option continuation expiry may be mapped to another one.
1674 const auto& ocm = convention_->optionContinuationMappings();
1675 auto it = ocm.find(fcIndex);
1676 if (it != ocm.end())
1677 fcIndex = it->second;
1678
1679 if (fcIndex > 1) {
1680 result += 1 * Days;
1681 result = expCalc_->nextExpiry(true, result, fcIndex - 2, true);
1682 }
1683
1684 DLOG("Expiry date corresponding to continuation expiry, " << *fcExpiry <<
1685 ", is " << io::iso_date(result) << ".");
1686
1687 } else {
1688 QL_FAIL("CommodityVolCurve::getExpiry: cannot determine expiry type.");
1689 }
1690
1691 return result;
1692}
+ Here is the caller graph for this function:

◆ populateCurves()

void populateCurves ( const CommodityVolatilityConfig config,
const std::map< std::string, QuantLib::ext::shared_ptr< YieldCurve > > &  yieldCurves,
const std::map< std::string, QuantLib::ext::shared_ptr< CommodityCurve > > &  commodityCurves,
bool  searchYield,
bool  dontThrow = false 
)
private

Populate price curve, pts_, and yield curve, yts_.

Definition at line 1694 of file commodityvolcurve.cpp.

1697 {
1698
1699 if (searchYield) {
1700 const string& ytsId = config.yieldCurveId();
1701 if (!ytsId.empty()) {
1702 auto itYts = yieldCurves.find(ytsId);
1703 if (itYts != yieldCurves.end()) {
1704 yts_ = itYts->second->handle();
1705 } else if (!dontThrow) {
1706 QL_FAIL("CommodityVolCurve: can't find yield curve with id " << ytsId);
1707 }
1708 } else if (!dontThrow) {
1709 QL_FAIL("CommodityVolCurve: YieldCurveId was not populated for " << config.curveID());
1710 }
1711 }
1712
1713 const string& ptsId = config.priceCurveId();
1714 if (!ptsId.empty()) {
1715 auto itPts = commodityCurves.find(ptsId);
1716 if (itPts != commodityCurves.end()) {
1717 pts_ = Handle<PriceTermStructure>(itPts->second->commodityPriceCurve());
1718 } else if (!dontThrow) {
1719 QL_FAIL("CommodityVolCurve: can't find price curve with id " << ptsId);
1720 }
1721 } else if (!dontThrow) {
1722 QL_FAIL("CommodityVolCurve: PriceCurveId was not populated for " << config.curveID());
1723 }
1724}
+ Here is the call graph for this function:

◆ checkMoneyness()

vector< Real > checkMoneyness ( const std::vector< std::string > &  moneynessLevels) const
private

Check and return moneyness levels.

Definition at line 1726 of file commodityvolcurve.cpp.

1726 {
1727
1728 using boost::adaptors::transformed;
1729 using boost::algorithm::join;
1730
1731 vector<Real> moneynessLevels = parseVectorOfValues<Real>(strMoneynessLevels, &parseReal);
1732 sort(moneynessLevels.begin(), moneynessLevels.end(), [](Real x, Real y) { return !close(x, y) && x < y; });
1733 QL_REQUIRE(adjacent_find(moneynessLevels.begin(), moneynessLevels.end(),
1734 [](Real x, Real y) { return close(x, y); }) == moneynessLevels.end(),
1735 "The configured moneyness levels contain duplicates");
1736 DLOG("Parsed " << moneynessLevels.size() << " unique configured moneyness levels.");
1737 DLOG("The moneyness levels are: " << join(
1738 moneynessLevels | transformed([](Real d) { return ore::data::to_string(d); }), ","));
1739
1740 return moneynessLevels;
1741}
+ Here is the call graph for this function:

Member Data Documentation

◆ spec_

Definition at line 72 of file commodityvolcurve.hpp.

◆ volatility_

QuantLib::ext::shared_ptr<QuantLib::BlackVolTermStructure> volatility_
private

Definition at line 73 of file commodityvolcurve.hpp.

◆ expCalc_

QuantLib::ext::shared_ptr<QuantExt::FutureExpiryCalculator> expCalc_
private

Definition at line 74 of file commodityvolcurve.hpp.

◆ convention_

QuantLib::ext::shared_ptr<CommodityFutureConvention> convention_
private

Definition at line 75 of file commodityvolcurve.hpp.

◆ calendar_

QuantLib::Calendar calendar_
private

Definition at line 76 of file commodityvolcurve.hpp.

◆ dayCounter_

QuantLib::DayCounter dayCounter_
private

Definition at line 77 of file commodityvolcurve.hpp.

◆ calibrationInfo_

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

Definition at line 78 of file commodityvolcurve.hpp.

◆ maxExpiry_

QuantLib::Date maxExpiry_
private

Definition at line 79 of file commodityvolcurve.hpp.

◆ pts_

QuantLib::Handle<QuantExt::PriceTermStructure> pts_
private

Definition at line 82 of file commodityvolcurve.hpp.

◆ yts_

QuantLib::Handle<QuantLib::YieldTermStructure> yts_
private

Definition at line 83 of file commodityvolcurve.hpp.