11/*
2- Copyright (C) 2016-2022 Quaternion Risk Management Ltd
2+ Copyright (C) 2016 Quaternion Risk Management Ltd
33 All rights reserved.
44
55 This file is part of ORE, a free-software/open-source library
1818
1919#include < ored/model/lgmbuilder.hpp>
2020#include < ored/portfolio/builders/swaption.hpp>
21+ #include < ored/utilities/dategrid.hpp>
2122#include < ored/utilities/parsers.hpp>
2223#include < ored/utilities/to_string.hpp>
2324
2425#include < ql/methods/montecarlo/lsmbasissystem.hpp>
2526#include < ql/pricingengines/swaption/blackswaptionengine.hpp>
26- #include < ql/pricingengines/swaption/blackswaptionengine.hpp>
2727
2828#include < qle/methods/multipathgeneratorbase.hpp>
29- #include < qle/pricingengines/numericlgmmultilegoptionengine.hpp>
3029#include < qle/pricingengines/mcmultilegoptionengine.hpp>
30+ #include < qle/pricingengines/numericlgmmultilegoptionengine.hpp>
3131
3232#include < set>
3333
@@ -77,17 +77,19 @@ boost::shared_ptr<PricingEngine> EuropeanSwaptionEngineBuilder::engineImpl(const
7777 }
7878}
7979
80- boost::shared_ptr<QuantExt::LGM> LGMBermudanSwaptionEngineBuilder::model (const string& id, const string& key,
81- const std::vector<Date>& expiries,
82- const Date& maturity,
83- const std::vector<Real>& strikes) {
80+ boost::shared_ptr<QuantExt::LGM> LGMBermudanAmericanSwaptionEngineBuilder::model (const string& id, const string& key,
81+ const std::vector<Date>& expiries,
82+ const Date& maturity,
83+ const std::vector<Real>& strikes,
84+ const bool isAmerican) {
8485 boost::shared_ptr<IborIndex> index;
8586 std::string ccy = tryParseIborIndex (key, index) ? index->currency ().code () : key;
8687
8788 DLOG (" Get model data" );
8889 auto calibration = parseCalibrationType (modelParameter (" Calibration" ));
8990 auto calibrationStrategy = parseCalibrationStrategy (modelParameter (" CalibrationStrategy" ));
90- std::string referenceCalibrationGrid = modelParameter (" ReferenceCalibrationGrid" , {}, false , " " );
91+ // required for american options to set up calibration basket
92+ std::string referenceCalibrationGrid = modelParameter (" ReferenceCalibrationGrid" , {}, isAmerican, " " );
9193 Real lambda = parseReal (modelParameter (" Reversion" , {key, ccy}));
9294 vector<Real> sigma = parseListOfValues<Real>(modelParameter (" Volatility" ), &parseReal);
9395 vector<Real> sigmaTimes = parseListOfValues<Real>(modelParameter (" VolatilityTimes" , {}, false ), &parseReal);
@@ -135,21 +137,45 @@ boost::shared_ptr<QuantExt::LGM> LGMBermudanSwaptionEngineBuilder::model(const s
135137 data->calibrationType () = calibration;
136138 data->shiftHorizon () = shiftHorizon;
137139
140+ std::vector<Date> effExpiries;
141+ std::vector<Real> effStrikes;
142+ if (!isAmerican) {
143+ effExpiries = expiries;
144+ effStrikes = strikes;
145+ } else {
146+ QL_REQUIRE (expiries.size () == 2 && strikes.size () == 2 ,
147+ " LGMBermudanAmericanSwaptionEngineBuilder::model(): expected 2 expiries and strikes for exercise "
148+ " style 'American', got "
149+ << expiries.size () << " expiries and " << strikes.size () << " strikes." );
150+ // keep one calibration instrument per reference grid interval
151+ DateGrid grid (referenceCalibrationGrid);
152+ std::copy_if (grid.dates ().begin (), grid.dates ().end (), std::back_inserter (effExpiries),
153+ [&expiries](const Date& d) { return d >= expiries[0 ] && d < expiries[1 ]; });
154+ // simple linear interpolation of calibration strikes between endpoints, this can be refined obviously
155+ effStrikes.resize (effExpiries.size ());
156+ Real t0 = Actual365Fixed ().yearFraction (today, expiries[0 ]);
157+ Real t1 = Actual365Fixed ().yearFraction (today, expiries[1 ]);
158+ for (Size i=0 ;i<effExpiries.size ();++i) {
159+ Real t = Actual365Fixed ().yearFraction (today, effExpiries[i]);
160+ effStrikes[i] = strikes[0 ] + (strikes[1 ] - strikes[0 ]) / (t1 - t0) * (t - t0);
161+ }
162+ }
163+
138164 if (calibrationStrategy == CalibrationStrategy::CoterminalATM ||
139165 calibrationStrategy == CalibrationStrategy::CoterminalDealStrike) {
140166 DLOG (" Build LgmData for co-terminal specification" );
141167 vector<string> expiryDates, termDates;
142- for (Size i = 0 ; i < expiries .size (); ++i) {
143- expiryDates.push_back (to_string (expiries [i]));
168+ for (Size i = 0 ; i < effExpiries .size (); ++i) {
169+ expiryDates.push_back (to_string (effExpiries [i]));
144170 termDates.push_back (to_string (maturity));
145171 }
146172 data->optionExpiries () = expiryDates;
147173 data->optionTerms () = termDates;
148174 data->optionStrikes ().resize (expiryDates.size (), " ATM" );
149175 if (calibrationStrategy == CalibrationStrategy::CoterminalDealStrike) {
150- for (Size i = 0 ; i < expiries .size (); ++i) {
151- if (strikes [i] != Null<Real>())
152- data->optionStrikes ()[i] = std::to_string (strikes [i]);
176+ for (Size i = 0 ; i < effExpiries .size (); ++i) {
177+ if (effStrikes [i] != Null<Real>())
178+ data->optionStrikes ()[i] = std::to_string (effStrikes [i]);
153179 }
154180 }
155181 if (calibration == CalibrationType::Bootstrap) {
@@ -202,13 +228,13 @@ boost::shared_ptr<QuantExt::LGM> LGMBermudanSwaptionEngineBuilder::model(const s
202228 return model;
203229}
204230
205- boost::shared_ptr<PricingEngine> LGMGridBermudanSwaptionEngineBuilder::engineImpl ( const string& id, const string& key,
206- const std::vector<Date>& expiries ,
207- const Date& maturity,
208- const std::vector<Real>& strikes) {
209- DLOG (" Building LGM Grid Bermudan Swaption engine for trade " << id);
231+ boost::shared_ptr<PricingEngine>
232+ LGMGridBermudanAmericanSwaptionEngineBuilder::engineImpl ( const string& id, const string& key ,
233+ const std::vector<Date>& expiries, const Date& maturity,
234+ const std::vector<Real>& strikes, const bool isAmerican ) {
235+ DLOG (" Building LGM Grid Bermudan/American Swaption engine for trade " << id);
210236
211- boost::shared_ptr<QuantExt::LGM> lgm = model (id, key, expiries, maturity, strikes);
237+ boost::shared_ptr<QuantExt::LGM> lgm = model (id, key, expiries, maturity, strikes, isAmerican );
212238
213239 DLOG (" Get engine data" );
214240 Real sy = parseReal (engineParameter (" sy" ));
@@ -221,16 +247,17 @@ boost::shared_ptr<PricingEngine> LGMGridBermudanSwaptionEngineBuilder::engineImp
221247 boost::shared_ptr<IborIndex> index;
222248 std::string ccy = tryParseIborIndex (key, index) ? index->currency ().code () : key;
223249 return boost::make_shared<QuantExt::NumericLgmMultiLegOptionEngine>(
224- lgm, sy, ny, sx, nx, market_->discountCurve (ccy, configuration (MarketContext::pricing)));
250+ lgm, sy, ny, sx, nx, market_->discountCurve (ccy, configuration (MarketContext::pricing)),
251+ isAmerican ? parseInteger (modelParameter (" ExerciseTimeStepsPerYear" )) : 0 );
225252}
226253
227- boost::shared_ptr<PricingEngine> LGMFDBermudanSwaptionEngineBuilder::engineImpl ( const string& id, const string& key,
228- const std::vector<Date>& expiries ,
229- const Date& maturity,
230- const std::vector<Real>& strikes) {
231- DLOG (" Building LGM FD Bermudan Swaption engine for trade " << id);
254+ boost::shared_ptr<PricingEngine>
255+ LGMFDBermudanAmericanSwaptionEngineBuilder::engineImpl ( const string& id, const string& key ,
256+ const std::vector<Date>& expiries, const Date& maturity,
257+ const std::vector<Real>& strikes, const bool isAmerican ) {
258+ DLOG (" Building LGM FD Bermudan/American Swaption engine for trade " << id);
232259
233- boost::shared_ptr<QuantExt::LGM> lgm = model (id, key, expiries, maturity, strikes);
260+ boost::shared_ptr<QuantExt::LGM> lgm = model (id, key, expiries, maturity, strikes, isAmerican );
234261
235262 DLOG (" Get engine data" );
236263 QuantLib::FdmSchemeDesc scheme = parseFdmSchemeDesc (engineParameter (" Scheme" ));
@@ -245,16 +272,17 @@ boost::shared_ptr<PricingEngine> LGMFDBermudanSwaptionEngineBuilder::engineImpl(
245272 std::string ccy = tryParseIborIndex (key, index) ? index->currency ().code () : key;
246273 return boost::make_shared<QuantExt::NumericLgmMultiLegOptionEngine>(
247274 lgm, maxTime, scheme, stateGridPoints, timeStepsPerYear, mesherEpsilon,
248- market_->discountCurve (ccy, configuration (MarketContext::pricing)));
275+ market_->discountCurve (ccy, configuration (MarketContext::pricing)),
276+ isAmerican ? parseInteger (modelParameter (" ExerciseTimeStepsPerYear" )) : 0 );
249277}
250278
251- boost::shared_ptr<PricingEngine> LgmMcBermudanSwaptionEngineBuilder::engineImpl ( const string& id, const string& key,
252- const std::vector<Date>& expiries ,
253- const Date& maturity,
254- const std::vector<Real>& strikes) {
255- DLOG (" Building MC Bermudan Swaption engine for trade " << id);
279+ boost::shared_ptr<PricingEngine>
280+ LgmMcBermudanAmericanSwaptionEngineBuilder::engineImpl ( const string& id, const string& key ,
281+ const std::vector<Date>& expiries, const Date& maturity,
282+ const std::vector<Real>& strikes, const bool isAmerican ) {
283+ DLOG (" Building MC Bermudan/American Swaption engine for trade " << id);
256284
257- auto lgm = model (id, key, expiries, maturity, strikes);
285+ auto lgm = model (id, key, expiries, maturity, strikes, isAmerican );
258286
259287 // Build engine
260288 DLOG (" Build engine (configuration " << configuration (MarketContext::pricing) << " )" );
@@ -265,16 +293,17 @@ boost::shared_ptr<PricingEngine> LgmMcBermudanSwaptionEngineBuilder::engineImpl(
265293 std::vector<Date>(), std::vector<Size>());
266294} // LgmMc engineImpl()
267295
268- boost::shared_ptr<PricingEngine> LgmAmcBermudanSwaptionEngineBuilder::engineImpl ( const string& id, const string& key,
269- const std::vector<Date>& expiries ,
270- const Date& maturity,
271- const std::vector<Real>& strikes) {
296+ boost::shared_ptr<PricingEngine>
297+ LgmAmcBermudanAmericanSwaptionEngineBuilder::engineImpl ( const string& id, const string& key ,
298+ const std::vector<Date>& expiries, const Date& maturity,
299+ const std::vector<Real>& strikes, const bool isAmerican ) {
272300 boost::shared_ptr<IborIndex> index;
273301 std::string ccy = tryParseIborIndex (key, index) ? index->currency ().code () : key;
274302 Currency curr = parseCurrency (ccy);
275- DLOG (" Building AMC Bermudan Swaption engine for key " << key << " , ccy " << ccy << " (from externally given CAM)" );
303+ DLOG (" Building AMC Bermudan/American Swaption engine for key " << key << " , ccy " << ccy
304+ << " (from externally given CAM)" );
276305
277- QL_REQUIRE (cam_ != nullptr , " LgmCamBermudanSwaptionEngineBuilder ::engineImpl: cam is null" );
306+ QL_REQUIRE (cam_ != nullptr , " LgmCamBermudanAmericanSwaptionEngineBuilder ::engineImpl: cam is null" );
278307 Size currIdx = cam_->ccyIndex (curr);
279308 auto lgm = cam_->lgm (currIdx);
280309 std::vector<Size> modelIndex (1 , cam_->pIdx (CrossAssetModel::AssetType::IR, currIdx));
0 commit comments