21#include <boost/algorithm/string/join.hpp>
22#include <boost/algorithm/string/replace.hpp>
23#include <boost/range/adaptor/indexed.hpp>
24#include <boost/range/adaptor/transformed.hpp>
32#include <ql/math/interpolations/bicubicsplineinterpolation.hpp>
33#include <ql/math/interpolations/loginterpolation.hpp>
34#include <ql/math/matrix.hpp>
35#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
36#include <ql/termstructures/volatility/equityfx/blackvariancecurve.hpp>
37#include <ql/termstructures/volatility/equityfx/blackvariancesurface.hpp>
38#include <ql/time/calendars/weekendsonly.hpp>
50#include <ql/pricingengines/blackformula.hpp>
61 ExpiryStrike(
const Date& exp = Date(), Real stk = Null<Real>()) : expiry(
exp), strike(stk) {}
66ostream&
operator<<(ostream& os,
const ExpiryStrike& es) {
67 return os <<
"(" << io::iso_date(es.expiry) <<
"," << es.strike <<
")";
71struct ExpiryStrikeComp {
72 bool operator() (
const ExpiryStrike& lhs,
const ExpiryStrike& rhs)
const {
73 return (lhs.expiry < rhs.expiry) || (!(rhs.expiry < lhs.expiry) &&
74 !close(lhs.strike, rhs.strike) && (lhs.strike < rhs.strike));
81 CallPutDatum() : call(Null<Real>()), put(Null<Real>()) {}
83 CallPutDatum(Option::Type optionType, Real
value)
84 : call(Null<Real>()), put(Null<Real>()) {
85 if (optionType == Option::Call) {
99 void addDatum(ExpiryStrike node, Option::Type optionType, Real
value) {
101 auto it =
data.find(node);
102 if (it !=
data.end()) {
104 CallPutDatum& cpd = it->second;
105 Real& toUpdate = optionType == Option::Call ? cpd.call : cpd.put;
106 if (toUpdate == Null<Real>()) {
108 TLOG(
"Updated " << optionType <<
" option data point with value " << fixed <<
109 setprecision(9) <<
value <<
" for expiry strike pair " << node <<
".");
111 TLOG(
"Expiry strike pair " << node <<
" already has " << optionType <<
" option data " <<
112 "point with value " << fixed << setprecision(9) << toUpdate <<
113 " so did not update with value " <<
value <<
".");
117 data[node] = CallPutDatum(optionType,
value);
118 TLOG(
"Added " << optionType <<
" option data point with value " << fixed << setprecision(9) <<
119 value <<
" for expiry strike pair " << node <<
".");
123 map<ExpiryStrike, CallPutDatum, ExpiryStrikeComp>
data;
127QuantLib::ext::shared_ptr<OptionPriceSurface> optPriceSurface(
const CallPutData& cpData,
128 const Date& asof,
const DayCounter& dc,
bool forCall) {
130 DLOG(
"Creating " << (forCall ?
"Call" :
"Put") <<
" option price surface.");
132 const auto& priceData = cpData.data;
133 auto n = priceData.size();
135 vector<Date> expiries; expiries.reserve(n);
137 vector<Real> prices; prices.reserve(n);
139 for (
const auto& kv : cpData.data) {
141 if ((forCall && kv.second.call != Null<Real>()) || (!forCall && kv.second.put != Null<Real>())) {
143 expiries.push_back(kv.first.expiry);
144 strikes.push_back(kv.first.strike);
145 prices.push_back(forCall ? kv.second.call : kv.second.put);
147 TLOG(
"Using option datum (" << (forCall ?
"Call" :
"Put") <<
"," << io::iso_date(expiries.back()) <<
148 "," << fixed << setprecision(9) <<
strikes.back() <<
"," << prices.back() <<
")");
153 QL_REQUIRE(!prices.empty(),
"Need at least one point for " << (forCall ?
"Call" :
"Put") <<
154 " commodity option price surface.");
156 return QuantLib::ext::make_shared<OptionPriceSurface>(asof, expiries, strikes, prices, dc);
166 const map<
string, QuantLib::ext::shared_ptr<YieldCurve>>& yieldCurves,
167 const map<
string, QuantLib::ext::shared_ptr<CommodityCurve>>& commodityCurves,
168 const map<
string, QuantLib::ext::shared_ptr<CommodityVolCurve>>& commodityVolCurves,
169 const map<
string, QuantLib::ext::shared_ptr<FXVolCurve>>& fxVolCurves,
170 const map<
string, QuantLib::ext::shared_ptr<CorrelationCurve>>& correlationCurves,
171 const Market* fxIndices,
const bool buildCalibrationInfo) {
174 LOG(
"CommodityVolCurve: start building commodity volatility structure with ID " <<
spec.
curveConfigID());
178 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
180 if (!config.futureConventionsId().empty()) {
181 const auto& cId = config.futureConventionsId();
182 QL_REQUIRE(conventions->has(cId),
183 "Conventions, " << cId <<
" for config " << config.curveID() <<
" not found.");
184 convention_ = QuantLib::ext::dynamic_pointer_cast<CommodityFutureConvention>(conventions->get(cId));
185 QL_REQUIRE(
convention_,
"Convention with ID '" << cId <<
"' should be of type CommodityFutureConvention");
193 DLOG(
"CommodityVolCurve: Attempting to build commodity vol curve from volatilityConfig, "
194 << config.volatilityConfig().size() <<
" volatility configs provided.");
195 for (
auto vc : config.volatilityConfig()) {
198 if (!vc->calendar().empty())
201 if (
auto eqvc = QuantLib::ext::dynamic_pointer_cast<ProxyVolatilityConfig>(vc)) {
203 correlationCurves, fxIndices);
204 }
else if (
auto qvc = QuantLib::ext::dynamic_pointer_cast<QuoteBasedVolatilityConfig>(vc)) {
206 if (
auto cvc = QuantLib::ext::dynamic_pointer_cast<ConstantVolatilityConfig>(vc)) {
208 }
else if (
auto vcc = QuantLib::ext::dynamic_pointer_cast<VolatilityCurveConfig>(vc)) {
210 }
else if (
auto vssc = QuantLib::ext::dynamic_pointer_cast<VolatilityStrikeSurfaceConfig>(vc)) {
214 }
else if (
auto vdsc = QuantLib::ext::dynamic_pointer_cast<VolatilityDeltaSurfaceConfig>(vc)) {
218 }
else if (
auto vmsc = QuantLib::ext::dynamic_pointer_cast<VolatilityMoneynessSurfaceConfig>(vc)) {
222 populateCurves(config, yieldCurves, commodityCurves, fwdMoneyness);
224 }
else if (
auto vapo = QuantLib::ext::dynamic_pointer_cast<VolatilityApoFutureSurfaceConfig>(vc)) {
227 QL_REQUIRE(!vapo->baseConventionsId().empty(),
228 "The APO FutureConventions must be populated to build a future APO surface");
229 QL_REQUIRE(conventions->has(vapo->baseConventionsId()),
230 "Conventions, " << vapo->baseConventionsId() <<
" for config " << config.curveID()
232 auto convention = QuantLib::ext::dynamic_pointer_cast<CommodityFutureConvention>(
233 conventions->get(vapo->baseConventionsId()));
234 QL_REQUIRE(convention,
"Convention with ID '"
235 << config.futureConventionsId()
236 <<
"' should be of type CommodityFutureConvention");
237 auto baseExpCalc = QuantLib::ext::make_shared<ConventionsBasedFutureExpiry>(*convention);
240 QL_REQUIRE(!vapo->baseVolatilityId().empty(),
241 "The APO VolatilityId must be populated to build a future APO surface.");
242 auto itVs = commodityVolCurves.find(vapo->baseVolatilityId());
243 QL_REQUIRE(itVs != commodityVolCurves.end(),
244 "Can't find commodity volatility with id " << vapo->baseVolatilityId());
245 auto baseVs = Handle<BlackVolTermStructure>(itVs->second->volatility());
248 QL_REQUIRE(!vapo->basePriceCurveId().empty(),
249 "The APO PriceCurveId must be populated to build a future APO surface.");
250 auto itPts = commodityCurves.find(vapo->basePriceCurveId());
251 QL_REQUIRE(itPts != commodityCurves.end(),
252 "Can't find price curve with id " << vapo->basePriceCurveId());
253 auto basePts = Handle<PriceTermStructure>(itPts->second->commodityPriceCurve());
261 QL_FAIL(
"Unexpected VolatilityConfig in CommodityVolatilityConfig");
264 QL_FAIL(
"CommodityVolCurve: VolatilityConfig must be QuoteBased or a Proxy");
266 if (buildCalibrationInfo)
268 }
catch (std::exception& e) {
269 DLOG(
"CommodityVolCurve: commodity vol curve building failed :" << e.what());
271 DLOG(
"CommodityVolCurve: commodity vol curve building failed: unknown error");
276 QL_REQUIRE(
volatility_ ,
"CommodityVolCurve: Failed to build commodity volatility structure from "
277 << config.volatilityConfig().size() <<
" volatility configs provided.");
279 LOG(
"CommodityVolCurve: finished building commodity volatility structure with ID " <<
spec.
curveConfigID());
281 }
catch (exception& e) {
282 QL_FAIL(
"Commodity volatility curve building failed : " << e.what());
284 QL_FAIL(
"Commodity volatility curve building failed: unknown error");
289 const ConstantVolatilityConfig& cvc,
const Loader& loader) {
291 LOG(
"CommodityVolCurve: start building constant volatility structure");
293 const QuantLib::ext::shared_ptr<MarketDatum>& md = loader.get(cvc.quote(), asof);
294 QL_REQUIRE(md->asofDate() == asof,
"MarketDatum asofDate '" << md->asofDate() <<
"' <> asof '" << asof <<
"'");
296 "MarketDatum instrument type '" << md->instrumentType() <<
"' <> 'MarketDatum::InstrumentType::COMMODITY_OPTION'");
297 QuantLib::ext::shared_ptr<CommodityOptionQuote> q = QuantLib::ext::dynamic_pointer_cast<CommodityOptionQuote>(md);
298 QL_REQUIRE(q,
"Internal error: could not downcast MarketDatum '" << md->name() <<
"' to CommodityOptionQuote");
299 QL_REQUIRE(q->name() == cvc.quote(),
300 "CommodityOptionQuote name '" << q->name() <<
"' <> ConstantVolatilityConfig quote '" << cvc.quote() <<
"'");
301 TLOG(
"Found the constant volatility quote " << q->name());
302 Real quoteValue = q->quote()->value();
304 DLOG(
"Creating BlackConstantVol structure");
307 LOG(
"CommodityVolCurve: finished building constant volatility structure");
313 LOG(
"CommodityVolCurve: start building 1-D volatility curve");
316 QL_REQUIRE(vcc.
quotes().size() > 0,
"No quotes specified in config " << vc.
curveID());
319 " RATE_LNVOL is currently supported for 1-D commodity volatility curves.");
326 map<Date, Real> curveData;
331 DLOG(
"Have single quote with pattern " << wildcard->pattern());
334 for (
const auto& md : loader.
get(*wildcard, asof)) {
337 if (md->asofDate() != asof)
340 auto q = QuantLib::ext::dynamic_pointer_cast<CommodityOptionQuote>(md);
343 QL_REQUIRE(q->quoteType() == vcc.
quoteType(),
344 "CommodityOptionQuote type '" << q->quoteType() <<
"' <> VolatilityCurveConfig quote type '" << vcc.
quoteType() <<
"'");
346 TLOG(
"The quote " << q->name() <<
" matched the pattern");
349 if (expiryDate > asof) {
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());
357 curveData[expiryDate] = q->quote()->value();
358 TLOG(
"Added quote " << q->name() <<
": (" << io::iso_date(expiryDate) <<
"," <<
359 fixed << setprecision(9) << q->quote()->value() <<
")");
365 QL_REQUIRE(curveData.size() > 0,
"No quotes found matching regular expression " << vcc.
quotes()[0]);
369 DLOG(
"Have " << vcc.
quotes().size() <<
" explicit quotes");
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 <<
"'");
378 QL_REQUIRE(md->asofDate() == asof,
"MarketDatum asofDate '" << md->asofDate() <<
"' <> asof '" << asof <<
"'");
380 auto q = QuantLib::ext::dynamic_pointer_cast<CommodityOptionQuote>(md);
381 QL_REQUIRE(q,
"Internal error: could not downcast MarketDatum '" << md->name() <<
"' to CommodityOptionQuote");
383 TLOG(
"Found the configured quote " << q->name());
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 "
394 curveData[expiryDate] = q->quote()->value();
396 TLOG(
"Added quote " << q->name() <<
": (" << io::iso_date(expiryDate) <<
"," << fixed
397 << setprecision(9) << q->quote()->value() <<
")");
399 skippedExpiredQuotes++;
400 WLOG(
"Skipped quote " << q->name() <<
": (" << io::iso_date(expiryDate) <<
"," << fixed
401 << setprecision(9) << q->quote()->value() <<
")");
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.");
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() <<
")");
423 DLOG(
"Creating BlackVarianceCurve object.");
424 auto tmp = QuantLib::ext::make_shared<BlackVarianceCurve>(asof, dates, volatilities,
dayCounter_);
428 DLOG(
"Interpolation set to Linear.");
430 DLOG(
"Setting interpolation to Cubic.");
431 tmp->setInterpolation<Cubic>();
433 DLOG(
"Setting interpolation to LogLinear.");
434 tmp->setInterpolation<LogLinear>();
436 DLOG(
"Interpolation " << vcc.
interpolation() <<
" not recognised so leaving it Linear.");
444 DLOG(
"Enabling BlackVarianceCurve flat volatility extrapolation.");
447 DLOG(
"Disabling BlackVarianceCurve extrapolation.");
450 DLOG(
"BlackVarianceCurve does not support using interpolator for extrapolation "
451 <<
"so default to flat volatility extrapolation.");
454 DLOG(
"Unexpected extrapolation so default to flat volatility extrapolation.");
458 LOG(
"CommodityVolCurve: finished building 1-D volatility curve");
464 LOG(
"CommodityVolCurve: start building 2-D volatility absolute strike surface");
476 QL_REQUIRE(vssc.
expiries().size() == 1,
"Wild card expiry specified but more expiries also specified.");
477 DLOG(
"Have expiry wildcard pattern " << vssc.
expiries()[0]);
483 QL_REQUIRE(vssc.
strikes().size() == 1,
"Wild card strike specified but more strikes also specified.");
484 DLOG(
"Have strike wildcard pattern " << vssc.
strikes()[0]);
488 vector<Real> configuredStrikes;
491 configuredStrikes = parseVectorOfValues<Real>(vssc.
strikes(), &parseReal);
492 sort(configuredStrikes.begin(), configuredStrikes.end(), [](Real x, Real y) { return !close(x, y) && x < y; });
493 QL_REQUIRE(adjacent_find(configuredStrikes.begin(), configuredStrikes.end(),
494 [](Real x, Real y) { return close(x, y); }) == configuredStrikes.end(),
495 "The configured strikes contain duplicates");
496 DLOG(
"Parsed " << configuredStrikes.size() <<
" unique configured absolute strikes");
500 vector<QuantLib::ext::shared_ptr<Expiry>> configuredExpiries;
503 for (
const string& strExpiry : vssc.
expiries()) {
504 configuredExpiries.push_back(
parseExpiry(strExpiry));
506 DLOG(
"Parsed " << configuredExpiries.size() <<
" unique configured expiries");
510 if (!expWc && !strkWc) {
515 DLOG(
"Expiries and or strikes have been configured via wildcards so building a "
516 <<
"wildcard based absolute strike surface");
523 map<Date, Real> fwdCurve;
526 std::ostringstream ss;
529 Wildcard w(ss.str());
530 for (
const auto& md : loader.
get(w, asof)) {
532 QL_REQUIRE(md->asofDate() == asof,
"MarketDatum asofDate '" << md->asofDate() <<
"' <> asof '" << asof <<
"'");
534 auto q = QuantLib::ext::dynamic_pointer_cast<CommodityOptionQuote>(md);
535 QL_REQUIRE(q,
"Internal error: could not downcast MarketDatum '" << md->name() <<
"' to CommodityOptionQuote");
537 QL_REQUIRE(vc.
curveID() == q->commodityName(),
538 "CommodityVolatilityConfig curve ID '" << vc.
curveID() <<
539 "' <> CommodityOptionQuote commodity name '" << q->commodityName() <<
"'");
540 QL_REQUIRE(vc.
currency() == q->quoteCurrency(),
541 "CommodityVolatilityConfig currency '" << vc.
currency() <<
542 "' <> CommodityOptionQuote currency '" << q->quoteCurrency() <<
"'");
543 QL_REQUIRE(vssc.
quoteType() == q->quoteType(),
544 "VolatilityStrikeSurfaceConfig quote type '" << vssc.
quoteType() <<
545 "' <> CommodityOptionQuote quote type '" << q->quoteType() <<
"'");
548 auto strike = QuantLib::ext::dynamic_pointer_cast<AbsoluteStrike>(q->strike());
555 auto expiryIt = find_if(configuredExpiries.begin(), configuredExpiries.end(),
556 [&q](QuantLib::ext::shared_ptr<Expiry> e) { return *e == *q->expiry(); });
557 if (expiryIt == configuredExpiries.end())
564 auto strikeIt = find_if(configuredStrikes.begin(), configuredStrikes.end(),
565 [&strike](Real s) { return close(s, strike->strike()); });
566 if (strikeIt == configuredStrikes.end())
573 ExpiryStrike node(expiry, strike->strike());
574 cpData.addDatum(ExpiryStrike(expiry, strike->strike()), q->optionType(), q->quote()->value());
578 fwdCurve.count(expiry) == 0) {
579 fwdCurve[expiry] =
pts_->price(expiry);
582 TLOG(
"Added quote " << q->name() <<
" to intermediate data.");
584 TLOG(
"Skipped quote " << q->name() <<
" to intermediate data, already expired at " << QuantLib::io::iso_date(expiry));
589 bool flatStrikeExtrap =
true;
590 bool flatTimeExtrap =
true;
595 DLOG(
"Strike extrapolation switched to using interpolator.");
596 flatStrikeExtrap =
false;
598 DLOG(
"Strike extrapolation cannot be turned off on its own so defaulting to flat.");
600 DLOG(
"Strike extrapolation has been set to flat.");
602 DLOG(
"Strike extrapolation " << strikeExtrapType <<
" not expected so default to flat.");
607 DLOG(
"Time extrapolation switched to using interpolator.");
608 flatTimeExtrap =
false;
610 DLOG(
"Time extrapolation cannot be turned off on its own so defaulting to flat.");
612 DLOG(
"Time extrapolation has been set to flat.");
614 DLOG(
"Time extrapolation " << timeExtrapType <<
" not expected so default to flat.");
618 DLOG(
"Extrapolation is turned off for the whole surface so the time and"
619 <<
" strike extrapolation settings are ignored");
629 DLOG(
"Creating the BlackVarianceSurfaceSparse object");
635 vector<Date> expiries;
636 vector<Volatility> vols;
637 Size quotesAdded = 0;
639 for (
const auto& kv : cpData.data) {
641 const Date& expiry = kv.first.expiry;
642 const Real& stk = kv.first.strike;
643 const CallPutDatum& cpd = kv.second;
644 QL_REQUIRE(cpd.call != Null<Real>() || cpd.put != Null<Real>(),
645 "CommodityVolCurve: expected the call or put value to be populated.");
647 expiries.push_back(expiry);
649 bool useCall = cpd.call != Null<Real>();
651 if ((cpd.call != Null<Real>() && cpd.put != Null<Real>()) && !fwdCurve.empty()) {
653 auto it = fwdCurve.find(expiry);
654 QL_REQUIRE(it != fwdCurve.end(),
"CommodityVolCurve: expected forwards for all expiries.");
655 const Real& fwd = it->second;
656 useCall = (preferOutOfTheMoney && stk >= fwd) || (!preferOutOfTheMoney && stk <= fwd);
657 TLOG(
"(Forward,Strike,UseCall) = (" << fixed << setprecision(6) <<
658 fwd <<
"," << stk <<
"," <<
to_string(useCall) <<
").");
661 vols.push_back(useCall ? cpd.call : cpd.put);
664 TLOG(
"Using option datum (" << (useCall ?
"Call" :
"Put") <<
"," << io::iso_date(expiries.back()) <<
665 "," << fixed << setprecision(9) <<
strikes.back() <<
"," << vols.back() <<
")");
668 LOG(
"CommodityVolCurve: added " << quotesAdded <<
" quotes building wildcard based absolute strike surface.");
669 QL_REQUIRE(quotesAdded > 0,
"No quotes loaded for " << vc.
curveID());
671 volatility_ = QuantLib::ext::make_shared<BlackVarianceSurfaceSparse>(asof,
calendar_, expiries, strikes,
672 vols,
dayCounter_, flatStrikeExtrap, flatStrikeExtrap, flatTimeExtrap);
676 QL_REQUIRE(!
pts_.empty(),
"CommodityVolCurve: require non-empty price term structure" <<
677 " to strip volatilities from prices.");
678 QL_REQUIRE(!
yts_.empty(),
"CommodityVolCurve: require non-empty yield term structure" <<
679 " to strip volatilities from prices.");
685 auto cSurface = optPriceSurface(cpData, asof,
dayCounter_,
true);
686 auto pSurface = optPriceSurface(cpData, asof,
dayCounter_,
false);
688 DLOG(
"CommodityVolCurve: stripping volatility surface from the option premium surfaces.");
689 auto coss = QuantLib::ext::make_shared<CommodityOptionSurfaceStripper>(
pts_,
yts_, cSurface, pSurface,
calendar_,
691 preferOutOfTheMoney, solverOptions);
697 TLOG(
"CommodityVolCurve: stripped volatilities:");
698 TLOG(
"expiry,strike,forward_price,call_price,put_price,discount,volatility");
699 for (
const auto& kv : cpData.data) {
700 const Date& expiry = kv.first.expiry;
701 const Real& strike = kv.first.strike;
702 Real fwd =
pts_->price(expiry);
704 if (kv.second.call != Null<Real>()) {
706 ss << fixed << setprecision(6) << kv.second.call;
710 if (kv.second.put != Null<Real>()) {
712 ss << fixed << setprecision(6) << kv.second.put;
715 Real discount =
yts_->discount(expiry);
717 TLOG(io::iso_date(expiry) <<
"," << fixed << setprecision(6) << strike <<
"," <<
718 fwd <<
"," << cPrice <<
"," << pPrice <<
"," << discount <<
"," << vol);
723 QL_FAIL(
"CommodityVolCurve: invalid quote type " << vssc.
quoteType() <<
" provided.");
729 LOG(
"CommodityVolCurve: finished building 2-D volatility absolute strike surface");
734 const vector<Real>& configuredStrikes) {
736 LOG(
"CommodityVolCurve: start building 2-D volatility absolute strike surface with explicit strikes and expiries");
739 " RATE_LNVOL is currently supported for 2-D volatility strike surface with explicit strikes and expiries.");
743 map<Date, vector<Real>> surfaceData;
746 Size quotesAdded = 0;
747 Size skippedExpiredQuotes = 0;
749 std::ostringstream ss;
753 for (
const auto& md : loader.
get(w, asof)) {
755 QL_REQUIRE(md->asofDate() == asof,
"MarketDatum asofDate '" << md->asofDate() <<
"' <> asof '" << asof <<
"'");
757 auto q = QuantLib::ext::dynamic_pointer_cast<CommodityOptionQuote>(md);
758 QL_REQUIRE(q,
"Internal error: could not downcast MarketDatum '" << md->name() <<
"' to CommodityOptionQuote");
761 auto strike = QuantLib::ext::dynamic_pointer_cast<AbsoluteStrike>(q->strike());
766 auto it = find(vc.
quotes().begin(), vc.
quotes().end(), q->name());
767 if (it == vc.
quotes().end())
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(),
782 <<
"' is in the list of configured quotes but does not match any of the configured strikes");
785 if (surfaceData.count(eDate) == 0)
786 surfaceData[eDate] = vector<Real>(configuredStrikes.size(), Null<Real>());
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();
794 TLOG(
"Added quote " << q->name() <<
": (" << io::iso_date(eDate) <<
"," << fixed << setprecision(9)
795 << configuredStrikes[pos] <<
"," << q->quote()->value() <<
")");
797 skippedExpiredQuotes++;
798 TLOG(
"Skipped quote " << q->name() <<
": (" << io::iso_date(eDate) <<
"," << fixed << setprecision(9)
799 << q->quote()->value() <<
")");
803 LOG(
"CommodityVolCurve: added " << quotesAdded <<
" quotes in building explicit absolute strike surface.");
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.");
811 vector<Date> expiryDates;
812 Matrix volatilities = Matrix(configuredStrikes.size(), surfaceData.size());
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];
823 if (!expiryDates.empty())
824 maxExpiry_ = *max_element(expiryDates.begin(), expiryDates.end());
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]);
838 BlackVarianceSurface::Extrapolation strikeExtrap = BlackVarianceSurface::ConstantExtrapolation;
843 DLOG(
"Strike extrapolation switched to using interpolator.");
844 strikeExtrap = BlackVarianceSurface::InterpolatorDefaultExtrapolation;
846 DLOG(
"Strike extrapolation cannot be turned off on its own so defaulting to flat.");
848 DLOG(
"Strike extrapolation has been set to flat.");
850 DLOG(
"Strike extrapolation " << strikeExtrapType <<
" not expected so default to flat.");
855 DLOG(
"BlackVarianceSurface only supports flat volatility extrapolation in the time direction");
858 DLOG(
"Extrapolation is turned off for the whole surface so the time and"
859 <<
" strike extrapolation settings are ignored");
862 DLOG(
"Creating BlackVarianceSurface object");
863 auto tmp = QuantLib::ext::make_shared<BlackVarianceSurface>(asof,
calendar_, expiryDates, configuredStrikes, volatilities,
869 DLOG(
"Time and strike interpolation must be the same for BlackVarianceSurface but we got strike "
873 DLOG(
"Setting interpolation to BiCubic");
874 tmp->setInterpolation<Bicubic>();
886 LOG(
"CommodityVolCurve: finished building 2-D volatility absolute strike "
887 <<
"surface with explicit strikes and expiries");
893 using boost::adaptors::transformed;
894 using boost::algorithm::join;
896 LOG(
"CommodityVolCurve: start building 2-D volatility delta strike surface");
899 " RATE_LNVOL is currently supported for a 2-D volatility delta strike surface.");
902 vector<Real> putDeltas = parseVectorOfValues<Real>(vdsc.
putDeltas(), &parseReal);
903 sort(putDeltas.begin(), putDeltas.end(), [](Real x, Real y) { return !close(x, y) && x < y; });
904 QL_REQUIRE(adjacent_find(putDeltas.begin(), putDeltas.end(), [](Real x, Real y) { return close(x, y); }) ==
906 "The configured put deltas contain duplicates");
907 DLOG(
"Parsed " << putDeltas.size() <<
" unique configured put deltas");
911 vector<Real> callDeltas = parseVectorOfValues<Real>(vdsc.
callDeltas(), &parseReal);
912 sort(callDeltas.begin(), callDeltas.end(), [](Real x, Real y) { return !close(x, y) && x > y; });
913 QL_REQUIRE(adjacent_find(callDeltas.begin(), callDeltas.end(), [](Real x, Real y) { return close(x, y); }) ==
915 "The configured call deltas contain duplicates");
916 DLOG(
"Parsed " << callDeltas.size() <<
" unique configured call deltas");
923 QL_REQUIRE(vdsc.
expiries().size() == 1,
"Wild card expiry specified but more expiries also specified.");
924 DLOG(
"Have expiry wildcard pattern " << vdsc.
expiries()[0]);
929 map<Date, vector<Real>> surfaceData;
932 Size numStrikes = putDeltas.size() + 1 + callDeltas.size();
935 Size quotesAdded = 0;
936 Size skippedExpiredQuotes = 0;
940 boost::optional<DeltaVolQuote::DeltaType> atmDeltaType;
946 vector<QuantLib::ext::shared_ptr<BaseStrike>>
strikes;
947 for (
const auto& pd : putDeltas) {
948 strikes.push_back(QuantLib::ext::make_shared<DeltaStrike>(deltaType, Option::Put, pd));
950 strikes.push_back(QuantLib::ext::make_shared<AtmStrike>(atmType, atmDeltaType));
951 for (
const auto& cd : callDeltas) {
952 strikes.push_back(QuantLib::ext::make_shared<DeltaStrike>(deltaType, Option::Call, cd));
956 std::ostringstream ss;
959 Wildcard w(ss.str());
960 for (
const auto& md : loader.
get(w, asof)) {
962 QL_REQUIRE(md->asofDate() == asof,
"MarketDatum asofDate '" << md->asofDate() <<
"' <> asof '" << asof <<
"'");
964 auto q = QuantLib::ext::dynamic_pointer_cast<CommodityOptionQuote>(md);
965 QL_REQUIRE(q,
"Internal error: could not downcast MarketDatum '" << md->name() <<
"' to CommodityOptionQuote");
967 QL_REQUIRE(vc.
curveID() == q->commodityName(),
968 "CommodityVolatilityConfig curve ID '" << vc.
curveID() <<
969 "' <> CommodityOptionQuote commodity name '" << q->commodityName() <<
"'");
970 QL_REQUIRE(vc.
currency() == q->quoteCurrency(),
971 "CommodityVolatilityConfig currency '" << vc.
currency() <<
972 "' <> CommodityOptionQuote currency '" << q->quoteCurrency() <<
"'");
975 vector<QuantLib::ext::shared_ptr<BaseStrike>>::iterator strikeIt;
981 [&q](QuantLib::ext::shared_ptr<BaseStrike> s) { return *s == *q->strike(); });
987 auto it = find(vc.
quotes().begin(), vc.
quotes().end(), q->name());
988 if (it == vc.
quotes().end())
994 [&q](QuantLib::ext::shared_ptr<BaseStrike> s) { return *s == *q->strike(); });
995 QL_REQUIRE(strikeIt !=
strikes.end(),
998 <<
"' is in the list of configured quotes but does not match any of the configured strikes");
1002 Size pos = std::distance(
strikes.begin(), strikeIt);
1009 if (surfaceData.count(eDate) == 0)
1010 surfaceData[eDate] = vector<Real>(numStrikes, Null<Real>());
1012 QL_REQUIRE(surfaceData[eDate][pos] == Null<Real>(),
1013 "Quote " << q->name() <<
" provides a duplicate quote for the date " << io::iso_date(eDate)
1014 <<
" and strike " << *q->strike());
1015 surfaceData[eDate][pos] = q->quote()->value();
1018 TLOG(
"Added quote " << q->name() <<
": (" << io::iso_date(eDate) <<
"," << *q->strike() <<
"," << fixed
1019 << setprecision(9) <<
"," << q->quote()->value() <<
")");
1021 skippedExpiredQuotes++;
1022 TLOG(
"Skiped quote, already expired: " << q->name() <<
": (" << io::iso_date(eDate) <<
"," << *q->strike() <<
"," << fixed
1023 << setprecision(9) <<
"," << q->quote()->value() <<
")");
1027 LOG(
"CommodityVolCurve: added " << quotesAdded <<
" quotes in building delta strike surface.");
1032 for (
const auto& kv : surfaceData) {
1033 for (Size j = 0; j < numStrikes; j++) {
1034 QL_REQUIRE(kv.second[j] != Null<Real>(),
"Volatility for expiry date "
1035 << io::iso_date(kv.first) <<
" and strike " << *strikes[j]
1036 <<
" not found. Cannot proceed with a sparse matrix.");
1042 QL_REQUIRE(vc.
quotes().size() == quotesAdded + skippedExpiredQuotes,
1043 "Found " << quotesAdded <<
" quotes and "<< skippedExpiredQuotes <<
" expired quotes , but " << vc.
quotes().size() <<
" quotes required by config.");
1047 vector<Date> expiryDates;
1048 Matrix vols(surfaceData.size(), numStrikes);
1049 for (
const auto row : surfaceData | boost::adaptors::indexed(0)) {
1050 expiryDates.push_back(row.value().first);
1051 copy(row.value().second.begin(), row.value().second.end(), vols.row_begin(row.index()));
1054 if (!expiryDates.empty())
1055 maxExpiry_ = *std::max_element(expiryDates.begin(), expiryDates.end());
1059 transform(putDeltas.begin(), putDeltas.end(), putDeltas.begin(), [](Real pd) { return -1.0 * pd; });
1060 DLOG(
"Multiply put deltas by -1.0 before creating BlackVolatilitySurfaceDelta object.");
1065 bool flatExtrapolation =
true;
1070 DLOG(
"Strike extrapolation switched to using interpolator.");
1071 flatExtrapolation =
false;
1073 DLOG(
"Strike extrapolation cannot be turned off on its own so defaulting to flat.");
1075 DLOG(
"Strike extrapolation has been set to flat.");
1077 DLOG(
"Strike extrapolation " << strikeExtrapType <<
" not expected so default to flat.");
1082 DLOG(
"BlackVolatilitySurfaceDelta only supports flat volatility extrapolation in the time direction");
1085 DLOG(
"Extrapolation is turned off for the whole surface so the time and"
1086 <<
" strike extrapolation settings are ignored");
1091 DLOG(
"BlackVolatilitySurfaceDelta only supports linear time interpolation.");
1097 im = InterpolatedSmileSection::InterpolationMethod::Linear;
1099 im = InterpolatedSmileSection::InterpolationMethod::NaturalCubic;
1101 im = InterpolatedSmileSection::InterpolationMethod::FinancialCubic;
1103 im = InterpolatedSmileSection::InterpolationMethod::CubicSpline;
1105 im = InterpolatedSmileSection::InterpolationMethod::Linear;
1107 <<
"' so setting it to linear.");
1111 QL_REQUIRE(!
pts_.empty(),
"Expected the price term structure to be populated for a delta surface.");
1112 Handle<PriceTermStructure> cpts =
pts_;
1117 QL_REQUIRE(!
yts_.empty(),
"Expected the yield term structure to be populated for a delta surface.");
1118 Handle<Quote> spot(QuantLib::ext::make_shared<DerivedPriceQuote>(cpts));
1119 Handle<YieldTermStructure> pyts =
1120 Handle<YieldTermStructure>(QuantLib::ext::make_shared<PriceTermStructureAdapter>(*cpts, *
yts_));
1121 pyts->enableExtrapolation();
1123 DLOG(
"Creating BlackVolatilitySurfaceDelta object");
1125 volatility_ = QuantLib::ext::make_shared<BlackVolatilitySurfaceDelta>(
1126 asof, expiryDates, putDeltas, callDeltas, hasAtm, vols,
dayCounter_,
calendar_, spot,
yts_, pyts, deltaType,
1127 atmType, atmDeltaType, 0 * Days, deltaType, atmType, atmDeltaType, im, flatExtrapolation);
1132 LOG(
"CommodityVolCurve: finished building 2-D volatility delta strike surface");
1136 const VolatilityMoneynessSurfaceConfig& vmsc,
const Loader& loader) {
1138 using boost::adaptors::transformed;
1139 using boost::algorithm::join;
1141 LOG(
"CommodityVolCurve: start building 2-D volatility moneyness strike surface");
1144 " RATE_LNVOL is currently supported for a 2-D volatility moneyness strike surface.");
1147 vector<Real> moneynessLevels =
checkMoneyness(vmsc.moneynessLevels());
1151 if (find(vmsc.expiries().begin(), vmsc.expiries().end(),
"*") != vmsc.expiries().end()) {
1153 QL_REQUIRE(vmsc.expiries().size() == 1,
"Wild card expiry specified but more expiries also specified.");
1154 DLOG(
"Have expiry wildcard pattern " << vmsc.expiries()[0]);
1159 map<Date, vector<Real>> surfaceData;
1162 Size quotesAdded = 0;
1163 Size skippedExpiredQuotes = 0;
1169 vector<QuantLib::ext::shared_ptr<BaseStrike>>
strikes;
1170 for (
const auto& moneynessLevel : moneynessLevels) {
1171 strikes.push_back(QuantLib::ext::make_shared<MoneynessStrike>(moneynessType, moneynessLevel));
1175 std::ostringstream ss;
1177 << vc.currency() <<
"/*";
1178 Wildcard w(ss.str());
1179 for (
const auto& md : loader.get(w, asof)) {
1181 QL_REQUIRE(md->asofDate() == asof,
"MarketDatum asofDate '" << md->asofDate() <<
"' <> asof '" << asof <<
"'");
1183 auto q = QuantLib::ext::dynamic_pointer_cast<CommodityOptionQuote>(md);
1184 QL_REQUIRE(q,
"Internal error: could not downcast MarketDatum '" << md->name() <<
"' to CommodityOptionQuote");
1186 QL_REQUIRE(vc.curveID() == q->commodityName(),
1187 "CommodityVolatilityConfig curve ID '" << vc.curveID() <<
1188 "' <> CommodityOptionQuote commodity name '" << q->commodityName() <<
"'");
1189 QL_REQUIRE(vc.currency() == q->quoteCurrency(),
1190 "CommodityVolatilityConfig currency '" << vc.currency() <<
1191 "' <> CommodityOptionQuote currency '" << q->quoteCurrency() <<
"'");
1194 vector<QuantLib::ext::shared_ptr<BaseStrike>>::iterator strikeIt;
1200 [&q](QuantLib::ext::shared_ptr<BaseStrike> s) { return *s == *q->strike(); });
1201 if (strikeIt ==
strikes.end())
1206 auto it = find(vc.quotes().begin(), vc.quotes().end(), q->name());
1207 if (it == vc.quotes().end())
1213 [&q](QuantLib::ext::shared_ptr<BaseStrike> s) { return *s == *q->strike(); });
1214 QL_REQUIRE(strikeIt !=
strikes.end(),
1217 <<
"' is in the list of configured quotes but does not match any of the configured strikes");
1221 Size pos = std::distance(
strikes.begin(), strikeIt);
1224 Date eDate =
getExpiry(asof, q->expiry(), vc.futureConventionsId(), vc.optionExpiryRollDays());
1227 if (surfaceData.count(eDate) == 0)
1228 surfaceData[eDate] = vector<Real>(moneynessLevels.size(), Null<Real>());
1230 QL_REQUIRE(surfaceData[eDate][pos] == Null<Real>(),
1231 "Quote " << q->name() <<
" provides a duplicate quote for the date " << io::iso_date(eDate)
1232 <<
" and strike " << *q->strike());
1233 surfaceData[eDate][pos] = q->quote()->value();
1236 TLOG(
"Added quote " << q->name() <<
": (" << io::iso_date(eDate) <<
"," << *q->strike() <<
"," << fixed
1237 << setprecision(9) <<
"," << q->quote()->value() <<
")");
1240 skippedExpiredQuotes++;
1241 TLOG(
"Skip quote, already expired: " << q->name() <<
": (" << io::iso_date(eDate) <<
"," << *q->strike() <<
"," << fixed
1242 << setprecision(9) <<
"," << q->quote()->value() <<
")");
1246 LOG(
"CommodityVolCurve: added " << quotesAdded <<
" quotes in building moneyness strike surface.");
1251 for (
const auto& kv : surfaceData) {
1252 for (Size j = 0; j < moneynessLevels.size(); j++) {
1253 QL_REQUIRE(kv.second[j] != Null<Real>(),
"Volatility for expiry date "
1254 << io::iso_date(kv.first) <<
" and strike " << *strikes[j]
1255 <<
" not found. Cannot proceed with a sparse matrix.");
1261 QL_REQUIRE(vc.quotes().size() == quotesAdded + skippedExpiredQuotes,
1262 "Found " << quotesAdded <<
" quotes and " << skippedExpiredQuotes <<
" expired quotes, but " << vc.quotes().size()
1263 <<
" quotes required by config.");
1268 vector<Date> expiryDates(surfaceData.size());
1269 vector<Time> expiryTimes(surfaceData.size());
1270 vector<vector<Handle<Quote>>> vols(moneynessLevels.size());
1271 for (
const auto row : surfaceData | boost::adaptors::indexed(0)) {
1272 expiryDates[row.index()] = row.value().first;
1273 expiryTimes[row.index()] =
dayCounter_.yearFraction(asof, row.value().first);
1274 for (Size i = 0; i < row.value().second.size(); i++) {
1275 vols[i].push_back(Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(row.value().second[i])));
1279 if (!expiryDates.empty())
1280 maxExpiry_ = *std::max_element(expiryDates.begin(), expiryDates.end());
1284 bool flatExtrapolation =
true;
1285 if (vmsc.extrapolation()) {
1289 DLOG(
"Strike extrapolation switched to using interpolator.");
1290 flatExtrapolation =
false;
1292 DLOG(
"Strike extrapolation cannot be turned off on its own so defaulting to flat.");
1294 DLOG(
"Strike extrapolation has been set to flat.");
1296 DLOG(
"Strike extrapolation " << strikeExtrapType <<
" not expected so default to flat.");
1301 DLOG(
"BlackVarianceSurfaceMoneyness only supports flat volatility extrapolation in the time direction");
1304 DLOG(
"Extrapolation is turned off for the whole surface so the time and"
1305 <<
" strike extrapolation settings are ignored");
1309 if (vmsc.timeInterpolation() !=
"Linear") {
1310 DLOG(
"BlackVarianceSurfaceMoneyness only supports linear time interpolation in variance.");
1314 if (vmsc.strikeInterpolation() !=
"Linear") {
1315 DLOG(
"BlackVarianceSurfaceMoneyness only supports linear strike interpolation in variance.");
1319 QL_REQUIRE(!
pts_.empty(),
"Expected the price term structure to be populated for a moneyness surface.");
1320 Handle<PriceTermStructure> cpts =
pts_;
1321 if (vmsc.futurePriceCorrection() &&
expCalc_)
1325 Handle<Quote> spot(QuantLib::ext::make_shared<DerivedPriceQuote>(cpts));
1331 bool stickyStrike =
false;
1335 QL_REQUIRE(!
yts_.empty(),
"Expected yield term structure to be populated for a forward moneyness surface.");
1336 Handle<YieldTermStructure> pyts =
1337 Handle<YieldTermStructure>(QuantLib::ext::make_shared<PriceTermStructureAdapter>(*cpts, *
yts_));
1338 pyts->enableExtrapolation();
1340 DLOG(
"Creating BlackVarianceSurfaceMoneynessForward object");
1341 volatility_ = QuantLib::ext::make_shared<BlackVarianceSurfaceMoneynessForward>(
calendar_, spot, expiryTimes,
1343 yts_, stickyStrike, flatExtrapolation);
1347 DLOG(
"Creating BlackVarianceSurfaceMoneynessSpot object");
1348 volatility_ = QuantLib::ext::make_shared<BlackVarianceSurfaceMoneynessSpot>(
1349 calendar_, spot, expiryTimes, moneynessLevels, vols,
dayCounter_, stickyStrike, flatExtrapolation);
1352 DLOG(
"Setting BlackVarianceSurfaceMoneyness extrapolation to " <<
to_string(vmsc.extrapolation()));
1353 volatility_->enableExtrapolation(vmsc.extrapolation());
1355 LOG(
"CommodityVolCurve: finished building 2-D volatility moneyness strike surface");
1359 const VolatilityApoFutureSurfaceConfig& vapo,
1360 const Handle<BlackVolTermStructure>& baseVts,
1361 const Handle<PriceTermStructure>& basePts) {
1363 LOG(
"CommodityVolCurve: start building the APO surface");
1366 " RATE_LNVOL is currently supported for an APO surface.");
1369 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
1370 QL_REQUIRE(!vapo.baseConventionsId().empty(),
1371 "The APO FutureConventions must be populated to build a future APO surface");
1372 QL_REQUIRE(conventions->has(vapo.baseConventionsId()),
1373 "Conventions, " << vapo.baseConventionsId() <<
" for config " << vc.curveID() <<
" not found.");
1374 auto baseConvention =
1375 QuantLib::ext::dynamic_pointer_cast<CommodityFutureConvention>(conventions->get(vapo.baseConventionsId()));
1376 QL_REQUIRE(baseConvention,
1377 "Convention with ID '" << vapo.baseConventionsId() <<
"' should be of type CommodityFutureConvention");
1379 auto baseExpCalc = QuantLib::ext::make_shared<ConventionsBasedFutureExpiry>(*baseConvention);
1382 boost::optional<QuantLib::Period> maxTenor;
1383 if (!vapo.maxTenor().empty())
1387 vector<Real> moneynessLevels =
checkMoneyness(vapo.moneynessLevels());
1390 Real beta = vapo.beta();
1398 bool flatExtrapolation =
true;
1399 if (vapo.extrapolation()) {
1403 DLOG(
"Strike extrapolation switched to using interpolator.");
1404 flatExtrapolation =
false;
1406 DLOG(
"Strike extrapolation cannot be turned off on its own so defaulting to flat.");
1408 DLOG(
"Strike extrapolation has been set to flat.");
1410 DLOG(
"Strike extrapolation " << strikeExtrapType <<
" not expected so default to flat.");
1415 DLOG(
"ApoFutureSurface only supports flat volatility extrapolation in the time direction");
1418 DLOG(
"Extrapolation is turned off for the whole surface so the time and"
1419 <<
" strike extrapolation settings are ignored");
1423 if (vapo.timeInterpolation() !=
"Linear") {
1424 DLOG(
"ApoFutureSurface only supports linear time interpolation in variance.");
1428 if (vapo.strikeInterpolation() !=
"Linear") {
1429 DLOG(
"ApoFutureSurface only supports linear strike interpolation in variance.");
1432 DLOG(
"Creating ApoFutureSurface object");
1434 baseExpCalc, beta, flatExtrapolation, maxTenor);
1436 DLOG(
"Setting ApoFutureSurface extrapolation to " <<
to_string(vapo.extrapolation()));
1437 volatility_->enableExtrapolation(vapo.extrapolation());
1439 LOG(
"CommodityVolCurve: finished building the APO surface");
1444 const ProxyVolatilityConfig& pvc,
const map<
string, QuantLib::ext::shared_ptr<CommodityCurve>>& comCurves,
1445 const map<
string, QuantLib::ext::shared_ptr<CommodityVolCurve>>& volCurves,
1446 const map<
string, QuantLib::ext::shared_ptr<FXVolCurve>>& fxVolCurves,
1447 const map<
string, QuantLib::ext::shared_ptr<CorrelationCurve>>& requiredCorrelationCurves,
const Market* fxIndices) {
1449 DLOG(
"Build Proxy Vol surface");
1455 auto proxyConfig = *
curveConfigs.commodityCurveConfig(proxy);
1456 auto proxyVolConfig = *
curveConfigs.commodityVolatilityConfig(proxy);
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 "
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 "
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) {
1483 "CommodityVolCurve: FXVolatilityCurve must be provided for commodity vol config "
1484 <<
spec.
curveConfigID() <<
" as proxy currencies if different from commodity currency.");
1486 "CommodityVolCurve: CorrelationCurve must be provided for commodity vol config "
1487 <<
spec.
curveConfigID() <<
" as proxy currencies if different from commodity currency.");
1490 QL_REQUIRE(pvc.
fxVolatilityCurve().size() == 6,
"CommodityVolCurve: FXVolatilityCurve provided "
1493 <<
" must be of length 6, and of form CC1CCY2 e.g EURUSD");
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();
1504 if (proxyVolForCcy != proxyVolConfig.currency()) {
1505 Handle<BlackVolTermStructure> hFx(fxSurface);
1506 fxSurface = QuantLib::ext::make_shared<QuantExt::BlackInvertedVolTermStructure>(hFx);
1507 fxSurface->enableExtrapolation();
1510 fxIndex = fxIndices->
fxIndex(proxyVolConfig.currency() + config.currency()).currentLink();
1511 FXSpotSpec spotSpec(proxyVolConfig.currency(), config.currency());
1514 auto corrIt = requiredCorrelationCurves.find(corrSpec.
name());
1515 if (corrIt == requiredCorrelationCurves.end())
1516 QL_FAIL(
"CommodityVolCurve: cannot find required correlation curve "
1518 correlation = corrIt->second->corrTermStructure();
1521 volatility_ = QuantLib::ext::make_shared<BlackVolatilitySurfaceProxy>(
1522 proxyVolCurve->second->volatility(), curve->second->commodityIndex(), proxyCurve->second->commodityIndex(),
1523 fxSurface, fxIndex, correlation);
1527 const QuantLib::ext::shared_ptr<PriceTermStructure>& pts,
1528 const vector<Date>& optionExpiries)
const {
1530 LOG(
"CommodityVolCurve: start adding future price correction at option expiry.");
1533 map<Date, Real> curveData;
1536 vector<Date> ptsDates;
1538 ptsDates = ipc->pillarDates();
1540 ptsDates = ipc->pillarDates();
1542 ptsDates = ipc->pillarDates();
1544 ptsDates = ipc->pillarDates();
1546 ptsDates = ipc->pillarDates();
1548 ptsDates = ipc->pillarDates();
1550 DLOG(
"Could not cast the price term structure so do not have its pillar dates.");
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) <<
").");
1562 auto futureExpiryCalculator =
expCalc_;
1564 QuantLib::ext::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
1565 auto [found, conv] =
1568 futureExpiryCalculator = QuantLib::ext::make_shared<ConventionsBasedFutureExpiry>(
1569 *QuantLib::ext::dynamic_pointer_cast<CommodityFutureConvention>(conv));
1572 for (
const Date& oed : optionExpiries) {
1573 Date fed = futureExpiryCalculator->nextExpiry(
true, oed, 0,
false);
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) <<
").");
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) <<
").");
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);
1597 DayCounter dc = pts->dayCounter();
1600 QuantLib::ext::shared_ptr<PriceTermStructure> cpts;
1602 cpts = QuantLib::ext::make_shared<InterpolatedPriceCurve<Linear>>(asof, curveDates, curvePrices, dc, pts->currency());
1605 QuantLib::ext::make_shared<InterpolatedPriceCurve<LogLinear>>(asof, curveDates, curvePrices, dc, pts->currency());
1607 cpts = QuantLib::ext::make_shared<InterpolatedPriceCurve<Cubic>>(asof, curveDates, curvePrices, dc, pts->currency());
1610 QuantLib::ext::make_shared<InterpolatedPriceCurve<LinearFlat>>(asof, curveDates, curvePrices, dc, pts->currency());
1612 cpts = QuantLib::ext::make_shared<InterpolatedPriceCurve<LogLinearFlat>>(asof, curveDates, curvePrices, dc,
1616 QuantLib::ext::make_shared<InterpolatedPriceCurve<CubicFlat>>(asof, curveDates, curvePrices, dc, pts->currency());
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());
1621 cpts->enableExtrapolation(pts->allowsExtrapolation());
1623 LOG(
"CommodityVolCurve: finished adding future price correction at option expiry.");
1625 return Handle<PriceTermStructure>(cpts);
1629 Natural rollDays)
const {
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)) {
1637 result =
calendar_.adjust(asof + expiryPeriod->expiryPeriod());
1638 }
else if (
auto fcExpiry = QuantLib::ext::dynamic_pointer_cast<FutureContinuationExpiry>(expiry)) {
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);
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) <<
".");
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) <<
".");
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));
1668 TLOG(
"CommodityVolCurve::getExpiry: first option expiry is " << io::iso_date(result) <<
".");
1671 Natural fcIndex = fcExpiry->expiryIndex();
1674 const auto& ocm =
convention_->optionContinuationMappings();
1675 auto it = ocm.find(fcIndex);
1676 if (it != ocm.end())
1677 fcIndex = it->second;
1681 result =
expCalc_->nextExpiry(
true, result, fcIndex - 2,
true);
1684 DLOG(
"Expiry date corresponding to continuation expiry, " << *fcExpiry <<
1685 ", is " << io::iso_date(result) <<
".");
1688 QL_FAIL(
"CommodityVolCurve::getExpiry: cannot determine expiry type.");
1695 const map<
string, QuantLib::ext::shared_ptr<YieldCurve>>& yieldCurves,
1696 const map<
string, QuantLib::ext::shared_ptr<CommodityCurve>>& commodityCurves,
1697 bool searchYield,
bool dontThrow) {
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);
1708 }
else if (!dontThrow) {
1709 QL_FAIL(
"CommodityVolCurve: YieldCurveId was not populated for " << config.
curveID());
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);
1721 }
else if (!dontThrow) {
1722 QL_FAIL(
"CommodityVolCurve: PriceCurveId was not populated for " << config.
curveID());
1728 using boost::adaptors::transformed;
1729 using boost::algorithm::join;
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(
1740 return moneynessLevels;
1745 DLOG(
"CommodityVolCurve: building volatility calibration info");
1751 std::vector<Real> moneyness = *rc.
moneyness();
1752 std::vector<std::string> deltas = *rc.
deltas();
1753 std::vector<Period> expiries = *rc.
expiries();
1756 auto info = QuantLib::ext::make_shared<FxEqCommVolCalibrationInfo>();
1758 DeltaVolQuote::AtmType atmType = DeltaVolQuote::AtmType::AtmDeltaNeutral;
1759 DeltaVolQuote::DeltaType deltaType = DeltaVolQuote::DeltaType::Fwd;
1761 if (
auto vdsc = QuantLib::ext::dynamic_pointer_cast<VolatilityDeltaSurfaceConfig>(vc)) {
1772 info->switchTenor =
"na";
1773 info->riskReversalInFavorOf =
"na";
1774 info->butterflyStyle =
"na";
1776 std::vector<Real> times, forwards;
1777 for (
auto const& p : expiries) {
1779 info->expiryDates.push_back(d);
1780 times.push_back(
volatility_->dayCounter().empty() ? Actual365Fixed().yearFraction(asof, d)
1782 forwards.push_back(
pts_->price(d));
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));
1804 Real maxTime = QL_MAX_REAL;
1807 maxTime = Actual365Fixed().yearFraction(asof,
maxExpiry_);
1812 DeltaVolQuote::AtmType at;
1813 DeltaVolQuote::DeltaType dt;
1814 for (Size i = 0; i < times.size(); ++i) {
1821 at = DeltaVolQuote::AtmDeltaNeutral;
1822 dt = DeltaVolQuote::Fwd;
1824 bool validSlice =
true;
1825 for (Size j = 0; j < deltas.size(); ++j) {
1839 Real stddev = std::sqrt(
volatility_->blackVariance(t, strike));
1840 callPricesDelta[i][j] = QuantExt::blackFormula(Option::Call, strike, forwards[i], stddev);
1843 info->deltaPutPrices[i][j] = blackFormula(Option::Put, strike, forwards[i], stddev,
yts_->discount(t));
1845 info->deltaCallPrices[i][j] = blackFormula(Option::Call, strike, forwards[i], stddev,
yts_->discount(t));
1848 info->deltaGridStrikes[i][j] = strike;
1849 info->deltaGridImpliedVolatility[i][j] = stddev / std::sqrt(t);
1850 }
catch (
const std::exception& e) {
1852 TLOG(
"CommodityVolCurve: error for time " << t <<
" delta " << deltas[j] <<
": " << e.what());
1858 callPricesDelta[i]);
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;
1871 info->isArbitrageFree =
false;
1875 TLOG(
"CommodityVolCurve: Delta surface arbitrage analysis completed.");
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) {
1898 for (Size j = 0; j < moneyness.size(); ++j) {
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));
1908 calibrationInfo_->moneynessPutPrices[i][j] = blackFormula(Option::Put, strike, forwards[i], stddev,
yts_->discount(t));
1910 }
catch (
const std::exception& e) {
1911 TLOG(
"CommodityVolCurve: error for time " << t <<
" moneyness " << moneyness[j] <<
": " << e.what());
1915 if (!times.empty() && !moneyness.empty()) {
1918 callPricesMoneyness);
1919 for (Size i = 0; i < times.size(); ++i) {
1920 info->moneynessGridProb[i] = cm.
timeSlices()[i].density();
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;
1933 TLOG(
"CommodityVolCurve: Moneyness surface Arbitrage analysis completed:");
1938 catch (std::exception& e){
1939 QL_FAIL(
"CommodityVolCurve: calibration info building failed: " << e.what());
1941 QL_FAIL(
"CommodityVolCurve: calibration info building failed: unknown error");
const std::vector< bool > & butterflyArbitrage() const
bool arbitrageFree() const
const std::vector< bool > & callSpreadArbitrage() const
const std::vector< Real > & density() const
const std::vector< std::vector< bool > > & butterflyArbitrage() const
bool arbitrageFree() const
const std::vector< std::vector< bool > > & calendarArbitrage() const
const std::vector< std::vector< bool > > & callSpreadArbitrage() const
const std::vector< CarrMadanMarginalProbability > & timeSlices() const
Commodity curve description.
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.
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.
QuantLib::ext::shared_ptr< QuantExt::FutureExpiryCalculator > expCalc_
QuantLib::Handle< QuantExt::PriceTermStructure > pts_
QuantLib::ext::shared_ptr< CommodityFutureConvention > convention_
QuantLib::ext::shared_ptr< QuantLib::BlackVolTermStructure > volatility_
QuantLib::ext::shared_ptr< FxEqCommVolCalibrationInfo > calibrationInfo_
void buildVolCalibrationInfo(const Date &asof, QuantLib::ext::shared_ptr< VolatilityConfig > &volatilityConfig, const CurveConfigurations &curveConfigs, const CommodityVolatilityConfig &config)
Build the calibration info.
QuantLib::Date maxExpiry_
QuantLib::Calendar calendar_
QuantLib::Handle< QuantExt::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
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_.
const CommodityVolatilityCurveSpec & spec() const
CommodityVolCurve()
Default constructor.
QuantLib::Handle< QuantLib::YieldTermStructure > yts_
std::vector< QuantLib::Real > checkMoneyness(const std::vector< std::string > &moneynessLevels) const
Check and return moneyness levels.
void buildVolatilityExplicit(const QuantLib::Date &asof, CommodityVolatilityConfig &vc, const VolatilityStrikeSurfaceConfig &vssc, const Loader &loader, const std::vector< QuantLib::Real > &configuredStrikes)
QuantLib::DayCounter dayCounter_
Commodity volatility configuration.
const std::string & currency() const
OneDimSolverConfig solverConfig() const
QuantLib::Natural optionExpiryRollDays() const
const std::string & yieldCurveId() const
const std::string & priceCurveId() const
const std::string & futureConventionsId() const
const boost::optional< bool > & preferOutOfTheMoney() const
const ReportConfig & reportConfig() const
Commodity volatility description.
Correlation curve description.
const string & curveID() const
virtual const vector< string > & quotes()
Return all the market quotes required for this config.
Container class for all Curve Configurations.
const std::string & curveConfigID() const
string name() const
returns the unique curve name
Utility class for handling delta strings ATM, 10P, 25C, ... used e.g. for FX Surfaces.
FX Volatility curve description.
Market data loader base class.
virtual QuantLib::ext::shared_ptr< MarketDatum > get(const std::string &name, const QuantLib::Date &d) const
get quote by its unique name, throws if not existent, override in derived classes for performance
QuantLib::Handle< QuantExt::FxIndex > fxIndex(const string &fxIndex, const string &configuration=Market::defaultConfiguration) const
const std::string & correlationCurve() const
const std::string & fxVolatilityCurve() const
const std::string & proxyVolatilityCurve() const
const MarketDatum::QuoteType & quoteType() const
const QuantLib::Exercise::Type & exerciseType() const
const boost::optional< bool > reportOnDeltaGrid() const
const boost::optional< std::vector< std::string > > & deltas() const
const boost::optional< bool > reportOnMoneynessGrid() const
const boost::optional< std::vector< Period > > & expiries() const
const boost::optional< std::vector< Real > > & moneyness() const
const std::string & interpolation() const
const std::string & extrapolation() const
const std::vector< std::string > & quotes() const
const std::string & deltaType() const
bool futurePriceCorrection() const
const std::vector< std::string > & putDeltas() const
const std::vector< std::string > & callDeltas() const
const std::vector< std::string > & expiries() const
const std::string & atmDeltaType() const
const std::string & atmType() const
const std::vector< std::string > & expiries() const
const std::vector< std::string > & strikes() const
const std::string & timeInterpolation() const
bool extrapolation() const
const std::string & strikeInterpolation() const
const std::string & strikeExtrapolation() const
const std::string & timeExtrapolation() const
Wrapper class for building commodity volatility structures.
SafeStack< ValueType > value
SafeStack< Filter > filter
Base class for classes that perform date calculations for future contracts.
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
Period parsePeriod(const string &s)
Convert text to QuantLib::Period.
DeltaVolQuote::AtmType parseAtmType(const std::string &s)
Convert text to QuantLib::DeltaVolQuote::AtmType.
Real parseReal(const string &s)
Convert text to Real.
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
DeltaVolQuote::DeltaType parseDeltaType(const std::string &s)
Convert text to QuantLib::DeltaVolQuote::DeltaType.
Map text representations to QuantLib/QuantExt types.
Classes and functions for log message handling.
#define LOG(text)
Logging Macro (Level = Notice)
#define DLOG(text)
Logging Macro (Level = Debug)
#define TLOGGERSTREAM(text)
#define WLOG(text)
Logging Macro (Level = Warning)
#define TLOG(text)
Logging Macro (Level = Data)
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)
RandomVariable exp(RandomVariable x)
std::ostream & operator<<(std::ostream &out, EquityReturnType t)
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)
MoneynessStrike::Type parseMoneynessType(const string &type)
Parse MoneynessStrike::Type from type.
ReportConfig effectiveReportConfig(const ReportConfig &globalConfig, const ReportConfig &localConfig)
std::string to_string(const LocationInfo &l)
QuantLib::ext::shared_ptr< QuantExt::CommodityIndex > parseCommodityIndex(const string &name, bool hasPrefix, const Handle< PriceTermStructure > &ts, const Calendar &cal, const bool enforceFutureIndex)
Extrapolation parseExtrapolation(const string &s)
Parse Extrapolation from string.
QuantLib::ext::shared_ptr< Expiry > parseExpiry(const string &strExpiry)
Parse an Expiry from its string representation, strExpiry.
boost::optional< Wildcard > getUniqueWildcard(const C &c)
checks if at most one element in C has a wild card and returns it in this case
Serializable Credit Default Swap.
Map text representations to QuantLib/QuantExt types.
vector< string > curveConfigs
string conversion utilities
utilities for wildcard handling