Skip to content

Commit 1024a69

Browse files
pcaspersjenkins
authored andcommitted
QPR-12074 QPR-12099 Remove extrapolation of equity curve, write out equity curves by equity Id
1 parent d0a90f6 commit 1024a69

16 files changed

Lines changed: 169 additions & 43 deletions

Docs/UserGuide/parameterisation/curveconfig.tex

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,9 @@ \subsubsection{Equity Curve Structures}
309309
<!-- See Table \ref{tab:allow_interp_methods} for allowed interpolation methods -->
310310
<InterpolationMethod>Linear</InterpolationMethod>
311311
</DividendInterpolation>
312-
<!-- Optional node, defaults to true -->
313-
<Extrapolation>True</Extrapolation>
312+
<DividendExtrapolation>False</DividendExtrapolation>
313+
<!-- Optional node, defaults to false -->
314+
<Extrapolation>False</Extrapolation>
314315
<DayCounter>A365</DayCounter>
315316
</EquityCurve>
316317
<EquityCurve>
@@ -336,7 +337,8 @@ \subsubsection{Equity Curve Structures}
336337
\item Quotes [Optional]: Market datum IDs/names to be used in building the curve structure.
337338
\item DayCounter [Optional]: The term structure's day counter used in date to time conversions. Defaults to {\tt A365F}.
338339
\item DividendInterpolation [Optional]: This node contains an \lstinline!InterpolationVariable! and \lstinline!InterpolationMethod! sub-node, which define the variable on which the interpolation is performed and the interpolation method for the dividend curve, respectively. The allowable values are found in Table \ref{tab:allow_interp_variables} and Table \ref{tab:allow_interp_methods}, respectively. This should not be provided if \lstinline!Type! is {\tt NoDividends}.
339-
\item Extrapolation [Optional]: Boolean flag indicating whether extrapolation is allowed. Defaults to {\tt True}.
340+
\item DividendExtrapolation [Optional]: Boolean flag indicating whether extrapolation in the dividend curve is allowed. If True the dividend curve is extrapolated forward at the risk free rate beyond the last date of the curve - this is only considered when \lstinline!Extrapolation! is False. Defaults to {\tt False}.
341+
\item Extrapolation [Optional]: Boolean flag indicating whether extrapolation in the forward price is allowed. Defaults to {\tt False}.
340342
\end{itemize}
341343

