diff --git a/Docs/UserGuide/parameterisation/curveconfig.tex b/Docs/UserGuide/parameterisation/curveconfig.tex
index 0b8b00d273..46a2675e25 100644
--- a/Docs/UserGuide/parameterisation/curveconfig.tex
+++ b/Docs/UserGuide/parameterisation/curveconfig.tex
@@ -162,6 +162,7 @@ \subsubsection{FX Volatility Structures}
A365
US,JP
USD-JPY-FXOPTION
+ UseInterpolator
\end{minted}
\caption{FX option volatility configuration Smile / Delta}
@@ -273,6 +274,10 @@ \subsubsection{FX Volatility Structures}
data input. See \ref{sss:fx_option_conv} for more details.
\item BaseVolatility1: For `ATMTriangulated' this denotes one of the surfaces we want to triangulate from
\item BaseVolatility2: For `ATMTriangulated' this denotes one of the surfaces we want to triangulate from
+\item SmileExtrapolation [Optional]: Applicable only in case of SmileType Delta. Indicates the extrapolation in the
+smile direction. The allowable values are None, UseInterpolator (or Linear) and Flat. Both Flat and None give flat
+extrapolation. UseInterpolator indicates that the configured interpolation should be continued in the strike
+direction in order to extrapolate. Default if not provided value is Flat.
\end{itemize}
\subsubsection{Equity Curve Structures}
diff --git a/OREData/ored/configuration/fxvolcurveconfig.cpp b/OREData/ored/configuration/fxvolcurveconfig.cpp
index 7e2852b5c1..a9a405d784 100644
--- a/OREData/ored/configuration/fxvolcurveconfig.cpp
+++ b/OREData/ored/configuration/fxvolcurveconfig.cpp
@@ -1,5 +1,6 @@
/*
Copyright (C) 2016 Quaternion Risk Management Ltd
+ Copyright (C) 2024 Skandinaviska Enskilda Banken AB (publ)
All rights reserved.
This file is part of ORE, a free-software/open-source library
@@ -33,11 +34,11 @@ FXVolatilityCurveConfig::FXVolatilityCurveConfig(const string& curveID, const st
const string& fxForeignCurveID, const string& fxDomesticCurveID,
const DayCounter& dayCounter, const Calendar& calendar,
const SmileInterpolation& interp, const string& conventionsID,
- const std::vector& smileDelta)
+ const std::vector& smileDelta, const string& smileExtrapolation)
: CurveConfig(curveID, curveDescription), dimension_(dimension), expiries_(expiries), dayCounter_(dayCounter),
calendar_(calendar), fxSpotID_(fxSpotID), fxForeignYieldCurveID_(fxForeignCurveID),
fxDomesticYieldCurveID_(fxDomesticCurveID), conventionsID_(conventionsID), smileDelta_(smileDelta),
- smileInterpolation_(interp) {
+ smileInterpolation_(interp), smileExtrapolation_(smileExtrapolation) {
populateRequiredCurveIds();
}
@@ -134,7 +135,7 @@ void FXVolatilityCurveConfig::fromXML(XMLNode* node) {
smileDelta_ = parseListOfValues(sDelta, &parseInteger);
} else if (smileType == "Delta") {
dimension_ = Dimension::SmileDelta;
- // only read smile interpolation method if dimension is smile.
+ // only read smile interpolation and extrapolation method if dimension is smile.
if (smileInterp == "" || smileInterp == "Linear") {
smileInterpolation_ = SmileInterpolation::Linear;
} else if (smileInterp == "Cubic") {
@@ -142,6 +143,9 @@ void FXVolatilityCurveConfig::fromXML(XMLNode* node) {
} else {
QL_FAIL("SmileInterpolation " << smileInterp << " not supported");
}
+
+ smileExtrapolation_ = XMLUtils::getChildValue(node, "SmileExtrapolation", false, "Flat");
+
deltas_ = XMLUtils::getChildrenValuesAsStrings(node, "Deltas", true);
// check that these are valid deltas
@@ -235,6 +239,8 @@ XMLNode* FXVolatilityCurveConfig::toXML(XMLDocument& doc) {
} else {
QL_FAIL("Unknown SmileInterpolation in FXVolatilityCurveConfig::toXML()");
}
+ if (!smileExtrapolation_.empty())
+ XMLUtils::addChild(doc, node, "SmileExtrapolation", smileExtrapolation_);
XMLUtils::addChild(doc, node, "Conventions", to_string(conventionsID_));
XMLUtils::addGenericChildAsList(doc, node, "Deltas", deltas_);
} else if (dimension_ == Dimension::SmileBFRR) {
diff --git a/OREData/ored/configuration/fxvolcurveconfig.hpp b/OREData/ored/configuration/fxvolcurveconfig.hpp
index f16ecd05d1..df2700ce98 100644
--- a/OREData/ored/configuration/fxvolcurveconfig.hpp
+++ b/OREData/ored/configuration/fxvolcurveconfig.hpp
@@ -1,5 +1,6 @@
/*
Copyright (C) 2016 Quaternion Risk Management Ltd
+ Copyright (C) 2024 Skandinaviska Enskilda Banken AB (publ)
All rights reserved.
This file is part of ORE, a free-software/open-source library
@@ -72,7 +73,8 @@ class FXVolatilityCurveConfig : public CurveConfig {
const DayCounter& dayCounter = QuantLib::Actual365Fixed(),
const Calendar& calendar = QuantLib::TARGET(),
const SmileInterpolation& interp = SmileInterpolation::VannaVolga2,
- const string& conventionsID = "", const std::vector& smileDelta = {25});
+ const string& conventionsID = "", const std::vector& smileDelta = {25},
+ const string& smileExtrapolation = "Flat");
FXVolatilityCurveConfig(const string& curveID, const string& curveDescription, const Dimension& dimension,
const string& baseVolatility1, const string& baseVolatility2,
@@ -98,6 +100,7 @@ class FXVolatilityCurveConfig : public CurveConfig {
const string& fxForeignYieldCurveID() const { return fxForeignYieldCurveID_; }
const string& fxDomesticYieldCurveID() const { return fxDomesticYieldCurveID_; }
const SmileInterpolation& smileInterpolation() const { return smileInterpolation_; }
+ const std::string& smileExtrapolation() const { return smileExtrapolation_; }
const string& conventionsID() const { return conventionsID_; }
const std::vector& smileDelta() const { return smileDelta_; }
const vector& quotes() override;
@@ -111,6 +114,7 @@ class FXVolatilityCurveConfig : public CurveConfig {
//@{
Dimension& dimension() { return dimension_; }
SmileInterpolation& smileInterpolation() { return smileInterpolation_; }
+ string& smileExtrapolation() { return smileExtrapolation_; }
vector& deltas() { return deltas_; }
DayCounter& dayCounter() { return dayCounter_; }
Calendar& calendar() { return calendar_; }
@@ -140,6 +144,7 @@ class FXVolatilityCurveConfig : public CurveConfig {
std::vector smileDelta_;
std::set requiredYieldCurveIDs_;
SmileInterpolation smileInterpolation_;
+ string smileExtrapolation_;
string baseVolatility1_;
string baseVolatility2_;
string fxIndexTag_;
diff --git a/OREData/ored/marketdata/fxvolcurve.cpp b/OREData/ored/marketdata/fxvolcurve.cpp
index cb22a20d98..7c4468ecf8 100644
--- a/OREData/ored/marketdata/fxvolcurve.cpp
+++ b/OREData/ored/marketdata/fxvolcurve.cpp
@@ -217,6 +217,19 @@ void FXVolCurve::buildSmileDeltaCurve(Date asof, FXVolatilityCurveSpec spec, con
QL_FAIL("Delta FX vol surface: invalid interpolation, expected Linear, Cubic");
}
+ bool flatExtrapolation = true;
+ auto smileExtrapType = parseExtrapolation(config->smileExtrapolation());
+ if (smileExtrapType == Extrapolation::UseInterpolator) {
+ DLOG("Smile extrapolation switched to using interpolator.");
+ flatExtrapolation = false;
+ } else if (smileExtrapType == Extrapolation::None) {
+ DLOG("Smile extrapolation cannot be turned off on its own so defaulting to flat.");
+ } else if (smileExtrapType == Extrapolation::Flat) {
+ DLOG("Smile extrapolation has been set to flat.");
+ } else {
+ DLOG("Smile extrapolation " << smileExtrapType << " not expected so defaulting to flat.");
+ }
+
// daycounter used for interpolation in time.
// TODO: push into conventions or config
DayCounter dc = config->dayCounter();
@@ -227,7 +240,8 @@ void FXVolCurve::buildSmileDeltaCurve(Date asof, FXVolatilityCurveSpec spec, con
[](const std::pair& x) { return x.first; });
vol_ = boost::make_shared(
asof, dates, putDeltasNum, callDeltasNum, hasATM, blackVolMatrix, dc, cal, fxSpot_, domYts_, forYts_,
- deltaType_, atmType_, boost::none, switchTenor_, longTermDeltaType_, longTermAtmType_, boost::none, interp);
+ deltaType_, atmType_, boost::none, switchTenor_, longTermDeltaType_, longTermAtmType_, boost::none, interp,
+ flatExtrapolation);
vol_->enableExtrapolation();
}
diff --git a/xsd/curveconfig.xsd b/xsd/curveconfig.xsd
index 680d618f3a..813215785e 100755
--- a/xsd/curveconfig.xsd
+++ b/xsd/curveconfig.xsd
@@ -159,6 +159,7 @@
+