Skip to content

Commit 60eef0f

Browse files
pcaspersjenkins
authored andcommitted
Merge branch 'QPR-12824_fabian' into 'master'
Resolve QPR-12824 (representative swaption calibration for LGM) Closes QPR-12824 See merge request qs/oreplus!2638
1 parent 71106d6 commit 60eef0f

11 files changed

Lines changed: 177 additions & 66 deletions

File tree

Docs/UserGuide/pricing/pricingengines.tex

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ \subsection{Product Type: EuropeanSwaption}
366366

367367
\begin{itemize}
368368
\item Calibration: Bootstrap, BestFit, None
369-
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM
369+
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM, DeltaGammaAdjusted
370370
\item ReferenceCalibrationGrid: An optional grid, only one calibration instrument per interval is kept
371371
\item Reversion: The mean reversion
372372
\item ReversionType: Hagan, HullWhite
@@ -418,7 +418,7 @@ \subsection{Product Type: EuropeanSwaption}
418418

419419
\begin{itemize}
420420
\item Calibration: Bootstrap, BestFit, None
421-
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM
421+
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM, DeltaGammaAdjusted
422422
\item ReferenceCalibrationGrid: An optional grid, only one calibration instrument per interval is kept
423423
\item Reversion: The mean reversion
424424
\item ReversionType: Hagan, HullWhite
@@ -469,7 +469,7 @@ \subsection{Product Type: EuropeanSwaption}
469469

470470
\begin{itemize}
471471
\item Calibration: Bootstrap, BestFit, None
472-
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM
472+
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM, DeltaGammaAdjusted
473473
\item ReferenceCalibrationGrid: An optional grid, only one calibration instrument per interval is kept
474474
\item Reversion: The mean reversion
475475
\item ReversionType: Hagan, HullWhite
@@ -551,7 +551,7 @@ \subsection{Product Type: EuropeanSwaption NonStandard}
551551
%--------------------------------------------------------
552552

553553
\label{sec:EuropeanSwaption_ns_pt}
554-
A pricing product type very similar to European Swaption in subsection \ref{sec:EuropeanSwaption_pt}.
554+
A pricing product type similar to European Swaption in subsection \ref{sec:EuropeanSwaption_pt}.
555555

556556
The ``NonStandard'' product type accessed here represents the case of swaptions that do not fullfill
557557
the standard case of a fixed-against-floating swaption.
@@ -597,7 +597,7 @@ \subsection{Product Type: BermudanSwaption}
597597

598598
\begin{itemize}
599599
\item Calibration: Bootstrap, BestFit, None
600-
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM
600+
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM, DeltaGammaAdjusted
601601
\item ReferenceCalibrationGrid: An optional grid, only one calibration instrument per interval is kept
602602
\item Reversion: The mean reversion
603603
\item ReversionType: Hagan, HullWhite
@@ -646,7 +646,7 @@ \subsection{Product Type: BermudanSwaption}
646646

647647
\begin{itemize}
648648
\item Calibration: Bootstrap, BestFit, None
649-
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM
649+
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM, DeltaGammaAdjusted
650650
\item ReferenceCalibrationGrid: An optional grid, only one calibration instrument per interval is kept
651651
\item Reversion: The mean reversion
652652
\item ReversionType: Hagan, HullWhite
@@ -696,7 +696,7 @@ \subsection{Product Type: BermudanSwaption}
696696

697697
\begin{itemize}
698698
\item Calibration: Bootstrap, BestFit, None
699-
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM
699+
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM, DeltaGammaAdjusted
700700
\item ReferenceCalibrationGrid: An optional grid, only one calibration instrument per interval is kept
701701
\item Reversion: The mean reversion
702702
\item ReversionType: Hagan, HullWhite
@@ -806,7 +806,7 @@ \subsection{Product Type: AmericanSwaption}
806806