342344
The equity curves here consists of a spot equity price, as well as a set of either forward prices or else dividend
@@ -358,6 +360,7 @@ \subsubsection{Equity Volatility Structures}
358360
\begin{itemize}
359361
\item CurveId: Unique identifier for the curve.
360362
\item CurveDescription [Optional]: A description of the curve. This field may be left blank.
363+
\item EquityId: [Optional] Identifies the underlying equity name, this is used in the construction of the \lstinline!Quote! strings. If omitted the \lstinline!CurveId! is used instead.
361364
\item Currency: Currency of the equity.
362365
\item Calendar [Optional]: allowable value is any valid calendar. Defaults to \lstinline!NullCalendar!.
363366
\item DayCounter [Optional]: allowable value is any valid day counter. Defaults to \lstinline!A365!.
@@ -374,10 +377,11 @@ \subsubsection{Equity Volatility Structures}
374377
<EquityVolatility>
375378
<CurveId>SP5</CurveId>
376379
<CurveDescription>Lognormal option implied vols for SP 500</CurveDescription>
380+
<EquityId>RIC:.SP5</EquityId>
377381
<Currency>USD</Currency>
378382
<DayCounter>Actual/365 (Fixed)</DayCounter>
379383
<Constant>
380-
<Quote>EQUITY_OPTION/RATE_LNVOL/SP5/USD/5Y/ATMF</Quote>
384+
<Quote>EQUITY_OPTION/RATE_LNVOL/RIC:.SP5/USD/5Y/ATMF</Quote>
381385
</Constant>
382386
</EquityVolatility>
383387
<EquityVolatility>
@@ -398,13 +402,14 @@ \subsubsection{Equity Volatility Structures}
398402
<EquityVolatility>
399403
<CurveId>SP5</CurveId>
400404
<CurveDescription>Lognormal option implied vols for SP 500</CurveDescription>
405+
<EquityId>RIC:.SP5</EquityId>
401406
<Currency>USD</Currency>
402407
<DayCounter>Actual/365 (Fixed)</DayCounter>
403408
<Curve>
404409
<QuoteType>ImpliedVolatility</QuoteType>
405410
<VolatilityType>Lognormal</VolatilityType>
406411
<Quotes>
407-
<Quote>EQUITY_OPTION/RATE_LNVOL/SP5/USD/*</Quote>
412+
<Quote>EQUITY_OPTION/RATE_LNVOL/RIC:.SP5/USD/*</Quote>
408413
</Quotes>
409414
<Interpolation>LinearFlat</Interpolation>
410415
<Extrapolation>Flat</Extrapolation>
@@ -463,6 +468,7 @@ \subsubsection{Equity Volatility Structures}
463468
<EquityVolatility>
464469
<CurveId>SP5</CurveId>
465470
<CurveDescription>Lognormal option implied vols for SP 500</CurveDescription>
471+
<EquityId>RIC:.SP5</EquityId>
466472
<Currency>USD</Currency>
467473
<DayCounter>Actual/365 (Fixed)</DayCounter>
468474
<StrikeSurface>
@@ -509,6 +515,7 @@ \subsubsection{Equity Volatility Structures}
509515
<EquityVolatility>
510516
<CurveId>SP5</CurveId>
511517
<CurveDescription>Lognormal option implied vols for SP 500</CurveDescription>
518+
<EquityId>RIC:.SP5</EquityId>
512519
<Currency>USD</Currency>
513520
<DayCounter>Actual/365 (Fixed)</DayCounter>
514521
<MoneynessSurface>
@@ -542,6 +549,7 @@ \subsubsection{Equity Volatility Structures}
542549
<EquityVolatility>
543550
<CurveId>ABC</CurveId>
544551
<CurveDescription>Lognormal option implied vols for APC - proxied from SP5</CurveDescription>
552+
<EquityId>RIC:.SP5</EquityId>
545553
<Currency>USD</Currency>
546554
<DayCounter>Actual/365 (Fixed)</DayCounter>
547555
<ProxySurface>

OREAnalytics/orea/app/inputparameters.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,7 @@ class InputParameters {
631631
bool outputCurves_ = false;
632632
std::string curvesMarketConfig_ = Market::defaultConfiguration;
633633
std::string curvesGrid_ = "240,1M";
634-
bool outputTodaysMarketCalibration_ = false;
634+
bool outputTodaysMarketCalibration_ = true;
635635

636636
/***********************************
637637
* CASHFLOW and CASHFLOWNPV analytic

OREData/ored/configuration/curveconfigurations.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ void CurveConfigurations::parseNode(const CurveSpec::CurveType& type, const stri
135135
configs_[type][curveId] = config;
136136
unparsed_.at(type).erase(curveId);
137137
} catch (std::exception& ex) {
138-
string err = "Curve config under node '" + to_string(type) + "was requested, but could not be parsed.";
138+
string err = "Curve config under node '" + to_string(type) + " was requested, but could not be parsed.";
139139
ALOG(StructuredCurveErrorMessage(curveId, err, ex.what()));
140140
QL_FAIL(err);
141141
}

OREData/ored/configuration/equitycurveconfig.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,12 @@ EquityCurveConfig::EquityCurveConfig(const string& curveID, const string& curveD
3131
const EquityCurveConfig::Type& type, const string& equitySpotQuote,
3232
const vector<string>& fwdQuotes, const string& dayCountID,
3333
const string& dividendInterpVariable, const string& dividendInterpMethod,
34-
bool extrapolation, const QuantLib::Exercise::Type& exerciseStyle)
34+
const bool dividendExtrapolation, const bool extrapolation,
35+
const QuantLib::Exercise::Type& exerciseStyle)
3536
: CurveConfig(curveID, curveDescription), fwdQuotes_(fwdQuotes), forecastingCurve_(forecastingCurve),
36-
currency_(currency), calendar_(calendar), type_(type), equitySpotQuoteID_(equitySpotQuote), dayCountID_(dayCountID),
37-
divInterpVariable_(dividendInterpVariable), divInterpMethod_(dividendInterpMethod), extrapolation_(extrapolation),
38-
exerciseStyle_(exerciseStyle) {
37+
currency_(currency), calendar_(calendar), type_(type), equitySpotQuoteID_(equitySpotQuote), dayCountID_(dayCountID),
38+
divInterpVariable_(dividendInterpVariable), divInterpMethod_(dividendInterpMethod), dividendExtrapolation_(dividendExtrapolation),
39+
extrapolation_(extrapolation), exerciseStyle_(exerciseStyle) {
3940
quotes_ = fwdQuotes;
4041
quotes_.insert(quotes_.begin(), equitySpotQuote);
4142
populateRequiredCurveIds();
@@ -72,8 +73,8 @@ void EquityCurveConfig::fromXML(XMLNode* node) {
7273
divInterpVariable_ = "Zero";
7374
divInterpMethod_ = divInterpVariable_ == "Zero" ? "Linear" : "LogLinear";
7475
}
75-
76-
extrapolation_ = XMLUtils::getChildValueAsBool(node, "Extrapolation"); // defaults to true
76+
dividendExtrapolation_ = XMLUtils::getChildValueAsBool(node, "DividendExtrapolation", false, false);
77+
extrapolation_ = XMLUtils::getChildValueAsBool(node, "Extrapolation", false, false);
7778

7879
if (type_ == Type::NoDividends) {
7980
QL_REQUIRE(fwdQuotes_.size() == 0,
@@ -105,6 +106,7 @@ XMLNode* EquityCurveConfig::toXML(XMLDocument& doc) {
105106
XMLUtils::addChild(doc, divInterpNode, "InterpolationVariable", divInterpVariable_);
106107
XMLUtils::addChild(doc, divInterpNode, "InterpolationMethod", divInterpMethod_);
107108
}
109+
XMLUtils::addChild(doc, node, "DividendExtrapolation", dividendExtrapolation_);
108110
XMLUtils::addChild(doc, node, "Extrapolation", extrapolation_);
109111

110112
return node;

OREData/ored/configuration/equitycurveconfig.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class EquityCurveConfig : public CurveConfig {
5656
const string& currency, const string& calendar, const Type& type, const string& equitySpotQuote,
5757
const vector<string>& quotes, const string& dayCountID = "",
5858
const string& dividendInterpVariable = "Zero", const string& dividendInterpMethod = "Linear",
59-
bool extrapolation = true,
59+
const bool dividendExtrapolation = false, const bool extrapolation = false,
6060
const QuantLib::Exercise::Type& exerciseStyle = QuantLib::Exercise::Type::European);
6161
//! Default constructor
6262
EquityCurveConfig() {}
@@ -78,6 +78,7 @@ class EquityCurveConfig : public CurveConfig {
7878
const string& dayCountID() const { return dayCountID_; }
7979
const string& dividendInterpolationVariable() const { return divInterpVariable_; }
8080
const string& dividendInterpolationMethod() const { return divInterpMethod_; }
81+
bool dividendExtrapolation() const { return dividendExtrapolation_; }
8182
bool extrapolation() const { return extrapolation_; }
8283
const QuantLib::Exercise::Type exerciseStyle() const { return exerciseStyle_; }
8384
const vector<string>& fwdQuotes() { return fwdQuotes_; }
@@ -91,6 +92,7 @@ class EquityCurveConfig : public CurveConfig {
9192
string& dayCountID() { return dayCountID_; }
9293
string& dividendInterpolationVariable() { return divInterpVariable_; }
9394
string& dividendInterpolationMethod() { return divInterpMethod_; }
95+
bool& dividendExtrapolation() { return dividendExtrapolation_; }
9496
bool& extrapolation() { return extrapolation_; }
9597
QuantLib::Exercise::Type& exerciseStyle() { return exerciseStyle_; }
9698

@@ -110,6 +112,7 @@ class EquityCurveConfig : public CurveConfig {
110112
string dayCountID_;
111113
string divInterpVariable_;
112114
string divInterpMethod_;
115+
bool dividendExtrapolation_;
113116
bool extrapolation_;
114117
QuantLib::Exercise::Type exerciseStyle_;
115118
};

OREData/ored/configuration/equityvolcurveconfig.cpp

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,27 @@ namespace data {
2929

3030
EquityVolatilityCurveConfig::EquityVolatilityCurveConfig(
3131
const string& curveID, const string& curveDescription, const string& currency,
32-
const vector<boost::shared_ptr<VolatilityConfig>>& volatilityConfig, const string& dayCounter,
33-
const string& calendar, const OneDimSolverConfig& solverConfig, const boost::optional<bool>& preferOutOfTheMoney)
32+
const vector<boost::shared_ptr<VolatilityConfig>>& volatilityConfig, const string& equityId,
33+
const string& dayCounter, const string& calendar, const OneDimSolverConfig& solverConfig,
34+
const boost::optional<bool>& preferOutOfTheMoney)
3435
: CurveConfig(curveID, curveDescription), ccy_(currency), volatilityConfig_(volatilityConfig),
35-
dayCounter_(dayCounter), calendar_(calendar), solverConfig_(solverConfig),
36+
equityId_(equityId), dayCounter_(dayCounter), calendar_(calendar), solverConfig_(solverConfig),
3637
preferOutOfTheMoney_(preferOutOfTheMoney) {
3738
populateQuotes();
3839
populateRequiredCurveIds();
3940
}
4041

41-
EquityVolatilityCurveConfig::EquityVolatilityCurveConfig(const string& curveID, const string& curveDescription,
42-
const string& currency,
43-
const boost::shared_ptr<VolatilityConfig>& volatilityConfig,
44-
const string& dayCounter, const string& calendar,
45-
const OneDimSolverConfig& solverConfig,
46-
const boost::optional<bool>& preferOutOfTheMoney)
42+
EquityVolatilityCurveConfig::EquityVolatilityCurveConfig(
43+
const string& curveID, const string& curveDescription, const string& currency,
44+
const boost::shared_ptr<VolatilityConfig>& volatilityConfig, const string& equityId,
45+
const string& dayCounter, const string& calendar, const OneDimSolverConfig& solverConfig,
46+
const boost::optional<bool>& preferOutOfTheMoney)
4747
: EquityVolatilityCurveConfig(curveID, curveDescription, currency,
48-
std::vector<boost::shared_ptr<VolatilityConfig>>{volatilityConfig}, dayCounter,
49-
calendar, solverConfig, preferOutOfTheMoney) {}
48+
std::vector<boost::shared_ptr<VolatilityConfig>>{volatilityConfig}, equityId, dayCounter,
49+
calendar, solverConfig, preferOutOfTheMoney) {}
5050

5151
const string EquityVolatilityCurveConfig::quoteStem(const string& volType) const {
52-
return "EQUITY_OPTION/" + volType + "/" + curveID_ + "/" + ccy_ + "/";
52+
return "EQUITY_OPTION/" + volType + "/" + equityId() + "/" + ccy_ + "/";
5353
}
5454

5555
void EquityVolatilityCurveConfig::populateQuotes() {
@@ -97,6 +97,7 @@ void EquityVolatilityCurveConfig::fromXML(XMLNode* node) {
9797

9898
curveID_ = XMLUtils::getChildValue(node, "CurveId", true);
9999
curveDescription_ = XMLUtils::getChildValue(node, "CurveDescription", true);
100+
equityId_ = XMLUtils::getChildValue(node, "EquityId", false);
100101
ccy_ = XMLUtils::getChildValue(node, "Currency", true);
101102

102103
calendar_ = XMLUtils::getChildValue(node, "Calendar", false);
@@ -138,13 +139,13 @@ void EquityVolatilityCurveConfig::fromXML(XMLNode* node) {
138139
"Dimension ATM, but multiple strikes provided for EquityVolatility " << curveID_);
139140
// if ATM create VolatilityCurveConfig which requires quotes to be provided
140141
vector<string> quotes(expiries.size());
141-
string quoteStem = "EQUITY_OPTION/RATE_LNVOL/" + curveID_ + "/" + ccy_ + "/";
142+
string stem = quoteStem("RATE_LNVOL");
142143
if (expiries.size() == 1 && expiries.front() == "*") {
143-
quotes[0] = (quoteStem + "*");
144+
quotes[0] = (stem + "*");
144145
} else {
145146
Size i = 0;
146147
for (auto ex : expiries) {
147-
quotes[i] = (quoteStem + ex + "/ATMF");
148+
quotes[i] = (stem + ex + "/ATMF");
148149
i++;
149150
}
150151
}
@@ -178,6 +179,7 @@ XMLNode* EquityVolatilityCurveConfig::toXML(XMLDocument& doc) {
178179

179180
XMLUtils::addChild(doc, node, "CurveId", curveID_);
180181
XMLUtils::addChild(doc, node, "CurveDescription", curveDescription_);
182+
XMLUtils::addChild(doc, node, "EquityId", equityId_);
181183
XMLUtils::addChild(doc, node, "Currency", ccy_);
182184
XMLUtils::addChild(doc, node, "DayCounter", dayCounter_);
183185

OREData/ored/configuration/equityvolcurveconfig.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,13 @@ class EquityVolatilityCurveConfig : public CurveConfig {
5353
//! Detailed constructor
5454
EquityVolatilityCurveConfig(const string& curveID, const string& curveDescription, const string& currency,
5555
const std::vector<boost::shared_ptr<VolatilityConfig>>& volatilityConfig,
56+
const string& equityId = string(),
5657
const string& dayCounter = "A365", const string& calendar = "NullCalendar",
5758
const OneDimSolverConfig& solverConfig = OneDimSolverConfig(),
5859
const boost::optional<bool>& preferOutOfTheMoney = boost::none);
5960
EquityVolatilityCurveConfig(const string& curveID, const string& curveDescription, const string& currency,
6061
const boost::shared_ptr<VolatilityConfig>& volatilityConfig,
62+
const string& equityId = string(),
6163
const string& dayCounter = "A365", const string& calendar = "NullCalendar",
6264
const OneDimSolverConfig& solverConfig = OneDimSolverConfig(),
6365
const boost::optional<bool>& preferOutOfTheMoney = boost::none);
@@ -71,6 +73,7 @@ class EquityVolatilityCurveConfig : public CurveConfig {
7173

7274
//! \name Inspectors
7375
//@{
76+
const string& equityId() const { return equityId_.empty() ? curveID_ : equityId_; }
7477
const string& ccy() const { return parseCurrencyWithMinors(ccy_).code(); }
7578
const string& dayCounter() const { return dayCounter_; }
7679
const string& calendar() const { return calendar_; }
@@ -96,6 +99,7 @@ class EquityVolatilityCurveConfig : public CurveConfig {
9699

97100
string ccy_;
98101
std::vector<boost::shared_ptr<VolatilityConfig>> volatilityConfig_;
102+
string equityId_;
99103
string dayCounter_;
100104
string calendar_;
101105
OneDimSolverConfig solverConfig_;

OREData/ored/marketdata/equitycurve.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <ql/termstructures/yield/flatforward.hpp>
2727
#include <ql/termstructures/yield/zerocurve.hpp>
2828
#include <qle/termstructures/equityforwardcurvestripper.hpp>
29+
#include <qle/termstructures/flatforwarddividendcurve.hpp>
2930
#include <qle/termstructures/optionpricesurface.hpp>
3031

3132
#include <ored/utilities/parsers.hpp>
@@ -412,11 +413,11 @@ EquityCurve::EquityCurve(Date asof, EquityCurveSpec spec, const Loader& loader,
412413
dividendDiscountFactors.push_back(std::exp(-dividendRates[i] * t));
413414
}
414415

415-
boost::shared_ptr<YieldTermStructure> divCurve;
416+
boost::shared_ptr<YieldTermStructure> baseDivCurve;
416417
// Build Dividend Term Structure
417418
if (dividendRates.size() == 1) {
418419
// We only have 1 quote so we build a flat curve
419-
divCurve.reset(new FlatForward(asof, dividendRates[0], dc_));
420+
baseDivCurve.reset(new FlatForward(asof, dividendRates[0], dc_));
420421
} else {
421422
// Build a ZeroCurve
422423
Size n = terms_.size();
@@ -439,14 +440,25 @@ EquityCurve::EquityCurve(Date asof, EquityCurveSpec spec, const Loader& loader,
439440
std::exp(-rates.back() * maxTime)); // flat zero extrapolation used to imply dividend DF
440441
}
441442
if (dividendInterpVariable_ == YieldCurve::InterpolationVariable::Zero) {
442-
divCurve = zerocurve(dates, rates, dc_, dividendInterpMethod_);
443+
baseDivCurve = zerocurve(dates, rates, dc_, dividendInterpMethod_);
443444
} else if (dividendInterpVariable_ == YieldCurve::InterpolationVariable::Discount) {
444-
divCurve = discountcurve(dates, discounts, dc_, dividendInterpMethod_);
445+
baseDivCurve = discountcurve(dates, discounts, dc_, dividendInterpMethod_);
445446
} else {
446447
QL_FAIL("Unsupported interpolation variable for dividend yield curve");
447448
}
449+
}
450+
451+
boost::shared_ptr<YieldTermStructure> divCurve;
452+
if (config->extrapolation()) {
453+
divCurve = baseDivCurve;
448454
divCurve->enableExtrapolation();
455+
} else {
456+
divCurve = QuantLib::ext::make_shared<FlatForwardDividendCurve>(
457+
asof, Handle<YieldTermStructure>(baseDivCurve), forecastYieldTermStructure);
458+
if (config->dividendExtrapolation())
459+
divCurve->enableExtrapolation();
449460
}
461+
450462
dividendYieldTermStructure = Handle<YieldTermStructure>(divCurve);
451463

452464
equityIndex_ =

0 commit comments

Comments
 (0)