22#include <ql/math/interpolations/backwardflatinterpolation.hpp>
23#include <ql/math/interpolations/convexmonotoneinterpolation.hpp>
24#include <ql/math/interpolations/loginterpolation.hpp>
25#include <ql/termstructures/yield/discountcurve.hpp>
26#include <ql/termstructures/yield/flatforward.hpp>
27#include <ql/termstructures/yield/zerocurve.hpp>
45 const map<
string, QuantLib::ext::shared_ptr<YieldCurve>>& requiredYieldCurves,
46 const bool buildCalibrationInfo) {
52 dc_ = Actual365Fixed();
53 if (config->dayCountID() ==
"") {
61 if (!config->calendar().empty()) {
64 }
catch (exception& ex) {
65 WLOG(
"Failed to get Calendar name for " << config->calendar() <<
":" << ex.what());
76 Handle<Quote> equitySpot;
77 Handle<YieldTermStructure> forecastYieldTermStructure;
78 Handle<YieldTermStructure> dividendYieldTermStructure;
81 YieldCurveSpec ycspec(config->currency(), config->forecastingCurve());
86 auto itr = requiredYieldCurves.find(ycspec.
name());
87 QL_REQUIRE(itr != requiredYieldCurves.end(),
88 "Yield Curve Spec - " << ycspec.
name() <<
" - not found during equity curve build");
89 QuantLib::ext::shared_ptr<YieldCurve> yieldCurve = itr->second;
90 forecastYieldTermStructure = yieldCurve->handle();
100 vector<QuantLib::ext::shared_ptr<EquityForwardQuote>> qt;
101 vector<QuantLib::ext::shared_ptr<EquityOptionQuote>> oqt;
103 Size quotesExpired = 0;
111 DLOG(
"Wild card quote specified for " << config->curveID())
114 oqt.reserve(config->fwdQuotes().size());
116 quotes_.reserve(config->fwdQuotes().size());
117 terms_.reserve(config->fwdQuotes().size());
123 auto q = QuantLib::ext::dynamic_pointer_cast<EquitySpotQuote>(loader.
get(config->equitySpotQuoteID(), asof));
124 QL_REQUIRE(q !=
nullptr,
"expected '" << config->equitySpotQuoteID() <<
"' to be an EquitySpotQuote");
127 equitySpot = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(spot));
132 for (
auto const& md : loader.
get(*wildcard, asof)) {
133 auto q = QuantLib::ext::dynamic_pointer_cast<EquityForwardQuote>(md);
134 QL_REQUIRE(q !=
nullptr,
"expected '" << md->name() <<
"' to be an EquityForwardQuote");
135 QL_REQUIRE(find(qt.begin(), qt.end(), q) == qt.end(),
136 "duplicate market datum found for " << q->name());
137 if (asof < q->expiryDate()) {
138 DLOG(
"EquityCurve Forward Price found for quote: " << q->name());
143 DLOG(
"Ignore expired ForwardPrice/ForwardDividendPrice quote "
144 << q->name() <<
", expired at " << io::iso_date(q->expiryDate()));
148 for (
auto const& md :
149 loader.
get(std::set<std::string>(config->fwdQuotes().begin(), config->fwdQuotes().end()), asof)) {
150 auto q = QuantLib::ext::dynamic_pointer_cast<EquityForwardQuote>(md);
151 QL_REQUIRE(q !=
nullptr,
"expected '" << md->name() <<
"' to be an EquityForwardQuote");
152 if (asof < q->expiryDate()) {
154 QL_REQUIRE(isUnique,
"duplicate market datum found for " << q->name());
155 terms_.push_back(q->expiryDate());
160 DLOG(
"Ignore expired ForwardPrice/ForwardDividendPrice quote "
161 << q->name() <<
", expired at " << io::iso_date(q->expiryDate()));
170 for (
auto const& md : loader.
get(*wildcard, asof)) {
171 auto q = QuantLib::ext::dynamic_pointer_cast<EquityOptionQuote>(md);
172 QL_REQUIRE(q !=
nullptr,
"expected '" << md->name() <<
"' to be an EquityOptionQuote");
173 QL_REQUIRE(find(oqt.begin(), oqt.end(), q) == oqt.end(),
174 "duplicate market datum found for " << q->name());
176 DLOG(
"EquityCurve Volatility Price found for quote: " << q->name());
181 DLOG(
"Ignore expired OptionPremium quote " << q->name() <<
", expired at " << q->expiry());
185 for (
auto const& md :
186 loader.
get(std::set<std::string>(config->fwdQuotes().begin(), config->fwdQuotes().end()), asof)) {
187 auto q = QuantLib::ext::dynamic_pointer_cast<EquityOptionQuote>(md);
188 QL_REQUIRE(q !=
nullptr,
"expected '" << md->name() <<
"' to be an EquityOptionQuote");
190 DLOG(
"EquityCurve Volatility Price found for quote: " << q->name());
195 DLOG(
"Ignore expired OptionPremium quote " << q->name() <<
", expired at " << q->expiry());
202 for (
auto const& md :
203 loader.
get(std::set<std::string>(config->fwdQuotes().begin(), config->fwdQuotes().end()), asof)) {
204 QuantLib::ext::shared_ptr<EquityDividendYieldQuote> q =
205 QuantLib::ext::dynamic_pointer_cast<EquityDividendYieldQuote>(md);
206 QL_REQUIRE(q !=
nullptr,
"expected '" << md->name() <<
"' to be an EquityDividendYieldQuote");
207 if (q->tenorDate() > asof) {
209 QL_REQUIRE(isUnique,
"duplicate market datum found for " << q->name());
210 DLOG(
"EquityCurve Dividend Yield found for quote: " << q->name());
211 terms_.push_back(q->tenorDate());
212 quotes_.push_back(q->quote()->value());
215 DLOG(
"Ignore expired DividendYield quote " << q->name() <<
", expired at "
216 << io::iso_date(q->tenorDate()));
223 DLOG(
"EquityCurve: read " << quotesRead + quotesExpired <<
" quotes of type " << config->type());
224 DLOG(
"EquityCurve: ignored " << quotesExpired <<
" expired quotes.");
227 QL_REQUIRE(quotesRead + quotesExpired == config->fwdQuotes().size(),
228 "read " << quotesRead <<
"quotes and " << quotesExpired <<
" expire quotes, but "
229 << config->fwdQuotes().size() <<
" required.");
234 QL_REQUIRE(
terms_.size() ==
quotes_.size(),
"Internal error: terms and quotes mismatch");
236 vector<pair<Date, Real>> tmpSort;
239 [](
const Date& d,
const Real& q) {
return make_pair(d, q); });
241 std::sort(tmpSort.begin(), tmpSort.end(), [](
const pair<Date, Real>& left,
const pair<Date, Real>& right) {
242 return left.first < right.first;
245 for (Size i = 0; i < tmpSort.size(); ++i) {
246 terms_[i] = tmpSort[i].first;
247 quotes_[i] = tmpSort[i].second;
250 for (Size i = 0; i <
terms_.size(); i++) {
251 QL_REQUIRE(
terms_[i] > asof,
"Invalid Fwd Expiry " <<
terms_[i] <<
" vs. " << asof);
253 QL_REQUIRE(
terms_[i] >
terms_[i - 1],
"terms must be increasing in curve config");
266 DLOG(
"Building Equity Dividend Yield curve from Forward/Future prices");
270 QL_REQUIRE(quotesRead > 0,
"Wild card quote specified, but no quotes read.");
273 std::sort(qt.begin(), qt.end(),
274 [](
const QuantLib::ext::shared_ptr<EquityForwardQuote>& a,
275 const QuantLib::ext::shared_ptr<EquityForwardQuote>& b) ->
bool {
276 return a->expiryDate() < b->expiryDate();
280 for (Size i = 0; i < qt.size(); i++) {
281 terms_.push_back(qt[i]->expiryDate());
288 DLOG(
"No Equity Forward quotes provided for " << config->curveID()
289 <<
", continuing without dividend curve.");
294 if (oqt.size() == 0) {
295 DLOG(
"No Equity Option quotes provided for " << config->curveID()
296 <<
", continuing without dividend curve.");
299 DLOG(
"Building Equity Dividend Yield curve from Option Volatilities");
300 vector<QuantLib::ext::shared_ptr<EquityOptionQuote>> calls, puts;
301 vector<Date> callDates, putDates;
302 vector<Real> callStrikes, putStrikes;
303 vector<Real> callPremiums, putPremiums;
304 Calendar cal = NullCalendar();
308 if (q->quote()->value() > 0) {
319 for (Size i = 0; i < calls.size(); i++) {
320 for (Size j = 0; j < puts.size(); j++) {
321 if (calls[i]->expiry() == puts[j]->expiry()) {
322 auto callAbsoluteStrike = QuantLib::ext::dynamic_pointer_cast<AbsoluteStrike>(calls[i]->strike());
323 auto putAbsoluteStrike = QuantLib::ext::dynamic_pointer_cast<AbsoluteStrike>(puts[j]->strike());
324 QL_REQUIRE(callAbsoluteStrike,
"Expected absolute strike for quote " << calls[i]->
name());
325 QL_REQUIRE(putAbsoluteStrike,
"Expected absolute strike for quote " << puts[j]->
name());
326 if (*calls[i]->strike() == *puts[j]->strike()) {
327 TLOG(
"Adding Call and Put for strike/expiry pair : " << calls[i]->expiry() <<
"/"
328 << calls[i]->strike()->toString());
337 callStrikes.push_back(callStrike);
338 putStrikes.push_back(putStrike);
340 callPremiums.push_back(
342 putPremiums.push_back(
349 if (callDates.size() > 0 && putDates.size() > 0) {
350 DLOG(
"Found " << callDates.size() <<
" Call and Put Option Volatilities");
352 DLOG(
"Building a Sparse Volatility surface for calls and puts");
354 QuantLib::ext::shared_ptr<OptionPriceSurface> callSurface =
355 QuantLib::ext::make_shared<OptionPriceSurface>(asof, callDates, callStrikes, callPremiums,
dc_);
356 QuantLib::ext::shared_ptr<OptionPriceSurface> putSurface =
357 QuantLib::ext::make_shared<OptionPriceSurface>(asof, putDates, putStrikes, putPremiums,
dc_);
358 DLOG(
"CallSurface contains " << callSurface->expiries().size() <<
" expiries.");
360 DLOG(
"Stripping equity forwards from the option premium surfaces");
361 QuantLib::ext::shared_ptr<EquityForwardCurveStripper> efcs = QuantLib::ext::make_shared<EquityForwardCurveStripper>(
362 callSurface, putSurface, forecastYieldTermStructure, equitySpot, config->exerciseStyle());
365 terms_ = efcs->expiries();
369 <<
" building NoDividends curve");
376 vector<Rate> dividendRates;
383 for (Size i = 0; i <
quotes_.size(); i++) {
385 <<
", expiry: " <<
terms_[i]);
386 Time t =
dc_.yearFraction(asof,
terms_[i]);
387 Rate ir_rate = forecastYieldTermStructure->zeroRate(t, Continuous);
388 dividendRates.push_back(
::log(equitySpot->value() /
quotes_[i]) / t + ir_rate);
391 DLOG(
"Building Equity Dividend Yield curve from Dividend Yield rates");
394 DLOG(
"Building flat Equity Dividend Yield curve as no quotes provided");
396 dividendYieldTermStructure = Handle<YieldTermStructure>(QuantLib::ext::make_shared<FlatForward>(asof, 0.0,
dc_));
399 equitySpot, forecastYieldTermStructure, dividendYieldTermStructure);
402 QL_FAIL(
"Invalid Equity curve configuration type for " <<
spec_.
name());
404 QL_REQUIRE(dividendRates.size() > 0,
"No dividend yield rates extracted for " <<
spec_.
name());
405 QL_REQUIRE(dividendRates.size() ==
terms_.size(),
"vector size mismatch - dividend rates ("
406 << dividendRates.size() <<
") vs terms (" <<
terms_.size()
410 vector<Real> dividendDiscountFactors;
411 for (Size i = 0; i <
quotes_.size(); i++) {
412 Time t =
dc_.yearFraction(asof,
terms_[i]);
413 dividendDiscountFactors.push_back(std::exp(-dividendRates[i] * t));
416 QuantLib::ext::shared_ptr<YieldTermStructure> baseDivCurve;
418 if (dividendRates.size() == 1) {
420 baseDivCurve.reset(
new FlatForward(asof, dividendRates[0],
dc_));
424 vector<Date> dates(n + 1);
425 vector<Rate> rates(n + 1);
426 vector<Real> discounts(n + 1);
427 for (Size i = 0; i < n; i++) {
429 rates[i + 1] = dividendRates[i];
430 discounts[i + 1] = dividendDiscountFactors[i];
435 if (forecastYieldTermStructure->maxDate() > dates.back()) {
436 dates.push_back(forecastYieldTermStructure->maxDate());
437 rates.push_back(rates.back());
438 Time maxTime =
dc_.yearFraction(asof, forecastYieldTermStructure->maxDate());
440 std::exp(-rates.back() * maxTime));
447 QL_FAIL(
"Unsupported interpolation variable for dividend yield curve");
451 QuantLib::ext::shared_ptr<YieldTermStructure> divCurve;
452 if (config->extrapolation()) {
453 divCurve = baseDivCurve;
454 divCurve->enableExtrapolation();
456 divCurve = QuantLib::ext::make_shared<FlatForwardDividendCurve>(
457 asof, Handle<YieldTermStructure>(baseDivCurve), forecastYieldTermStructure);
458 if (config->dividendExtrapolation())
459 divCurve->enableExtrapolation();
462 dividendYieldTermStructure = Handle<YieldTermStructure>(divCurve);
466 equitySpot, forecastYieldTermStructure, dividendYieldTermStructure);
468 if (buildCalibrationInfo) {
478 calibrationInfo_->zeroRates.push_back(dividendYieldTermStructure->zeroRate(d,
dc_, Continuous));
479 calibrationInfo_->discountFactors.push_back(dividendYieldTermStructure->discount(d));
480 calibrationInfo_->times.push_back(dividendYieldTermStructure->timeFromReference(d));
484 }
catch (std::exception& e) {
485 QL_FAIL(
"equity curve building failed: " << e.what());
487 QL_FAIL(
"equity curve building failed: unknown error");
Container class for all Curve Configurations.
const std::string & curveConfigID() const
string name() const
returns the unique curve name
Type
Supported equity curve types.
const EquityCurveSpec & spec() const
EquityCurve()
Default constructor.
EquityCurveConfig::Type curveType_
YieldCurve::InterpolationVariable dividendInterpVariable_
YieldCurve::InterpolationMethod dividendInterpMethod_
QuantLib::ext::shared_ptr< YieldCurveCalibrationInfo > calibrationInfo_
QuantLib::ext::shared_ptr< QuantExt::EquityIndex2 > equityIndex_
Equity 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
SafeStack< ValueType > value
Wrapper class for building Equity curves.
Date getDateFromDateOrPeriod(const string &token, Date asof, QuantLib::Calendar cal, QuantLib::BusinessDayConvention bdc)
Get a date from a date string or period.
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
QuantLib::Real convertMinorToMajorCurrency(const std::string &s, QuantLib::Real value)
Convert a value from a minor ccy to major.
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
Classes and functions for log message handling.
#define DLOG(text)
Logging Macro (Level = Debug)
#define WLOG(text)
Logging Macro (Level = Warning)
#define TLOG(text)
Logging Macro (Level = Data)
RandomVariable log(RandomVariable x)
YieldCurve::InterpolationVariable parseYieldCurveInterpolationVariable(const string &s)
Helper function for parsing interpolation variable.
QuantLib::ext::shared_ptr< YieldTermStructure > discountcurve(const vector< Date > &dates, const vector< DiscountFactor > &dfs, const DayCounter &dayCounter, YieldCurve::InterpolationMethod interpolationMethod, Size n)
Create a Interpolated Discount Curve and apply interpolators.
YieldCurve::InterpolationMethod parseYieldCurveInterpolationMethod(const string &s)
Helper function for parsing interpolation method.
QuantLib::ext::shared_ptr< YieldTermStructure > zerocurve(const vector< Date > &dates, const vector< Rate > &yields, const DayCounter &dayCounter, YieldCurve::InterpolationMethod interpolationMethod, Size n)
Create a Interpolated Zero Curve and apply interpolators.
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.
static const std::vector< QuantLib::Period > defaultPeriods
vector< string > curveConfigs
utilities for wildcard handling