807807
\begin{itemize}
808808
\item Calibration: Bootstrap, BestFit, None
809-
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM
809+
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM, DeltaGammaAdjusted
810810
\item ReferenceCalibrationGrid: An optional grid, only one calibration instrument per interval is kept
811811
\item Reversion: The mean reversion
812812
\item ReversionType: Hagan, HullWhite
@@ -857,7 +857,7 @@ \subsection{Product Type: AmericanSwaption}
857857

858858
\begin{itemize}
859859
\item Calibration: Bootstrap, BestFit, None
860-
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM
860+
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM, DeltaGammaAdjusted
861861
\item ReferenceCalibrationGrid: An optional grid, only one calibration instrument per interval is kept
862862
\item Reversion: The mean reversion
863863
\item ReversionType: Hagan, HullWhite
@@ -906,7 +906,7 @@ \subsection{Product Type: AmericanSwaption}
906906

907907
\begin{itemize}
908908
\item Calibration: Bootstrap, BestFit, None
909-
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM
909+
\item CalibrationStrategy: CoterminalDealStrike, CoterminalATM, DeltaGammaAdjusted
910910
\item ReferenceCalibrationGrid: An optional grid, only one calibration instrument per interval is kept
911911
\item Reversion: The mean reversion
912912
\item ReversionType: Hagan, HullWhite

OREData/ored/model/irmodeldata.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,21 +72,25 @@ CalibrationStrategy parseCalibrationStrategy(const string& s) {
7272
return CalibrationStrategy::CoterminalATM;
7373
else if (boost::algorithm::to_upper_copy(s) == "COTERMINALDEALSTRIKE")
7474
return CalibrationStrategy::CoterminalDealStrike;
75+
else if (boost::algorithm::to_upper_copy(s) == "DELTAGAMMAADJUSTED")
76+
return CalibrationStrategy::DeltaGammaAdjusted;
7577
else if (boost::algorithm::to_upper_copy(s) == "UNDERLYINGATM")
7678
return CalibrationStrategy::UnderlyingATM;
7779
else if (boost::algorithm::to_upper_copy(s) == "UNDERLYINGDEALSTRIKE")
7880
return CalibrationStrategy::UnderlyingDealStrike;
7981
else if (boost::algorithm::to_upper_copy(s) == "NONE")
8082
return CalibrationStrategy::None;
8183
else
82-
QL_FAIL("Calibration strategy " << s << " not recognized");
84+
QL_FAIL("calibration strategy '" << s << "' not recognized.");
8385
}
8486

