Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
multilegoption.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2017 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
27
29
30namespace ore {
31namespace data {
32
33using namespace QuantLib;
34using namespace QuantExt;
35
36namespace {
37struct CcyComp {
38 bool operator()(const Currency& c1, const Currency& c2) const { return c1.code() < c2.code(); }
39};
40} // namespace
41
42std::string CamMcMultiLegOptionEngineBuilder::getCcyValue(const string& s, const string& c, const bool mandatory) {
43 std::string sc = s + "_" + c;
44 auto it = modelParameters_.find(sc);
45 if (it != modelParameters_.end()) {
46 return it->second;
47 }
48 it = modelParameters_.find(s);
49 if (it != modelParameters_.end()) {
50 return it->second;
51 }
52 if (mandatory)
53 QL_FAIL("did not find model parameter " << s << " (when looking for ccy " << c << ")");
54 return "";
55}
56
57QuantLib::ext::shared_ptr<PricingEngine> CamMcMultiLegOptionEngineBuilder::engineImpl(
58 const string& id, const std::vector<Date>& exDates, const Date& maturityDate,
59 const std::vector<Currency>& currencies, const std::vector<Date>& fixingDates,
60 const std::vector<QuantLib::ext::shared_ptr<QuantLib::InterestRateIndex>>& indexes) {
61
62 DLOG("Building multi leg option engine for trade " << id << "...");
63
64 QL_REQUIRE(!currencies.empty(), "CamMcMultiLegOptionEngineBuilder: no currencies given");
65 QL_REQUIRE(fixingDates.size() == indexes.size(), "CamMcMultiLegOptionEngineBuilder: fixing dates size ("
66 << fixingDates.size() << ") must match indexes size ("
67 << indexes.size() << ")");
68
69 std::vector<Handle<YieldTermStructure>> discountCurves;
70 auto irCalibration = parseCalibrationType(modelParameters_.at("IrCalibration"));
71 auto irCalibrationStrategy = parseCalibrationStrategy(modelParameters_.at("IrCalibrationStrategy"));
72 std::vector<std::pair<CalibrationType, CalibrationStrategy>> validCalPairs = {
76
77 auto fxCalibration = parseCalibrationType(modelParameters_.at("FxCalibration"));
79 "CamMcMultiLegOptionEngineBuilder: invalid FxCalibration (" << fxCalibration << ")");
80
81 QL_REQUIRE(std::find(validCalPairs.begin(), validCalPairs.end(),
82 std::make_pair(irCalibration, irCalibrationStrategy)) != validCalPairs.end(),
83 "Calibration (" << irCalibration << ") and CalibrationStrategy (" << irCalibrationStrategy
84 << ") are not allowed in this combination");
85
86 Real tolerance = parseReal(modelParameters_.at("Tolerance"));
87
88 // shift horizon
89
90 Real shiftHorizon = 0.5; // default value
91 if (modelParameters_.find("ShiftHorizon") != modelParameters_.end()) {
92 shiftHorizon = parseReal(modelParameters_.at("ShiftHorizon"));
93 }
94 Date today = Settings::instance().evaluationDate();
95 shiftHorizon = ActualActual(ActualActual::ISDA).yearFraction(today, maturityDate) * shiftHorizon;
96
97 // build calibration basket data
98
99 vector<string> swaptionExStr, swaptionTermStr, swaptionStrikesStr, fxStrikesStr;
100 map<Currency, string, CcyComp> swaptionTermCcyStr; // will be duplicated to a vector below
101 if (irCalibrationStrategy == CalibrationStrategy::CoterminalATM) {
102 DLOG("Building calibration basket from exercise dates");
103 for (Size i = 0; i < exDates.size(); ++i) {
104 swaptionExStr.push_back(ore::data::to_string(QuantLib::io::iso_date(exDates[i])));
105 swaptionTermStr.push_back(ore::data::to_string(QuantLib::io::iso_date(maturityDate)));
106 swaptionStrikesStr.push_back("ATM");
107 fxStrikesStr.push_back("ATMF");
108 DLOG("added exercsie " << swaptionExStr.back() << ", term " << swaptionTermStr.back() << ", strike "
109 << swaptionStrikesStr.back());
110 }
111 } else if (irCalibrationStrategy == CalibrationStrategy::UnderlyingATM) {
112 DLOG("Building calibration basket from underlying fixing dates");
113 // find unique set of fixing dates
114 std::vector<Date> allFixingDates(fixingDates.begin(), fixingDates.end());
115 std::sort(allFixingDates.begin(), allFixingDates.end());
116 auto it = std::unique(allFixingDates.begin(), allFixingDates.end());
117 allFixingDates.resize(it - allFixingDates.begin());
118 for (auto const& d : allFixingDates) {
119 swaptionExStr.push_back(ore::data::to_string(QuantLib::io::iso_date(d)));
120 swaptionTermStr.push_back("1Y"); // default value, if we don't have anything for a ccy
121 swaptionStrikesStr.push_back("ATM");
122 fxStrikesStr.push_back("ATMF");
123 DLOG("added exercise " << swaptionExStr.back() << ", term tbd, strike " << swaptionStrikesStr.back());
124 }
125 // Find a tenor for each currency (there may be several of course, just pick one...)
126 for (auto const& i : indexes) {
127 if (swaptionTermCcyStr.find(i->currency()) == swaptionTermCcyStr.end())
128 swaptionTermCcyStr[i->currency()] = ore::data::to_string(i->tenor());
129 }
130 // Logging
131 for (auto const& k : swaptionTermCcyStr) {
132 DLOG("added term " << k.second << " for ccy " << k.first.code());
133 }
134 } else if (irCalibrationStrategy == CalibrationStrategy::None) {
135 // nothing to do
136 } else {
137 QL_FAIL("IR Calibration Strategy " << irCalibrationStrategy
138 << " not supported by CamMcMultiLegOptionEngineBuilder");
139 }
140
141 // ir components
142
143 std::vector<QuantLib::ext::shared_ptr<IrModelData>> irData;
144 for (Size i = 0; i < currencies.size(); ++i) {
145 DLOG("IR component #" << i << " (" << currencies[i].code() << ")");
146 discountCurves.push_back(market_->discountCurve(currencies[i].code(), configuration(MarketContext::pricing)));
147 Real rev = parseReal(getCcyValue("IrReversion", currencies[i].code(), true));
148 vector<Real> vols =
149 parseListOfValues<Real>(getCcyValue("IrVolatility", currencies[i].code(), true), &parseReal);
150 vector<Real> volTimes =
151 parseListOfValues<Real>(getCcyValue("IrVolatilityTimes", currencies[i].code(), false), &parseReal);
152 QL_REQUIRE(vols.size() == volTimes.size() + 1, "there must be n+1 volatilities ("
153 << vols.size() << ") for n volatility times ("
154 << volTimes.size() << "), for ccy " << currencies[i]);
155 auto reversionType = parseReversionType(getCcyValue("IrReversionType", currencies[i].code(), true));
156 auto volatilityType = parseVolatilityType(getCcyValue("IrVolatilityType", currencies[i].code(), true));
157 auto lgmData = QuantLib::ext::make_shared<IrLgmData>();
158 lgmData->reset();
159 lgmData->ccy() = currencies[i].code();
160 lgmData->calibrateH() = false;
161 lgmData->hParamType() = ParamType::Constant;
162 lgmData->hValues() = {rev};
163 lgmData->reversionType() = reversionType;
164 lgmData->calibrateA() = false;
165 lgmData->aParamType() = ParamType::Piecewise;
166 lgmData->aValues() = vols;
167 lgmData->aTimes() = volTimes;
168 lgmData->volatilityType() = volatilityType;
169 lgmData->calibrationType() = irCalibration;
170 lgmData->shiftHorizon() = i == 0 ? shiftHorizon : 0.0; // only for domestic component
171 lgmData->optionExpiries() = swaptionExStr;
172 // currency specific term or default term
173 if (swaptionTermCcyStr.find(currencies[i]) != swaptionTermCcyStr.end())
174 lgmData->optionTerms() =
175 std::vector<std::string>(swaptionTermStr.size(), swaptionTermCcyStr.at(currencies[i]));
176 else
177 lgmData->optionTerms() = swaptionTermStr;
178 lgmData->optionStrikes() = swaptionStrikesStr;
179 lgmData->calibrateA() = irCalibration == CalibrationType::Bootstrap;
180 irData.push_back(lgmData);
181 }
182
183 // fx components
184
185 std::vector<QuantLib::ext::shared_ptr<FxBsData>> fxData;
186 for (Size i = 1; i < currencies.size(); ++i) {
187 string ccyPair = currencies[i].code() + currencies.front().code();
188 DLOG("FX component # " << (i - 1) << " (" << ccyPair << ")");
189 auto bsData = QuantLib::ext::make_shared<FxBsData>();
190 vector<Real> vols = parseListOfValues<Real>(getCcyValue("FxVolatility", ccyPair, true), &parseReal);
191 vector<Real> volTimes =
192 parseListOfValues<Real>(getCcyValue("FxVolatilityTimes", currencies[i].code(), false), &parseReal);
193 QL_REQUIRE(vols.size() == volTimes.size() + 1, "there must be n+1 FX volatilities ("
194 << vols.size() << ") for n volatility times ("
195 << volTimes.size() << "), for pair " << ccyPair);
196 // cut off parts where we have to extrapolate ?
197 std::vector<string> fxExStr(swaptionExStr);
198 if (!parseBool(getCcyValue("ExtrapolateFxVolatility", ccyPair, true))) {
199 Size n = 0;
200 auto fxvol = market_->fxVol(ccyPair, configuration(MarketContext::pricing));
201 while (n < fxExStr.size() && parseDate(fxExStr[n]) <= fxvol->maxDate())
202 ++n;
203 fxExStr.resize(n);
204 fxStrikesStr.resize(n);
205 }
206 bsData->domesticCcy() = currencies.front().code();
207 bsData->foreignCcy() = currencies[i].code();
208 bsData->calibrationType() = fxCalibration;
209 bsData->calibrateSigma() = fxCalibration == CalibrationType::Bootstrap;
210 bsData->sigmaParamType() = ParamType::Piecewise;
211 bsData->sigmaTimes() = volTimes;
212 bsData->sigmaValues() = vols;
213 bsData->optionExpiries() = fxExStr;
214 bsData->optionStrikes() = fxStrikesStr;
215 fxData.push_back(bsData);
216 }
217
218 // correlations
219
220 DLOG("Setting correlations (IR-IR, IR-FX, FX-FX)");
221 map<CorrelationKey, Handle<Quote>> corr;
222 for (auto const& p : modelParameters_) {
223 if (p.first.substr(0, 5) == "Corr_") {
224 std::vector<std::string> tokens;
225 boost::split(tokens, p.first, boost::is_any_of("_"));
226 QL_REQUIRE(tokens.size() == 3, "CamMcMultiLegOptionEngineBuilder: invalid correlation key "
227 << p.first << " expected 'Corr_Key1_Key2'");
230 corr[std::make_pair(f_1, f_2)] = Handle<Quote>(QuantLib::ext::make_shared<SimpleQuote>(parseReal(p.second)));
231 DLOG("added correlation " << tokens[1] << " " << tokens[2] << " " << p.second);
232 }
233 }
234
235 // build the model
236
237 bool calibrate = globalParameters_.count("Calibrate") == 0 || parseBool(globalParameters_.at("Calibrate"));
238 bool continueOnCalibrationError = globalParameters_.count("ContinueOnCalibrationError") > 0 &&
239 parseBool(globalParameters_.at("ContinueOnCalibrationError"));
240
241 std::string configurationInCcy = configuration(MarketContext::irCalibration);
242 std::string configurationXois = configuration(MarketContext::pricing);
243
244 auto builder = QuantLib::ext::make_shared<CrossAssetModelBuilder>(
245 market_, QuantLib::ext::make_shared<CrossAssetModelData>(irData, fxData, corr, tolerance), configurationInCcy,
246 configurationXois, configurationXois, configurationInCcy, configurationInCcy, configurationXois, !calibrate,
247 continueOnCalibrationError, "", SalvagingAlgorithm::Spectral, id);
248
249 modelBuilders_.insert(std::make_pair(id, builder));
250
251 // build the pricing engine
252
253 auto engine = QuantLib::ext::make_shared<McMultiLegOptionEngine>(
254 builder->model(), parseSequenceType(engineParameter("Training.Sequence")),
255 parseSequenceType(engineParameter("Pricing.Sequence")), parseInteger(engineParameter("Training.Samples")),
256 parseInteger(engineParameter("Pricing.Samples")), parseInteger(engineParameter("Training.Seed")),
257 parseInteger(engineParameter("Pricing.Seed")), parseInteger(engineParameter("Training.BasisFunctionOrder")),
258 parsePolynomType(engineParameter("Training.BasisFunction")),
259 parseSobolBrownianGeneratorOrdering(engineParameter("BrownianBridgeOrdering")),
260 parseSobolRsgDirectionIntegers(engineParameter("SobolDirectionIntegers")), discountCurves, std::vector<Date>(),
261 std::vector<Size>());
262
263 return engine;
264}
265
266QuantLib::ext::shared_ptr<PricingEngine> CamAmcMultiLegOptionEngineBuilder::engineImpl(
267 const string& id, const std::vector<Date>& exDates, const Date& maturityDate,
268 const std::vector<Currency>& currencies, const std::vector<Date>& fixingDates,
269 const std::vector<QuantLib::ext::shared_ptr<QuantLib::InterestRateIndex>>& indexes) {
270
271 std::string ccysStr;
272 for (auto const& c : currencies) {
273 ccysStr += c.code() + "_";
274 }
275
276 DLOG("Building multi leg option engine for ccys " << ccysStr << " (from externally given CAM)");
277
278 QL_REQUIRE(!currencies.empty(), "CamMcMultiLegOptionEngineBuilder: no currencies given");
279 QL_REQUIRE(fixingDates.size() == indexes.size(), "CamMcMultiLegOptionEngineBuilder: fixing dates size ("
280 << fixingDates.size() << ") must match indexes size ("
281 << indexes.size() << ")");
282
283 std::vector<Size> externalModelIndices;
284 std::vector<Handle<YieldTermStructure>> discountCurves;
285 std::vector<Size> cIdx;
286 std::vector<QuantLib::ext::shared_ptr<IrModel>> lgm;
287 std::vector<QuantLib::ext::shared_ptr<FxBsParametrization>> fx;
288
289 // base ccy is the base ccy of the external cam by definition
290 // but in case we only have one currency, we don't need this
291 bool needBaseCcy = currencies.size() > 1;
292
293 // add the IR and FX components in the order they appear in the CAM; this way
294 // we can sort the external model indices and be sure that they match up with
295 // the indices 0,1,2,3,... of the projected model we build here
296 for (Size i = 0; i < cam_->components(CrossAssetModel::AssetType::IR); ++i) {
297 if ((i == 0 && needBaseCcy) ||
298 std::find(currencies.begin(), currencies.end(), cam_->irlgm1f(i)->currency()) != currencies.end()) {
299 lgm.push_back(cam_->lgm(i));
300 externalModelIndices.push_back(cam_->pIdx(CrossAssetModel::AssetType::IR, i));
301 cIdx.push_back(cam_->cIdx(CrossAssetModel::AssetType::IR, i));
302 if (i > 0) {
303 fx.push_back(cam_->fxbs(i - 1));
304 externalModelIndices.push_back(cam_->pIdx(CrossAssetModel::AssetType::FX, i - 1));
305 cIdx.push_back(cam_->cIdx(CrossAssetModel::AssetType::FX, i - 1));
306 }
307 }
308 }
309
310 std::sort(externalModelIndices.begin(), externalModelIndices.end());
311 std::sort(cIdx.begin(), cIdx.end());
312
313 // build correlation matrix
314 Matrix corr(cIdx.size(), cIdx.size(), 1.0);
315 for (Size i = 0; i < cIdx.size(); ++i) {
316 for (Size j = 0; j < i; ++j) {
317 corr(i, j) = corr(j, i) = cam_->correlation()(cIdx[i], cIdx[j]);
318 }
319 }
320
321 Handle<CrossAssetModel> model(QuantLib::ext::make_shared<CrossAssetModel>(lgm, fx, corr));
322 // we assume that the model has the pricing discount curves attached already, so
323 // we leave the discountCurves vector empty here
324
325 // build the pricing engine
326
327 auto engine = QuantLib::ext::make_shared<McMultiLegOptionEngine>(
328 model, parseSequenceType(engineParameter("Training.Sequence")),
329 parseSequenceType(engineParameter("Pricing.Sequence")), parseInteger(engineParameter("Training.Samples")),
330 parseInteger(engineParameter("Pricing.Samples")), parseInteger(engineParameter("Training.Seed")),
331 parseInteger(engineParameter("Pricing.Seed")), parseInteger(engineParameter("Training.BasisFunctionOrder")),
332 parsePolynomType(engineParameter("Training.BasisFunction")),
333 parseSobolBrownianGeneratorOrdering(engineParameter("BrownianBridgeOrdering")),
334 parseSobolRsgDirectionIntegers(engineParameter("SobolDirectionIntegers")), discountCurves, simulationDates_,
335 externalModelIndices, parseBool(engineParameter("MinObsDate")),
336 parseRegressorModel(engineParameter("RegressorModel", {}, false, "Simple")),
337 parseRealOrNull(engineParameter("RegressionVarianceCutoff", {}, false, std::string())));
338
339 return engine;
340}
341
342} // namespace data
343} // namespace ore
multi leg option engine builder
const QuantLib::ext::shared_ptr< QuantExt::CrossAssetModel > cam_
QuantLib::ext::shared_ptr< PricingEngine > engineImpl(const string &id, const std::vector< Date > &exDates, const Date &maturityDate, const std::vector< Currency > &currencies, const std::vector< Date > &fixingDates, const std::vector< QuantLib::ext::shared_ptr< QuantLib::InterestRateIndex > > &indexes) override
std::string getCcyValue(const std::string &s, const std::string &c, const bool mandatory)
QuantLib::ext::shared_ptr< PricingEngine > engineImpl(const string &id, const std::vector< Date > &exDates, const Date &maturityDate, const std::vector< Currency > &currencies, const std::vector< Date > &fixingDates, const std::vector< QuantLib::ext::shared_ptr< QuantLib::InterestRateIndex > > &indexes) override
QuantLib::ext::shared_ptr< Market > market_
const string & engine() const
Return the engine name.
const string & model() const
Return the model name.
std::string engineParameter(const std::string &p, const std::vector< std::string > &qualifiers={}, const bool mandatory=true, const std::string &defaultValue="") const
const string & configuration(const MarketContext &key)
Return a configuration (or the default one if key not found)
set< std::pair< string, QuantLib::ext::shared_ptr< QuantExt::ModelBuilder > > > modelBuilders_
std::map< std::string, std::string > globalParameters_
map< string, string > modelParameters_
Build a cross asset model.
Cross asset model data.
FX component data for the cross asset model.
SequenceType parseSequenceType(const std::string &s)
Convert string to sequence type.
Definition: parsers.cpp:668
QuantLib::LsmBasisSystem::PolynomialType parsePolynomType(const std::string &s)
Convert text to QuantLib::LsmBasisSystem::PolynomialType.
Definition: parsers.cpp:527
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
Definition: parsers.cpp:51
bool parseBool(const string &s)
Convert text to bool.
Definition: parsers.cpp:144
SobolRsg::DirectionIntegers parseSobolRsgDirectionIntegers(const std::string &s)
Convert text to QuantLib::SobolRsg::DirectionIntegers.
Definition: parsers.cpp:579
Real parseRealOrNull(const string &s)
Convert text to Real, empty string to Null<Real>()
Definition: parsers.cpp:120
QuantExt::McMultiLegBaseEngine::RegressorModel parseRegressorModel(const std::string &s)
Convert text to QuantExt::McMultiLegBaseEngine::RegressorModel.
Definition: parsers.cpp:1418
Real parseReal(const string &s)
Convert text to Real.
Definition: parsers.cpp:112
SobolBrownianGenerator::Ordering parseSobolBrownianGeneratorOrdering(const std::string &s)
Convert text to QuantLib::SobolBrownianGenerator::Ordering.
Definition: parsers.cpp:567
Integer parseInteger(const string &s)
Convert text to QuantLib::Integer.
Definition: parsers.cpp:136
IR component data for the cross asset model.
Linear Gauss Markov model data.
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
CorrelationFactor parseCorrelationFactor(const string &name, const char separator)
CalibrationType parseCalibrationType(const string &s)
Convert calibration type string into enumerated class value.
Definition: irmodeldata.cpp:47
CalibrationStrategy parseCalibrationStrategy(const string &s)
Convert calibration strategy string into enumerated class value.
Definition: irmodeldata.cpp:70
VolatilityType volatilityType(CapFloorVolatilityCurveConfig::VolatilityType type)
Imply QuantLib::VolatilityType from CapFloorVolatilityCurveConfig::VolatilityType.
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
LgmData::ReversionType parseReversionType(const string &s)
Enum parsers.
Definition: lgmdata.cpp:62
LgmData::VolatilityType parseVolatilityType(const string &s)
Definition: lgmdata.cpp:81
Serializable Credit Default Swap.
Definition: namespaces.docs:23
string conversion utilities