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 @@ +