Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
swaption.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 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
25
27#include <qle/pricingengines/blackmultilegoptionengine.hpp>
30
31#include <ql/methods/montecarlo/lsmbasissystem.hpp>
32#include <ql/termstructures/yield/zerospreadedtermstructure.hpp>
33
34#include <set>
35
36using namespace QuantLib;
37using namespace QuantExt;
38using namespace std;
39using namespace ore::data;
40
41namespace {
42QuantLib::ext::shared_ptr<PricingEngine> buildMcEngine(
43 const std::function<string(string, const std::vector<std::string>&, const bool, const string&)>& engineParameter,
44 const QuantLib::ext::shared_ptr<LGM>& lgm, const Handle<YieldTermStructure>& discountCurve,
45 const std::vector<Date>& simulationDates, const std::vector<Size>& externalModelIndices) {
46
47 return QuantLib::ext::make_shared<QuantExt::McMultiLegOptionEngine>(
48 lgm, parseSequenceType(engineParameter("Training.Sequence", {}, false, "SobolBrownianBridge")),
49 parseSequenceType(engineParameter("Pricing.Sequence", {}, false, "SobolBrownianBridge")),
50 parseInteger(engineParameter("Training.Samples", {}, true, std::string())),
51 parseInteger(engineParameter("Pricing.Samples", {}, false, "0")),
52 parseInteger(engineParameter("Training.Seed", {}, true, std::string())),
53 parseInteger(engineParameter("Pricing.Seed", {}, false, "42")),
54 parseInteger(engineParameter("Training.BasisFunctionOrder", {}, true, std::string())),
55 parsePolynomType(engineParameter("Training.BasisFunction", {}, true, std::string())),
56 parseSobolBrownianGeneratorOrdering(engineParameter("BrownianBridgeOrdering", {}, false, "Steps")),
57 parseSobolRsgDirectionIntegers(engineParameter("SobolDirectionIntegers", {}, false, "JoeKuoD7")), discountCurve,
58 simulationDates, externalModelIndices, parseBool(engineParameter("MinObsDate", {}, false, "true")),
59 parseRegressorModel(engineParameter("RegressorModel", {}, false, "Simple")),
60 parseRealOrNull(engineParameter("RegressionVarianceCutoff", {}, false, std::string())));
61}
62} // namespace
63
64namespace ore {
65namespace data {
66
67QuantLib::ext::shared_ptr<PricingEngine>
68EuropeanSwaptionEngineBuilder::engineImpl(const string& id, const string& key, const std::vector<Date>& dates,
69 const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
70 const std::string& discountCurve, const std::string& securitySpread) {
71 QuantLib::ext::shared_ptr<IborIndex> index;
72 string ccyCode = tryParseIborIndex(key, index) ? index->currency().code() : key;
73 Handle<YieldTermStructure> yts =
74 discountCurve.empty() ? market_->discountCurve(ccyCode, configuration(MarketContext::pricing))
76 if (!securitySpread.empty())
77 yts = Handle<YieldTermStructure>(QuantLib::ext::make_shared<ZeroSpreadedTermStructure>(
78 yts, market_->securitySpread(securitySpread, configuration(MarketContext::pricing))));
79 Handle<SwaptionVolatilityStructure> svts = market_->swaptionVol(key, configuration(MarketContext::pricing));
80 return QuantLib::ext::make_shared<BlackMultiLegOptionEngine>(yts, svts);
81}
82
83QuantLib::ext::shared_ptr<QuantExt::LGM> LGMSwaptionEngineBuilder::model(const string& id, const string& key,
84 const std::vector<Date>& expiries,
85 const Date& maturity, const std::vector<Real>& strikes,
86 const bool isAmerican) {
87 QuantLib::ext::shared_ptr<IborIndex> index;
88 std::string ccy = tryParseIborIndex(key, index) ? index->currency().code() : key;
89
90 DLOG("Get model data");
91 auto calibration = parseCalibrationType(modelParameter("Calibration"));
92 auto calibrationStrategy = parseCalibrationStrategy(modelParameter("CalibrationStrategy"));
93 // required for american options to set up calibration basket
94 std::string referenceCalibrationGrid = modelParameter("ReferenceCalibrationGrid", {}, isAmerican, "");
95 Real lambda = parseReal(modelParameter("Reversion", {key, ccy}));
96 vector<Real> sigma = parseListOfValues<Real>(modelParameter("Volatility"), &parseReal);
97 vector<Real> sigmaTimes = parseListOfValues<Real>(modelParameter("VolatilityTimes", {}, false), &parseReal);
98 QL_REQUIRE(sigma.size() == sigmaTimes.size() + 1, "there must be n+1 volatilities (" << sigma.size()
99 << ") for n volatility times ("
100 << sigmaTimes.size() << ")");
101 Real tolerance = parseReal(modelParameter("Tolerance"));
102 auto reversionType = parseReversionType(modelParameter("ReversionType"));
103 auto volatilityType = parseVolatilityType(modelParameter("VolatilityType"));
104 bool continueOnCalibrationError = globalParameters_.count("ContinueOnCalibrationError") > 0 &&
105 parseBool(globalParameters_.at("ContinueOnCalibrationError"));
106
107 auto floatSpreadMapping = parseFloatSpreadMapping(modelParameter("FloatSpreadMapping", {}, false, "proRata"));
108
109 auto data = QuantLib::ext::make_shared<IrLgmData>();
110
111 // check for allowed calibration / bermudan strategy settings
112 std::vector<std::pair<CalibrationType, CalibrationStrategy>> validCalPairs = {
118
119 QL_REQUIRE(std::find(validCalPairs.begin(), validCalPairs.end(),
120 std::make_pair(calibration, calibrationStrategy)) != validCalPairs.end(),
121 "Calibration (" << calibration << ") and CalibrationStrategy (" << calibrationStrategy
122 << ") are not allowed in this combination");
123
124 // compute horizon shift
125 Real shiftHorizon = parseReal(modelParameter("ShiftHorizon", {}, false, "0.5"));
126 Date today = Settings::instance().evaluationDate();
127 shiftHorizon = ActualActual(ActualActual::ISDA).yearFraction(today, maturity) * shiftHorizon;
128
129 // Default: no calibration, constant lambda and sigma from engine configuration
130 data->reset();
131 data->qualifier() = key;
132 data->calibrateH() = false;
133 data->hParamType() = ParamType::Constant;
134 data->hValues() = {lambda};
135 data->reversionType() = reversionType;
136 data->calibrateA() = false;
137 data->aParamType() = ParamType::Piecewise;
138 data->aValues() = sigma;
139 data->aTimes() = sigmaTimes;
140 data->volatilityType() = volatilityType;
141 data->calibrationType() = calibration;
142 data->shiftHorizon() = shiftHorizon;
143 data->floatSpreadMapping() = floatSpreadMapping;
144
145 std::vector<Date> effExpiries;
146 std::vector<Real> effStrikes;
147 if (!isAmerican) {
148 effExpiries = expiries;
149 effStrikes = strikes;
150 } else {
151 QL_REQUIRE(expiries.size() == 2 && strikes.size() == 2,
152 "LGMBermudanAmericanSwaptionEngineBuilder::model(): expected 2 expiries and strikes for exercise "
153 "style 'American', got "
154 << expiries.size() << " expiries and " << strikes.size() << " strikes.");
155 // keep one calibration instrument per reference grid interval
156 DateGrid grid(referenceCalibrationGrid);
157 std::copy_if(grid.dates().begin(), grid.dates().end(), std::back_inserter(effExpiries),
158 [&expiries](const Date& d) { return d >= expiries[0] && d < expiries[1]; });
159 // simple linear interpolation of calibration strikes between endpoints, this can be refined obviously
160 effStrikes.resize(effExpiries.size(), Null<Real>());
161 if (strikes[0] != Null<Real>() && strikes[1] != Null<Real>()) {
162 Real t0 = Actual365Fixed().yearFraction(today, expiries[0]);
163 Real t1 = Actual365Fixed().yearFraction(today, expiries[1]);
164 for (Size i = 0; i < effExpiries.size(); ++i) {
165 Real t = Actual365Fixed().yearFraction(today, effExpiries[i]);
166 effStrikes[i] = strikes[0] + (strikes[1] - strikes[0]) / (t1 - t0) * (t - t0);
167 }
168 }
169 }
170
171 if (calibrationStrategy == CalibrationStrategy::CoterminalATM ||
172 calibrationStrategy == CalibrationStrategy::CoterminalDealStrike) {
173 DLOG("Build LgmData for co-terminal specification");
174 vector<string> expiryDates, termDates;
175 for (Size i = 0; i < effExpiries.size(); ++i) {
176 expiryDates.push_back(to_string(effExpiries[i]));
177 termDates.push_back(to_string(maturity));
178 }
179 data->optionExpiries() = expiryDates;
180 data->optionTerms() = termDates;
181 data->optionStrikes().resize(expiryDates.size(), "ATM");
182 if (calibrationStrategy == CalibrationStrategy::CoterminalDealStrike) {
183 for (Size i = 0; i < effExpiries.size(); ++i) {
184 if (effStrikes[i] != Null<Real>())
185 data->optionStrikes()[i] = std::to_string(effStrikes[i]);
186 }
187 }
188 if (calibration == CalibrationType::Bootstrap) {
189 DLOG("Calibrate piecewise alpha");
190 data->calibrationType() = CalibrationType::Bootstrap;
191 data->calibrateH() = false;
192 data->hParamType() = ParamType::Constant;
193 data->hValues() = {lambda};
194 data->calibrateA() = true;
195 data->aParamType() = ParamType::Piecewise;
196 data->aValues() = {sigma};
197 } else if (calibration == CalibrationType::BestFit) {
198 DLOG("Calibrate constant sigma");
199 data->calibrationType() = CalibrationType::BestFit;
200 data->calibrateH() = false;
201 data->hParamType() = ParamType::Constant;
202 data->hValues() = {lambda};
203 data->calibrateA() = true;
204 data->aParamType() = ParamType::Constant;
205 data->aValues() = {sigma};
206 } else
207 QL_FAIL("choice of calibration type invalid");
208 }
209
210 bool generateAdditionalResults = false;
211 auto p = globalParameters_.find("GenerateAdditionalResults");
212 if (p != globalParameters_.end()) {
213 generateAdditionalResults = parseBool(p->second);
214 }
215
216 // Build and calibrate model
217 DLOG("Build LGM model");
218 QuantLib::ext::shared_ptr<LgmBuilder> calib = QuantLib::ext::make_shared<LgmBuilder>(
219 market_, data, configuration(MarketContext::irCalibration), tolerance, continueOnCalibrationError,
220 referenceCalibrationGrid, generateAdditionalResults, id);
221
222 // In some cases, we do not want to calibrate the model
223 QuantLib::ext::shared_ptr<QuantExt::LGM> model;
224 if (globalParameters_.count("Calibrate") == 0 || parseBool(globalParameters_.at("Calibrate"))) {
225 DLOG("Calibrate model (configuration " << configuration(MarketContext::irCalibration) << ")");
226 model = calib->model();
227 } else {
228 DLOG("Skip calibration of model based on global parameters");
229 calib->freeze();
230 model = calib->model();
231 calib->unfreeze();
232 }
233 modelBuilders_.insert(std::make_pair(id, calib));
234
235 return model;
236}
237
238QuantLib::ext::shared_ptr<PricingEngine>
239LGMGridSwaptionEngineBuilder::engineImpl(const string& id, const string& key, const std::vector<Date>& expiries,
240 const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
241 const std::string& discountCurve, const std::string& securitySpread) {
242 DLOG("Building LGM Grid Bermudan/American Swaption engine for trade " << id);
243
244 QuantLib::ext::shared_ptr<QuantExt::LGM> lgm = model(id, key, expiries, maturity, strikes, isAmerican);
245
246 DLOG("Get engine data");
247 Real sy = parseReal(engineParameter("sy"));
248 Size ny = parseInteger(engineParameter("ny"));
249 Real sx = parseReal(engineParameter("sx"));
250 Size nx = parseInteger(engineParameter("nx"));
251
252 // Build engine
253 DLOG("Build engine (configuration " << configuration(MarketContext::pricing) << ")");
254 QuantLib::ext::shared_ptr<IborIndex> index;
255 std::string ccy = tryParseIborIndex(key, index) ? index->currency().code() : key;
256 Handle<YieldTermStructure> yts =
257 discountCurve.empty() ? market_->discountCurve(ccy, configuration(MarketContext::pricing))
259 if (!securitySpread.empty())
260 yts = Handle<YieldTermStructure>(QuantLib::ext::make_shared<ZeroSpreadedTermStructure>(
261 yts, market_->securitySpread(securitySpread, configuration(MarketContext::pricing))));
262 return QuantLib::ext::make_shared<QuantExt::NumericLgmMultiLegOptionEngine>(
263 lgm, sy, ny, sx, nx, yts, isAmerican ? parseInteger(modelParameter("ExerciseTimeStepsPerYear")) : 0);
264}
265
266QuantLib::ext::shared_ptr<PricingEngine>
267LGMFDSwaptionEngineBuilder::engineImpl(const string& id, const string& key, const std::vector<Date>& expiries,
268 const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
269 const std::string& discountCurve, const std::string& securitySpread) {
270 DLOG("Building LGM FD Bermudan/American Swaption engine for trade " << id);
271
272 QuantLib::ext::shared_ptr<QuantExt::LGM> lgm = model(id, key, expiries, maturity, strikes, isAmerican);
273
274 DLOG("Get engine data");
275 QuantLib::FdmSchemeDesc scheme = parseFdmSchemeDesc(engineParameter("Scheme"));
276 Size stateGridPoints = parseInteger(engineParameter("StateGridPoints"));
277 Size timeStepsPerYear = parseInteger(engineParameter("TimeStepsPerYear"));
278 Real mesherEpsilon = parseReal(engineParameter("MesherEpsilon"));
279
280 Real maxTime = lgm->termStructure()->timeFromReference(maturity);
281
282 DLOG("Build engine (configuration " << configuration(MarketContext::pricing) << ")");
283 QuantLib::ext::shared_ptr<IborIndex> index;
284 std::string ccy = tryParseIborIndex(key, index) ? index->currency().code() : key;
285 Handle<YieldTermStructure> yts =
286 discountCurve.empty() ? market_->discountCurve(ccy, configuration(MarketContext::pricing))
288 if (!securitySpread.empty())
289 yts = Handle<YieldTermStructure>(QuantLib::ext::make_shared<ZeroSpreadedTermStructure>(
290 yts, market_->securitySpread(securitySpread, configuration(MarketContext::pricing))));
291 return QuantLib::ext::make_shared<QuantExt::NumericLgmMultiLegOptionEngine>(
292 lgm, maxTime, scheme, stateGridPoints, timeStepsPerYear, mesherEpsilon, yts,
293 isAmerican ? parseInteger(modelParameter("ExerciseTimeStepsPerYear")) : 0);
294}
295
296QuantLib::ext::shared_ptr<PricingEngine>
297LGMMCSwaptionEngineBuilder::engineImpl(const string& id, const string& key, const std::vector<Date>& expiries,
298 const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
299 const std::string& discountCurve, const std::string& securitySpread) {
300 DLOG("Building MC Bermudan/American Swaption engine for trade " << id);
301
302 auto lgm = model(id, key, expiries, maturity, strikes, isAmerican);
303
304 // Build engine
305 DLOG("Build engine (configuration " << configuration(MarketContext::pricing) << ")");
306 QuantLib::ext::shared_ptr<IborIndex> index;
307 std::string ccy = tryParseIborIndex(key, index) ? index->currency().code() : key;
308 Handle<YieldTermStructure> yts =
309 discountCurve.empty() ? market_->discountCurve(ccy, configuration(MarketContext::pricing))
311 if (!securitySpread.empty())
312 yts = Handle<YieldTermStructure>(QuantLib::ext::make_shared<ZeroSpreadedTermStructure>(
313 yts, market_->securitySpread(securitySpread, configuration(MarketContext::pricing))));
314 return buildMcEngine([this](const std::string& p, const std::vector<std::string>& q, const bool m,
315 const std::string& d) { return this->engineParameter(p, q, m, d); },
316 lgm, yts, std::vector<Date>(), std::vector<Size>());
317} // LgmMc engineImpl()
318
319QuantLib::ext::shared_ptr<PricingEngine>
320LGMAmcSwaptionEngineBuilder::engineImpl(const string& id, const string& key, const std::vector<Date>& expiries,
321 const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
322 const std::string& discountCurve, const std::string& securitySpread) {
323 QuantLib::ext::shared_ptr<IborIndex> index;
324 std::string ccy = tryParseIborIndex(key, index) ? index->currency().code() : key;
325 Currency curr = parseCurrency(ccy);
326 DLOG("Building AMC Bermudan/American Swaption engine for key " << key << ", ccy " << ccy
327 << " (from externally given CAM)");
328
329 QL_REQUIRE(cam_ != nullptr, "LgmCamBermudanAmericanSwaptionEngineBuilder::engineImpl: cam is null");
330 Size currIdx = cam_->ccyIndex(curr);
331 auto lgm = cam_->lgm(currIdx);
332 std::vector<Size> modelIndex(1, cam_->pIdx(CrossAssetModel::AssetType::IR, currIdx));
333
334 // Build engine
335 DLOG("Build engine (configuration " << configuration(MarketContext::pricing) << ")");
336 Handle<YieldTermStructure> yts =
337 discountCurve.empty() ? market_->discountCurve(ccy, configuration(MarketContext::pricing))
339 if (!securitySpread.empty())
340 yts = Handle<YieldTermStructure>(QuantLib::ext::make_shared<ZeroSpreadedTermStructure>(
341 yts, market_->securitySpread(securitySpread, configuration(MarketContext::pricing))));
342 return buildMcEngine([this](const std::string& p, const std::vector<std::string>& q, const bool m,
343 const std::string& d) { return this->engineParameter(p, q, m, d); },
344 lgm, yts, simulationDates_, modelIndex);
345} // LgmCam engineImpl
346
347} // namespace data
348} // namespace ore
Simulation Date Grid.
Definition: dategrid.hpp:38
const std::vector< QuantLib::Date > & dates() const
Definition: dategrid.hpp:81
QuantLib::ext::shared_ptr< Market > market_
const string & model() const
Return the model name.
std::string modelParameter(const std::string &p, const std::vector< std::string > &qualifiers={}, const bool mandatory=true, const std::string &defaultValue="") const
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_
QuantLib::ext::shared_ptr< PricingEngine > engineImpl(const string &id, const string &key, const std::vector< Date > &dates, const Date &maturity, const std::vector< Real > &strikes, const bool isAmerican, const std::string &discountCurve, const std::string &securitySpread) override
Definition: swaption.cpp:68
const std::vector< Date > simulationDates_
Definition: swaption.hpp:136
const QuantLib::ext::shared_ptr< QuantExt::CrossAssetModel > cam_
Definition: swaption.hpp:135
QuantLib::ext::shared_ptr< PricingEngine > engineImpl(const string &id, const string &key, const std::vector< Date > &dates, const Date &maturity, const std::vector< Real > &strikes, const bool isAmerican, const std::string &discountCurve, const std::string &securitySpread) override
Definition: swaption.cpp:320
QuantLib::ext::shared_ptr< PricingEngine > engineImpl(const string &id, const string &key, const std::vector< Date > &dates, const Date &maturity, const std::vector< Real > &strikes, const bool isAmerican, const std::string &discountCurve, const std::string &securitySpread) override
Definition: swaption.cpp:267
QuantLib::ext::shared_ptr< PricingEngine > engineImpl(const string &id, const string &key, const std::vector< Date > &dates, const Date &maturity, const std::vector< Real > &strikes, const bool isAmerican, const std::string &discountCurve, const std::string &securitySpread) override
Definition: swaption.cpp:239
QuantLib::ext::shared_ptr< PricingEngine > engineImpl(const string &id, const string &key, const std::vector< Date > &dates, const Date &maturity, const std::vector< Real > &strikes, const bool isAmerican, const std::string &discountCurve, const std::string &securitySpread) override
Definition: swaption.cpp:297
The date grid class.
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
bool tryParseIborIndex(const string &s, QuantLib::ext::shared_ptr< IborIndex > &index)
Try to convert std::string to QuantLib::IborIndex.
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
Definition: parsers.cpp:290
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
FdmSchemeDesc parseFdmSchemeDesc(const std::string &s)
Convert string to fdm scheme desc.
Definition: parsers.cpp:692
Integer parseInteger(const string &s)
Convert text to QuantLib::Integer.
Definition: parsers.cpp:136
Build an lgm model.
@ data
Definition: log.hpp:77
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
market data related utilties
Time maturity
Definition: utilities.cpp:66
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.
Handle< YieldTermStructure > indexOrYieldCurve(const QuantLib::ext::shared_ptr< Market > &market, const std::string &name, const std::string &configuration)
Definition: marketdata.cpp:65
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
LgmData::ReversionType parseReversionType(const string &s)
Enum parsers.
Definition: lgmdata.cpp:62
QuantExt::AnalyticLgmSwaptionEngine::FloatSpreadMapping parseFloatSpreadMapping(const string &s)
Definition: lgmdata.cpp:100
LgmData::VolatilityType parseVolatilityType(const string &s)
Definition: lgmdata.cpp:81
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Map text representations to QuantLib/QuantExt types.
vector< Real > strikes
string conversion utilities