Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
fxvolcurveconfig.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 Quaternion Risk Management Ltd
3 Copyright (C) 2024 Skandinaviska Enskilda Banken AB (publ)
4 All rights reserved.
5
6 This file is part of ORE, a free-software/open-source library
7 for transparent pricing and risk analysis - http://opensourcerisk.org
8
9 ORE is free software: you can redistribute it and/or modify it
10 under the terms of the Modified BSD License. You should have received a
11 copy of the license along with this program.
12 The license is also available online at <http://opensourcerisk.org>
13
14 This program is distributed on the basis that it will form a useful
15 contribution to risk analytics and model standardisation, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
20#include <boost/algorithm/string.hpp>
24#include <ql/errors.hpp>
25
27
28namespace ore {
29namespace data {
30
31FXVolatilityCurveConfig::FXVolatilityCurveConfig(const string& curveID, const string& curveDescription,
32 const Dimension& dimension, const vector<string>& expiries,
33 const vector<string>& strikes, const string& fxSpotID,
34 const string& fxForeignCurveID, const string& fxDomesticCurveID,
35 const DayCounter& dayCounter, const Calendar& calendar,
36 const SmileInterpolation& interp, const string& conventionsID,
37 const std::vector<Size>& smileDelta, const string& smileExtrapolation)
38 : CurveConfig(curveID, curveDescription), dimension_(dimension), expiries_(expiries), dayCounter_(dayCounter),
39 calendar_(calendar), fxSpotID_(fxSpotID), fxForeignYieldCurveID_(fxForeignCurveID),
40 fxDomesticYieldCurveID_(fxDomesticCurveID), conventionsID_(conventionsID), smileDelta_(smileDelta),
41 smileInterpolation_(interp), smileExtrapolation_(smileExtrapolation) {
43}
44
45FXVolatilityCurveConfig::FXVolatilityCurveConfig(const string& curveID, const string& curveDescription,
46 const Dimension& dimension, const string& baseVolatility1,
47 const string& baseVolatility2, const string& fxIndexTag)
48 : CurveConfig(curveID, curveDescription), dimension_(dimension), baseVolatility1_(baseVolatility1),
49 baseVolatility2_(baseVolatility2), fxIndexTag_(fxIndexTag) {
51}
52
53const vector<string>& FXVolatilityCurveConfig::quotes() {
54 if (quotes_.size() == 0) {
55 vector<string> tokens;
56 boost::split(tokens, fxSpotID(), boost::is_any_of("/"));
57 QL_REQUIRE(tokens.size() == 3, "Expected 3 tokens FX/CCY1/CCY2 in fxSpotID (" << fxSpotID() << ")");
58 quotes_.push_back("FX/RATE/" + tokens[1] + "/" + tokens[2]);
59 string base = "FX_OPTION/RATE_LNVOL/" + tokens[1] + "/" + tokens[2] + "/";
60 for (auto e : expiries_) {
61 quotes_.push_back(base + e + "/ATM");
63 for (auto const& d : smileDelta_) {
64 quotes_.push_back(base + e + "/" + to_string(d) + "RR");
65 quotes_.push_back(base + e + "/" + to_string(d) + "BF");
66 }
68 for (auto d : deltas_) {
69 quotes_.push_back(base + e + "/" + d);
70 }
71 }
72 }
73 }
74 return quotes_;
75}
76
78 XMLUtils::checkNode(node, "FXVolatility");
79
80 curveID_ = XMLUtils::getChildValue(node, "CurveId", true);
81 curveDescription_ = XMLUtils::getChildValue(node, "CurveDescription", true);
82 string dim = XMLUtils::getChildValue(node, "Dimension", true);
83 string cal = XMLUtils::getChildValue(node, "Calendar");
84 string smileInterp = XMLUtils::getChildValue(node, "SmileInterpolation");
85
86 fxSpotID_ = XMLUtils::getChildValue(node, "FXSpotID", true);
87
88 vector<string> tokens;
89 boost::split(tokens, fxSpotID_, boost::is_any_of("/"));
90 QL_REQUIRE(tokens.size() == 3, "Expected 3 tokens FX/CCY1/CCY2 in fxSpotID (" << fxSpotID_ << ")");
91 if (cal == "") {
92 cal = tokens[1] + "," + tokens[2];
93 }
95
96 string dc = XMLUtils::getChildValue(node, "DayCounter");
97 if (dc == "")
98 dc = "A365";
100
101 if (dim == "ATMTriangulated") {
103 baseVolatility1_ = XMLUtils::getChildValue(node, "BaseVolatility1", true);
104 baseVolatility2_ = XMLUtils::getChildValue(node, "BaseVolatility2", true);
105
106 string fxIndexTag = XMLUtils::getChildValue(node, "FXIndexTag");
107 if (fxIndexTag == "")
108 fxIndexTag = "GENERIC";
110 } else {
111 if (dim == "ATM") {
113 } else if (dim == "Smile") {
114
115 conventionsID_ = XMLUtils::getChildValue(node, "Conventions", false);
116 string smileType = XMLUtils::getChildValue(node, "SmileType");
117 if (smileType == "" || smileType == "VannaVolga") {
119
120 // only read smile interpolation method if dimension is smile.
121 if (smileInterp == "") {
122 smileInterpolation_ = SmileInterpolation::VannaVolga2; // default to VannaVolga 2nd approximation
123 } else if (smileInterp == "VannaVolga1") {
125 } else if (smileInterp == "VannaVolga2") {
127 } else {
128 QL_FAIL("SmileInterpolation " << smileInterp << " not supported");
129 }
130
131 string sDelta = XMLUtils::getChildValue(node, "SmileDelta");
132 if (sDelta == "")
133 smileDelta_ = {25};
134 else
135 smileDelta_ = parseListOfValues<Size>(sDelta, &parseInteger);
136 } else if (smileType == "Delta") {
138 // only read smile interpolation and extrapolation method if dimension is smile.
139 if (smileInterp == "" || smileInterp == "Linear") {
141 } else if (smileInterp == "Cubic") {
143 } else {
144 QL_FAIL("SmileInterpolation " << smileInterp << " not supported");
145 }
146
147 smileExtrapolation_ = XMLUtils::getChildValue(node, "SmileExtrapolation", false, "Flat");
148
149 deltas_ = XMLUtils::getChildrenValuesAsStrings(node, "Deltas", true);
150
151 // check that these are valid deltas
152 for (auto d : deltas_) {
153 QL_REQUIRE(d == "ATM" || d.back() == 'P' || d.back() == 'C',
154 "this is not a valid value for delta, " << d);
155 if (d != "ATM") {
156 parseReal(d.substr(0, d.size() - 1));
157 }
158 }
159 } else if (smileType == "BFRR") {
161 if (smileInterp == "" || smileInterp == "Cubic") {
163 } else if (smileInterp == "Linear") {
165 } else {
166 QL_FAIL("SmileInterpolation " << smileInterp << " not supported");
167 }
168 string sDelta = XMLUtils::getChildValue(node, "SmileDelta");
169 if (sDelta == "")
170 smileDelta_ = {10, 25};
171 else
172 smileDelta_ = parseListOfValues<Size>(sDelta, &parseInteger);
173 } else if (smileType == "Absolute") {
175 if (smileInterp == "" || smileInterp == "Cubic") {
177 } else if (smileInterp == "Linear") {
179 } else {
180 QL_FAIL("SmileInterpolation " << smileInterp << " not supported");
181 }
182 } else {
183 QL_FAIL("SmileType '" << smileType << "' not supported, expected VannaVolga, Delta, BFRR");
184 }
185 } else {
186 QL_FAIL("Dimension " << dim << " not supported yet");
187 }
188 expiries_ = XMLUtils::getChildrenValuesAsStrings(node, "Expiries", true);
189
192 fxForeignYieldCurveID_ = XMLUtils::getChildValue(node, "FXForeignCurveID", curvesRequired);
193 fxDomesticYieldCurveID_ = XMLUtils::getChildValue(node, "FXDomesticCurveID", curvesRequired);
194 }
195
196 if (auto tmp = XMLUtils::getChildNode(node, "Report")) {
198 }
199
201}
202
204 XMLNode* node = doc.allocNode("FXVolatility");
205
206 XMLUtils::addChild(doc, node, "CurveId", curveID_);
207 XMLUtils::addChild(doc, node, "CurveDescription", curveDescription_);
208 if (dimension_ == Dimension::ATM) {
209 XMLUtils::addChild(doc, node, "Dimension", "ATM");
211 XMLUtils::addChild(doc, node, "Dimension", "ATMTriangulated");
212 XMLUtils::addChild(doc, node, "FXSpotID", fxSpotID_);
213 XMLUtils::addChild(doc, node, "FXIndexTag", fxIndexTag_);
214 XMLUtils::addChild(doc, node, "BaseVolatility1", baseVolatility1_);
215 XMLUtils::addChild(doc, node, "BaseVolatility2", baseVolatility2_);
216
217 return node;
219 XMLUtils::addChild(doc, node, "Dimension", "Smile");
220 XMLUtils::addChild(doc, node, "SmileType", "VannaVolga");
221 // only write smile interpolation if dimension is smile
223 XMLUtils::addChild(doc, node, "SmileInterpolation", "VannaVolga1");
225 XMLUtils::addChild(doc, node, "SmileInterpolation", "VannaVolga2");
226 } else {
227 QL_FAIL("Unknown SmileInterpolation in FXVolatilityCurveConfig::toXML()");
228 }
229 XMLUtils::addGenericChildAsList(doc, node, "SmileDelta", deltas_);
230 XMLUtils::addChild(doc, node, "Conventions", to_string(conventionsID_));
231 } else if (dimension_ == Dimension::SmileDelta) {
232 XMLUtils::addChild(doc, node, "Dimension", "Smile");
233 XMLUtils::addChild(doc, node, "SmileType", "Delta");
234 // only write smile interpolation if dimension is smile
236 XMLUtils::addChild(doc, node, "SmileInterpolation", "Linear");
238 XMLUtils::addChild(doc, node, "SmileInterpolation", "Cubic");
239 } else {
240 QL_FAIL("Unknown SmileInterpolation in FXVolatilityCurveConfig::toXML()");
241 }
242 if (!smileExtrapolation_.empty())
243 XMLUtils::addChild(doc, node, "SmileExtrapolation", smileExtrapolation_);
244 XMLUtils::addChild(doc, node, "Conventions", to_string(conventionsID_));
245 XMLUtils::addGenericChildAsList(doc, node, "Deltas", deltas_);
246 } else if (dimension_ == Dimension::SmileBFRR) {
247 XMLUtils::addChild(doc, node, "Dimension", "Smile");
248 XMLUtils::addChild(doc, node, "SmileType", "BFRR");
250 XMLUtils::addChild(doc, node, "SmileInterpolation", "Linear");
252 XMLUtils::addChild(doc, node, "SmileInterpolation", "Cubic");
253 } else {
254 QL_FAIL("Unknown SmileInterpolation in FXVolatilityCurveConfig::toXML()");
255 }
256 XMLUtils::addGenericChildAsList(doc, node, "SmileDelta", smileDelta_);
257 XMLUtils::addChild(doc, node, "Conventions", to_string(conventionsID_));
258 } else if (dimension_ == Dimension::SmileAbsolute) {
259 XMLUtils::addChild(doc, node, "Dimension", "Smile");
260 XMLUtils::addChild(doc, node, "SmileType", "Absolute");
262 XMLUtils::addChild(doc, node, "SmileInterpolation", "Linear");
264 XMLUtils::addChild(doc, node, "SmileInterpolation", "Cubic");
265 } else {
266 QL_FAIL("Unknown SmileInterpolation in FXVolatilityCurveConfig::toXML()");
267 }
268 XMLUtils::addChild(doc, node, "Conventions", to_string(conventionsID_));
269 } else {
270 QL_FAIL("Unknown Dimension in FXVolatilityCurveConfig::toXML()");
271 }
272 XMLUtils::addGenericChildAsList(doc, node, "Expiries", expiries_);
273 XMLUtils::addChild(doc, node, "FXSpotID", fxSpotID_);
274 if (!fxForeignYieldCurveID_.empty())
275 XMLUtils::addChild(doc, node, "FXForeignCurveID", fxForeignYieldCurveID_);
276 if (!fxDomesticYieldCurveID_.empty())
277 XMLUtils::addChild(doc, node, "FXDomesticCurveID", fxDomesticYieldCurveID_);
278 XMLUtils::addChild(doc, node, "Calendar", to_string(calendar_));
279 XMLUtils::addChild(doc, node, "DayCounter", to_string(dayCounter_));
281
282 return node;
283}
284
286 if (!fxDomesticYieldCurveID_.empty() && !fxForeignYieldCurveID_.empty()) {
287 std::vector<string> domTokens, forTokens;
288 split(domTokens, fxDomesticYieldCurveID_, boost::is_any_of("/"));
289 split(forTokens, fxForeignYieldCurveID_, boost::is_any_of("/"));
290
291 if (domTokens.size() == 3 && domTokens[0] == "Yield") {
293 } else if (domTokens.size() == 1) {
295 } else {
296 QL_FAIL("Cannot determine the required domestic yield curve for fx vol curve " << curveID_);
297 }
298
299 if (forTokens.size() == 3 && forTokens[0] == "Yield") {
301 } else if (forTokens.size() == 1) {
303 } else {
304 QL_FAIL("Cannot determine the required foreign yield curve for fx vol curve " << curveID_);
305 }
306 }
307
311
312 vector<string> tokens;
313 boost::split(tokens, fxSpotID_, boost::is_any_of("/"));
314 QL_REQUIRE(tokens.size() == 3, "unexpected fxSpot format: " << fxSpotID_);
315 auto forTarget = tokens[1];
316 auto domTarget = tokens[2];
317
318 // we need to include inverse ccy pairs as well
319 QL_REQUIRE(baseVolatility1_.size() == 6, "invalid ccy pair length");
320 QL_REQUIRE(baseVolatility2_.size() == 6, "invalid ccy pair length");
321 auto forBase1 = baseVolatility1_.substr(0, 3);
322 auto domBase1 = baseVolatility1_.substr(3);
323 auto forBase2 = baseVolatility2_.substr(0, 3);
324 auto domBase2 = baseVolatility2_.substr(3);
325
326 requiredCurveIds_[CurveSpec::CurveType::FXVolatility].insert(domBase1 + forBase1);
327 requiredCurveIds_[CurveSpec::CurveType::FXVolatility].insert(domBase2 + forBase2);
328
329 // we need to establish the common currency between the two pairs to include the correlations
330 std::string baseCcy = "";
331 if (forBase1 == forBase2 || forBase1 == domBase2) {
332 baseCcy = forBase1;
333 } else {
334 QL_REQUIRE(domBase1 == forBase2 || domBase1 == domBase2, "no common currency found for baseVolatilities");
335 baseCcy = domBase1;
336 }
337
338 // straight pair
339 std::string forIndex = "FX-" + fxIndexTag_ + "-" + forTarget + "-" + baseCcy;
340 std::string domIndex = "FX-" + fxIndexTag_ + "-" + domTarget + "-" + baseCcy;
341 requiredCurveIds_[CurveSpec::CurveType::Correlation].insert(forIndex + "&" + domIndex);
342 // inverse pair
343 requiredCurveIds_[CurveSpec::CurveType::Correlation].insert(domIndex + "&" + forIndex);
344 // inverse fx index1
345 std::string forIndexInverse = "FX-" + fxIndexTag_ + "-" + baseCcy + "-" + forTarget;
346 requiredCurveIds_[CurveSpec::CurveType::Correlation].insert(forIndexInverse + "&" + domIndex);
347 requiredCurveIds_[CurveSpec::CurveType::Correlation].insert(domIndex + "&" + forIndexInverse);
348 // inverse fx index2
349 std::string domIndexInverse = "FX-" + fxIndexTag_ + "-" + baseCcy + "-" + domTarget;
350 requiredCurveIds_[CurveSpec::CurveType::Correlation].insert(forIndex + "&" + domIndexInverse);
351 requiredCurveIds_[CurveSpec::CurveType::Correlation].insert(domIndexInverse + "&" + forIndex);
352 // both fx indices inverted
353 requiredCurveIds_[CurveSpec::CurveType::Correlation].insert(forIndexInverse + "&" + domIndexInverse);
354 requiredCurveIds_[CurveSpec::CurveType::Correlation].insert(domIndexInverse + "&" + forIndexInverse);
355 }
356}
357} // namespace data
358} // namespace ore
Base curve configuration.
Definition: curveconfig.hpp:41
vector< string > quotes_
Definition: curveconfig.hpp:74
map< CurveSpec::CurveType, set< string > > requiredCurveIds_
Definition: curveconfig.hpp:75
void fromXML(XMLNode *node) override
XMLNode * toXML(XMLDocument &doc) const override
const vector< string > & quotes() override
Return all the market quotes required for this config.
FXVolatilityCurveConfig()
Default constructor.
Dimension
supported volatility structure types
void fromXML(XMLNode *node) override
XMLNode * toXML(XMLDocument &doc) const override
Small XML Document wrapper class.
Definition: xmlutils.hpp:65
XMLNode * allocNode(const string &nodeName)
util functions that wrap rapidxml
Definition: xmlutils.cpp:132
XML Utilities Class.
Definition: xmlutils.hpp:119
static void addGenericChildAsList(XMLDocument &doc, XMLNode *n, const string &name, const vector< T > &values, const string &attrName="", const string &attr="")
Definition: xmlutils.hpp:144
static void checkNode(XMLNode *n, const string &expectedName)
Definition: xmlutils.cpp:175
static string getChildValue(XMLNode *node, const string &name, bool mandatory=false, const string &defaultValue=string())
Definition: xmlutils.cpp:277
static XMLNode * getChildNode(XMLNode *n, const string &name="")
Definition: xmlutils.cpp:387
static vector< string > getChildrenValuesAsStrings(XMLNode *node, const string &name, bool mandatory=false)
Definition: xmlutils.cpp:342
static XMLNode * addChild(XMLDocument &doc, XMLNode *n, const string &name)
Definition: xmlutils.cpp:181
static void appendNode(XMLNode *parent, XMLNode *child)
Definition: xmlutils.cpp:406
FX volatility curve configuration classes.
Calendar parseCalendar(const string &s)
Convert text to QuantLib::Calendar.
Definition: parsers.cpp:157
Real parseReal(const string &s)
Convert text to Real.
Definition: parsers.cpp:112
Integer parseInteger(const string &s)
Convert text to QuantLib::Integer.
Definition: parsers.cpp:136
DayCounter parseDayCounter(const string &s)
Convert text to QuantLib::DayCounter.
Definition: parsers.cpp:209
@ data
Definition: log.hpp:77
Calendar calendar
Definition: utilities.cpp:441
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Map text representations to QuantLib/QuantExt types.
string conversion utilities