8587
std::ostream& operator<<(std::ostream& oss, const CalibrationStrategy& type) {
8688
if (type == CalibrationStrategy::CoterminalATM)
8789
oss << "CoterminalAtm";
8890
else if (type == CalibrationStrategy::CoterminalDealStrike)
8991
oss << "CoterminalDealStrike";
92+
else if (type == CalibrationStrategy::DeltaGammaAdjusted)
93+
oss << "DeltaGammaAdjusted";
9094
else if (type == CalibrationStrategy::UnderlyingATM)
9195
oss << "UnderlyingAtm";
9296
else if (type == CalibrationStrategy::UnderlyingDealStrike)

OREData/ored/model/irmodeldata.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ enum class CalibrationType {
6666
};
6767

6868
//! Supported calibration strategies
69-
enum class CalibrationStrategy { CoterminalATM, CoterminalDealStrike, UnderlyingATM, UnderlyingDealStrike, None };
69+
enum class CalibrationStrategy { CoterminalATM, CoterminalDealStrike, UnderlyingATM, UnderlyingDealStrike, DeltaGammaAdjusted, None };
7070

7171
//! Convert calibration type string into enumerated class value
7272
CalibrationType parseCalibrationType(const string& s);

OREData/ored/portfolio/builders/deltagammaengines.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ namespace data {
2525

2626
QuantLib::ext::shared_ptr<PricingEngine>
2727
EuropeanSwaptionEngineBuilderDeltaGamma::engineImpl(const string& id, const string& key, const std::vector<Date>& dates,
28-
const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
28+
const std::vector<Date>& maturities, const std::vector<Real>& strikes, const bool isAmerican,
2929
const std::string& discountCurve, const std::string& securitySpread) {
3030

3131
std::vector<Time> bucketTimesDeltaGamma = parseListOfValues<Time>(engineParameter("BucketTimesDeltaGamma"), &parseReal);

OREData/ored/portfolio/builders/deltagammaengines.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ class EuropeanSwaptionEngineBuilderDeltaGamma : public SwaptionEngineBuilder {
202202

203203
protected:
204204
QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const string& id, const string& key, const std::vector<Date>& dates,
205-
const Date& maturity, const std::vector<Real>& strikes,
205+
const std::vector<Date>& maturities, const std::vector<Real>& strikes,
206206
const bool isAmerican, const std::string& discountCurve,
207207
const std::string& securitySpread) override;
208208
};

OREData/ored/portfolio/builders/swaption.cpp

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ namespace data {
7272

7373
QuantLib::ext::shared_ptr<PricingEngine>
7474
EuropeanSwaptionEngineBuilder::engineImpl(const string& id, const string& key, const std::vector<Date>& dates,
75-
const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
75+
const std::vector<Date>& maturities, const std::vector<Real>& strikes, const bool isAmerican,
7676
const std::string& discountCurve, const std::string& securitySpread) {
7777
QuantLib::ext::shared_ptr<IborIndex> index;
7878
string ccyCode = tryParseIborIndex(key, index) ? index->currency().code() : key;
@@ -88,7 +88,7 @@ EuropeanSwaptionEngineBuilder::engineImpl(const string& id, const string& key, c
8888

8989
QuantLib::ext::shared_ptr<QuantExt::LGM> LGMSwaptionEngineBuilder::model(const string& id, const string& key,
9090
const std::vector<Date>& expiries,
91-
const Date& maturity, const std::vector<Real>& strikes,
91+
const std::vector<Date>& maturities, const std::vector<Real>& strikes,
9292
const bool isAmerican) {
9393
QuantLib::ext::shared_ptr<IborIndex> index;
9494
std::string ccy = tryParseIborIndex(key, index) ? index->currency().code() : key;
@@ -118,6 +118,7 @@ QuantLib::ext::shared_ptr<QuantExt::LGM> LGMSwaptionEngineBuilder::model(const s
118118
std::vector<std::pair<CalibrationType, CalibrationStrategy>> validCalPairs = {
119119
{CalibrationType::None, CalibrationStrategy::None},
120120
{CalibrationType::Bootstrap, CalibrationStrategy::CoterminalATM},
121+
{CalibrationType::Bootstrap, CalibrationStrategy::DeltaGammaAdjusted},
121122
{CalibrationType::Bootstrap, CalibrationStrategy::CoterminalDealStrike},
122123
{CalibrationType::BestFit, CalibrationStrategy::CoterminalATM},
123124
{CalibrationType::BestFit, CalibrationStrategy::CoterminalDealStrike}};
@@ -130,7 +131,7 @@ QuantLib::ext::shared_ptr<QuantExt::LGM> LGMSwaptionEngineBuilder::model(const s
130131
// compute horizon shift
131132
Real shiftHorizon = parseReal(modelParameter("ShiftHorizon", {}, false, "0.5"));
132133
Date today = Settings::instance().evaluationDate();
133-
shiftHorizon = ActualActual(ActualActual::ISDA).yearFraction(today, maturity) * shiftHorizon;
134+
shiftHorizon = ActualActual(ActualActual::ISDA).yearFraction(today, maturities.back()) * shiftHorizon;
134135

135136
// Default: no calibration, constant lambda and sigma from engine configuration
136137
data->reset();
@@ -150,18 +151,24 @@ QuantLib::ext::shared_ptr<QuantExt::LGM> LGMSwaptionEngineBuilder::model(const s
150151

151152
std::vector<Date> effExpiries;
152153
std::vector<Real> effStrikes;
154+
std::vector<Date> effMaturities;
155+
153156
if (!isAmerican) {
154157
effExpiries = expiries;
155158
effStrikes = strikes;
159+
effMaturities = maturities;
156160
} else {
157-
QL_REQUIRE(expiries.size() == 2 && strikes.size() == 2,
161+
QL_REQUIRE(expiries.size() == 2 && strikes.size() == 2 && maturities.size() ==2,
158162
"LGMBermudanAmericanSwaptionEngineBuilder::model(): expected 2 expiries and strikes for exercise "
159163
"style 'American', got "
160164
<< expiries.size() << " expiries and " << strikes.size() << " strikes.");
161165
// keep one calibration instrument per reference grid interval
162166
DateGrid grid(referenceCalibrationGrid);
163167
std::copy_if(grid.dates().begin(), grid.dates().end(), std::back_inserter(effExpiries),
164168
[&expiries](const Date& d) { return d >= expiries[0] && d < expiries[1]; });
169+
170+
effMaturities.resize(effExpiries.size(), maturities.back());
171+
165172
// simple linear interpolation of calibration strikes between endpoints, this can be refined obviously // TODO
166173
effStrikes.resize(effExpiries.size(), Null<Real>());
167174
if (strikes[0] != Null<Real>() && strikes[1] != Null<Real>()) {
@@ -175,17 +182,20 @@ QuantLib::ext::shared_ptr<QuantExt::LGM> LGMSwaptionEngineBuilder::model(const s
175182
}
176183

177184
if (calibrationStrategy == CalibrationStrategy::CoterminalATM ||
178-
calibrationStrategy == CalibrationStrategy::CoterminalDealStrike) {
185+
calibrationStrategy == CalibrationStrategy::CoterminalDealStrike ||
186+
calibrationStrategy == CalibrationStrategy::DeltaGammaAdjusted) {
179187
DLOG("Build LgmData for co-terminal specification");
188+
180189
vector<string> expiryDates, termDates;
181190
for (Size i = 0; i < effExpiries.size(); ++i) {
182191
expiryDates.push_back(to_string(effExpiries[i]));
183-
termDates.push_back(to_string(maturity));
192+
termDates.push_back(to_string(effMaturities[i]));
184193
}
185194
data->optionExpiries() = expiryDates;
186195
data->optionTerms() = termDates;
187196
data->optionStrikes().resize(expiryDates.size(), "ATM");
188-
if (calibrationStrategy == CalibrationStrategy::CoterminalDealStrike) {
197+
if (calibrationStrategy == CalibrationStrategy::CoterminalDealStrike ||
198+
calibrationStrategy == CalibrationStrategy::DeltaGammaAdjusted) {
189199
for (Size i = 0; i < effExpiries.size(); ++i) {
190200
if (effStrikes[i] != Null<Real>())
191201
data->optionStrikes()[i] = std::to_string(effStrikes[i]);
@@ -249,11 +259,11 @@ QuantLib::ext::shared_ptr<QuantExt::LGM> LGMSwaptionEngineBuilder::model(const s
249259

250260
QuantLib::ext::shared_ptr<PricingEngine>
251261
LGMGridSwaptionEngineBuilder::engineImpl(const string& id, const string& key, const std::vector<Date>& expiries,
252-
const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
262+
const std::vector<Date>& maturities, const std::vector<Real>& strikes, const bool isAmerican,
253263
const std::string& discountCurve, const std::string& securitySpread) {
254264
DLOG("Building LGM Grid Bermudan/American Swaption engine for trade " << id);
255265

256-
QuantLib::ext::shared_ptr<QuantExt::LGM> lgm = model(id, key, expiries, maturity, strikes, isAmerican);
266+
QuantLib::ext::shared_ptr<QuantExt::LGM> lgm = model(id, key, expiries, maturities, strikes, isAmerican);
257267

258268
DLOG("Get engine data");
259269
Real sy = parseReal(engineParameter("sy"));
@@ -277,19 +287,19 @@ LGMGridSwaptionEngineBuilder::engineImpl(const string& id, const string& key, co
277287

278288
QuantLib::ext::shared_ptr<PricingEngine>
279289
LGMFDSwaptionEngineBuilder::engineImpl(const string& id, const string& key, const std::vector<Date>& expiries,
280-
const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
290+
const std::vector<Date>& maturities, const std::vector<Real>& strikes, const bool isAmerican,
281291
const std::string& discountCurve, const std::string& securitySpread) {
282292
DLOG("Building LGM FD Bermudan/American Swaption engine for trade " << id);
283293

284-
QuantLib::ext::shared_ptr<QuantExt::LGM> lgm = model(id, key, expiries, maturity, strikes, isAmerican);
294+
QuantLib::ext::shared_ptr<QuantExt::LGM> lgm = model(id, key, expiries, maturities, strikes, isAmerican);
285295

286296
DLOG("Get engine data");
287297
QuantLib::FdmSchemeDesc scheme = parseFdmSchemeDesc(engineParameter("Scheme"));
288298
Size stateGridPoints = parseInteger(engineParameter("StateGridPoints"));
289299
Size timeStepsPerYear = parseInteger(engineParameter("TimeStepsPerYear"));
290300
Real mesherEpsilon = parseReal(engineParameter("MesherEpsilon"));
291301

292-
Real maxTime = lgm->termStructure()->timeFromReference(maturity);
302+
Real maxTime = lgm->termStructure()->timeFromReference(maturities.back());
293303

294304
DLOG("Build engine (configuration " << configuration(MarketContext::pricing) << ")");
295305
QuantLib::ext::shared_ptr<IborIndex> index;
@@ -307,11 +317,11 @@ LGMFDSwaptionEngineBuilder::engineImpl(const string& id, const string& key, cons
307317

308318
QuantLib::ext::shared_ptr<PricingEngine>
309319
LGMMCSwaptionEngineBuilder::engineImpl(const string& id, const string& key, const std::vector<Date>& expiries,
310-
const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
320+
const std::vector<Date>& maturities, const std::vector<Real>& strikes, const bool isAmerican,
311321
const std::string& discountCurve, const std::string& securitySpread) {
312322
DLOG("Building MC Bermudan/American Swaption engine for trade " << id);
313323

314-
auto lgm = model(id, key, expiries, maturity, strikes, isAmerican);
324+
auto lgm = model(id, key, expiries, maturities, strikes, isAmerican);
315325

316326
// Build engine
317327
DLOG("Build engine (configuration " << configuration(MarketContext::pricing) << ")");
@@ -330,7 +340,7 @@ LGMMCSwaptionEngineBuilder::engineImpl(const string& id, const string& key, cons
330340

331341
QuantLib::ext::shared_ptr<PricingEngine>
332342
LGMAmcSwaptionEngineBuilder::engineImpl(const string& id, const string& key, const std::vector<Date>& expiries,
333-
const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
343+
const std::vector<Date>& maturities, const std::vector<Real>& strikes, const bool isAmerican,
334344
const std::string& discountCurve, const std::string& securitySpread) {
335345
QuantLib::ext::shared_ptr<IborIndex> index;
336346
std::string ccy = tryParseIborIndex(key, index) ? index->currency().code() : key;
@@ -357,7 +367,7 @@ LGMAmcSwaptionEngineBuilder::engineImpl(const string& id, const string& key, con
357367

358368
QuantLib::ext::shared_ptr<PricingEngine>
359369
AmcCgSwaptionEngineBuilder::engineImpl(const string& id, const string& key, const std::vector<Date>& dates,
360-
const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
370+
const std::vector<Date>& maturities, const std::vector<Real>& strikes, const bool isAmerican,
361371
const std::string& discountCurve, const std::string& securitySpread) {
362372
QL_REQUIRE(modelCg_ != nullptr, "AmcCgSwapEngineBuilder::engineImpl: modelcg is null");
363373
QuantLib::ext::shared_ptr<IborIndex> index;

0 commit comments

Comments
 (0)