Skip to content

Commit 78eb974

Browse files
pcaspersjenkins
authored andcommitted
QPR-12275 add support for trade disocunt curve and security spread to swaption
1 parent e73c57e commit 78eb974

4 files changed

Lines changed: 82 additions & 40 deletions

File tree

OREData/ored/model/lgmbuilder.hpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ class LgmBuilder : public QuantExt::ModelBuilder {
4949
public:
5050
/*! The configuration refers to the configuration to read swaption vol and swap index from the market.
5151
The discounting curve to price calibrating swaptions is derived from the swap index directly though,
52-
i.e. it is not read as a discount curve from the market. */
52+
i.e. it is not read as a discount curve from the market (except as a fallback in case we do not find
53+
the swap index). */
5354
LgmBuilder(const boost::shared_ptr<ore::data::Market>& market, const boost::shared_ptr<IrLgmData>& data,
5455
const std::string& configuration = Market::defaultConfiguration, Real bootstrapTolerance = 0.001,
5556
const bool continueOnError = false, const std::string& referenceCalibrationGrid = "",
@@ -62,9 +63,9 @@ class LgmBuilder : public QuantExt::ModelBuilder {
6263
std::string qualifier() { return data_->qualifier(); }
6364
std::string ccy() { return currency_; }
6465
boost::shared_ptr<QuantExt::LGM> model() const;
65-
/* the curve used to build the lgm parametrization, this can be relinked later outside this builder to e.g.
66-
calibrate fx processes using an xccy curve instead of the in currency curve that we use for the calibration of
67-
the LGM model in this builder */
66+
/* The curve used to build the lgm parametrization. This is initially the swap index discount curve (see ctor). It
67+
can be relinked later outside this builder to calibrate fx processes, for which one wants to use a xccy curve
68+
instead of the in-ccy curve that is used to calibrate the LGM model within this builder. */
6869
RelinkableHandle<YieldTermStructure> discountCurve() { return modelDiscountCurve_; }
6970
boost::shared_ptr<QuantExt::IrLgm1fParametrization> parametrization() const;
7071
std::vector<boost::shared_ptr<BlackCalibrationHelper>> swaptionBasket() const;

OREData/ored/portfolio/builders/swaption.cpp

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <ored/model/lgmbuilder.hpp>
2020
#include <ored/portfolio/builders/swaption.hpp>
2121
#include <ored/utilities/dategrid.hpp>
22+
#include <ored/utilities/marketdata.hpp>
2223
#include <ored/utilities/parsers.hpp>
2324
#include <ored/utilities/to_string.hpp>
2425

@@ -28,6 +29,7 @@
2829
#include <qle/pricingengines/numericlgmmultilegoptionengine.hpp>
2930

3031
#include <ql/methods/montecarlo/lsmbasissystem.hpp>
32+
#include <ql/termstructures/yield/zerospreadedtermstructure.hpp>
3133

3234
#include <set>
3335

@@ -61,14 +63,18 @@ boost::shared_ptr<PricingEngine> buildMcEngine(
6163
namespace ore {
6264
namespace data {
6365

64-
boost::shared_ptr<PricingEngine> EuropeanSwaptionEngineBuilder::engineImpl(const string& id, const string& key,
65-
const std::vector<Date>& dates,
66-
const Date& maturity,
67-
const std::vector<Real>& strikes,
68-
const bool isAmerican) {
66+
boost::shared_ptr<PricingEngine>
67+
EuropeanSwaptionEngineBuilder::engineImpl(const string& id, const string& key, const std::vector<Date>& dates,
68+
const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
69+
const std::string& discountCurve, const std::string& securitySpread) {
6970
boost::shared_ptr<IborIndex> index;
7071
string ccyCode = tryParseIborIndex(key, index) ? index->currency().code() : key;
71-
Handle<YieldTermStructure> yts = market_->discountCurve(ccyCode, configuration(MarketContext::pricing));
72+
Handle<YieldTermStructure> yts =
73+
discountCurve.empty() ? market_->discountCurve(ccyCode, configuration(MarketContext::pricing))
74+
: indexOrYieldCurve(market_, discountCurve, configuration(MarketContext::pricing));
75+
if (!securitySpread.empty())
76+
yts = Handle<YieldTermStructure>(boost::make_shared<ZeroSpreadedTermStructure>(
77+
yts, market_->securitySpread(securitySpread, configuration(MarketContext::pricing))));
7278
Handle<SwaptionVolatilityStructure> svts = market_->swaptionVol(key, configuration(MarketContext::pricing));
7379
return boost::make_shared<BlackMultiLegOptionEngine>(yts, svts);
7480
}
@@ -225,11 +231,10 @@ boost::shared_ptr<QuantExt::LGM> LGMSwaptionEngineBuilder::model(const string& i
225231
return model;
226232
}
227233

228-
boost::shared_ptr<PricingEngine> LGMGridSwaptionEngineBuilder::engineImpl(const string& id, const string& key,
229-
const std::vector<Date>& expiries,
230-
const Date& maturity,
231-
const std::vector<Real>& strikes,
232-
const bool isAmerican) {
234+
boost::shared_ptr<PricingEngine>
235+
LGMGridSwaptionEngineBuilder::engineImpl(const string& id, const string& key, const std::vector<Date>& expiries,
236+
const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
237+
const std::string& discountCurve, const std::string& securitySpread) {
233238
DLOG("Building LGM Grid Bermudan/American Swaption engine for trade " << id);
234239

235240
boost::shared_ptr<QuantExt::LGM> lgm = model(id, key, expiries, maturity, strikes, isAmerican);
@@ -244,14 +249,20 @@ boost::shared_ptr<PricingEngine> LGMGridSwaptionEngineBuilder::engineImpl(const
244249
DLOG("Build engine (configuration " << configuration(MarketContext::pricing) << ")");
245250
boost::shared_ptr<IborIndex> index;
246251
std::string ccy = tryParseIborIndex(key, index) ? index->currency().code() : key;
252+
Handle<YieldTermStructure> yts =
253+
discountCurve.empty() ? market_->discountCurve(ccy, configuration(MarketContext::pricing))
254+
: indexOrYieldCurve(market_, discountCurve, configuration(MarketContext::pricing));
255+
if (!securitySpread.empty())
256+
yts = Handle<YieldTermStructure>(boost::make_shared<ZeroSpreadedTermStructure>(
257+
yts, market_->securitySpread(securitySpread, configuration(MarketContext::pricing))));
247258
return boost::make_shared<QuantExt::NumericLgmMultiLegOptionEngine>(
248-
lgm, sy, ny, sx, nx, market_->discountCurve(ccy, configuration(MarketContext::pricing)),
249-
isAmerican ? parseInteger(modelParameter("ExerciseTimeStepsPerYear")) : 0);
259+
lgm, sy, ny, sx, nx, yts, isAmerican ? parseInteger(modelParameter("ExerciseTimeStepsPerYear")) : 0);
250260
}
251261

252262
boost::shared_ptr<PricingEngine>
253263
LGMFDSwaptionEngineBuilder::engineImpl(const string& id, const string& key, const std::vector<Date>& expiries,
254-
const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican) {
264+
const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
265+
const std::string& discountCurve, const std::string& securitySpread) {
255266
DLOG("Building LGM FD Bermudan/American Swaption engine for trade " << id);
256267

257268
boost::shared_ptr<QuantExt::LGM> lgm = model(id, key, expiries, maturity, strikes, isAmerican);
@@ -267,15 +278,21 @@ LGMFDSwaptionEngineBuilder::engineImpl(const string& id, const string& key, cons
267278
DLOG("Build engine (configuration " << configuration(MarketContext::pricing) << ")");
268279
boost::shared_ptr<IborIndex> index;
269280
std::string ccy = tryParseIborIndex(key, index) ? index->currency().code() : key;
281+
Handle<YieldTermStructure> yts =
282+
discountCurve.empty() ? market_->discountCurve(ccy, configuration(MarketContext::pricing))
283+
: indexOrYieldCurve(market_, discountCurve, configuration(MarketContext::pricing));
284+
if (!securitySpread.empty())
285+
yts = Handle<YieldTermStructure>(boost::make_shared<ZeroSpreadedTermStructure>(
286+
yts, market_->securitySpread(securitySpread, configuration(MarketContext::pricing))));
270287
return boost::make_shared<QuantExt::NumericLgmMultiLegOptionEngine>(
271-
lgm, maxTime, scheme, stateGridPoints, timeStepsPerYear, mesherEpsilon,
272-
market_->discountCurve(ccy, configuration(MarketContext::pricing)),
288+
lgm, maxTime, scheme, stateGridPoints, timeStepsPerYear, mesherEpsilon, yts,
273289
isAmerican ? parseInteger(modelParameter("ExerciseTimeStepsPerYear")) : 0);
274290
}
275291

276292
boost::shared_ptr<PricingEngine>
277293
LGMMCSwaptionEngineBuilder::engineImpl(const string& id, const string& key, const std::vector<Date>& expiries,
278-
const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican) {
294+
const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
295+
const std::string& discountCurve, const std::string& securitySpread) {
279296
DLOG("Building MC Bermudan/American Swaption engine for trade " << id);
280297

281298
auto lgm = model(id, key, expiries, maturity, strikes, isAmerican);
@@ -284,15 +301,21 @@ LGMMCSwaptionEngineBuilder::engineImpl(const string& id, const string& key, cons
284301
DLOG("Build engine (configuration " << configuration(MarketContext::pricing) << ")");
285302
boost::shared_ptr<IborIndex> index;
286303
std::string ccy = tryParseIborIndex(key, index) ? index->currency().code() : key;
287-
auto discountCurve = market_->discountCurve(ccy, configuration(MarketContext::pricing));
304+
Handle<YieldTermStructure> yts =
305+
discountCurve.empty() ? market_->discountCurve(ccy, configuration(MarketContext::pricing))
306+
: indexOrYieldCurve(market_, discountCurve, configuration(MarketContext::pricing));
307+
if (!securitySpread.empty())
308+
yts = Handle<YieldTermStructure>(boost::make_shared<ZeroSpreadedTermStructure>(
309+
yts, market_->securitySpread(securitySpread, configuration(MarketContext::pricing))));
288310
return buildMcEngine([this](const std::string& p, const std::vector<std::string>& q, const bool m,
289311
const std::string& d) { return this->engineParameter(p, q, m, d); },
290-
lgm, discountCurve, std::vector<Date>(), std::vector<Size>());
312+
lgm, yts, std::vector<Date>(), std::vector<Size>());
291313
} // LgmMc engineImpl()
292314

293315
boost::shared_ptr<PricingEngine>
294316
LGMAmcSwaptionEngineBuilder::engineImpl(const string& id, const string& key, const std::vector<Date>& expiries,
295-
const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican) {
317+
const Date& maturity, const std::vector<Real>& strikes, const bool isAmerican,
318+
const std::string& discountCurve, const std::string& securitySpread) {
296319
boost::shared_ptr<IborIndex> index;
297320
std::string ccy = tryParseIborIndex(key, index) ? index->currency().code() : key;
298321
Currency curr = parseCurrency(ccy);
@@ -306,11 +329,15 @@ LGMAmcSwaptionEngineBuilder::engineImpl(const string& id, const string& key, con
306329

307330
// Build engine
308331
DLOG("Build engine (configuration " << configuration(MarketContext::pricing) << ")");
309-
// we assume that the given cam has pricing discount curves attached already
310-
Handle<YieldTermStructure> discountCurve;
332+
Handle<YieldTermStructure> yts =
333+
discountCurve.empty() ? market_->discountCurve(ccy, configuration(MarketContext::pricing))
334+
: indexOrYieldCurve(market_, discountCurve, configuration(MarketContext::pricing));
335+
if (!securitySpread.empty())
336+
yts = Handle<YieldTermStructure>(boost::make_shared<ZeroSpreadedTermStructure>(
337+
yts, market_->securitySpread(securitySpread, configuration(MarketContext::pricing))));
311338
return buildMcEngine([this](const std::string& p, const std::vector<std::string>& q, const bool m,
312339
const std::string& d) { return this->engineParameter(p, q, m, d); },
313-
lgm, discountCurve, simulationDates_, modelIndex);
340+
lgm, yts, simulationDates_, modelIndex);
314341
} // LgmCam engineImpl
315342

316343
} // namespace data

OREData/ored/portfolio/builders/swaption.hpp

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,15 @@ namespace data {
3838
//! Swaption engine builder base class
3939
class SwaptionEngineBuilder
4040
: public CachingPricingEngineBuilder<string, const string&, const string&, const std::vector<Date>&, const Date&,
41-
const std::vector<Real>&, const bool> {
41+
const std::vector<Real>&, const bool, const string&, const string&> {
4242
public:
4343
SwaptionEngineBuilder(const string& model, const string& engine, const set<string>& tradeTypes)
4444
: CachingEngineBuilder(model, engine, tradeTypes) {}
4545

4646
protected:
4747
string keyImpl(const string& id, const string& key, const std::vector<Date>& dates, const Date& maturity,
48-
const std::vector<Real>& strikes, const bool isAmerican) override {
48+
const std::vector<Real>& strikes, const bool isAmerican, const std::string& discountCurve,
49+
const std::string& securitySpread) override {
4950
return id;
5051
}
5152
};
@@ -61,7 +62,8 @@ class EuropeanSwaptionEngineBuilder final : public SwaptionEngineBuilder {
6162
private:
6263
boost::shared_ptr<PricingEngine> engineImpl(const string& id, const string& key, const std::vector<Date>& dates,
6364
const Date& maturity, const std::vector<Real>& strikes,
64-
const bool isAmerican) override;
65+
const bool isAmerican, const std::string& discountCurve,
66+
const std::string& securitySpread) override;
6567
};
6668

6769
//! Abstract LGMSwaptionEngineBuilder class
@@ -84,7 +86,8 @@ class LGMGridSwaptionEngineBuilder final : public LGMSwaptionEngineBuilder {
8486
private:
8587
boost::shared_ptr<PricingEngine> engineImpl(const string& id, const string& key, const std::vector<Date>& dates,
8688
const Date& maturity, const std::vector<Real>& strikes,
87-
const bool isAmerican) override;
89+
const bool isAmerican, const std::string& discountCurve,
90+
const std::string& securitySpread) override;
8891
};
8992

9093
//! Implementation of BermudanAmericanSwaptionEngineBuilder using LGM FD pricer
@@ -95,7 +98,8 @@ class LGMFDSwaptionEngineBuilder final : public LGMSwaptionEngineBuilder {
9598
private:
9699
boost::shared_ptr<PricingEngine> engineImpl(const string& id, const string& key, const std::vector<Date>& dates,
97100
const Date& maturity, const std::vector<Real>& strikes,
98-
const bool isAmerican) override;
101+
const bool isAmerican, const std::string& discountCurve,
102+
const std::string& securitySpread) override;
99103
};
100104

101105
//! Implementation of LGMBermudanAmericanSwaptionEngineBuilder using MC pricer
@@ -106,7 +110,8 @@ class LGMMCSwaptionEngineBuilder final : public LGMSwaptionEngineBuilder {
106110
private:
107111
boost::shared_ptr<PricingEngine> engineImpl(const string& id, const string& key, const std::vector<Date>& dates,
108112
const Date& maturity, const std::vector<Real>& strikes,
109-
const bool isAmerican) override;
113+
const bool isAmerican, const std::string& discountCurve,
114+
const std::string& securitySpread) override;
110115
};
111116

112117
// Implementation of BermudanAmericanSwaptionEngineBuilder for external cam, with additional simulation dates (AMC)
@@ -118,12 +123,14 @@ class LGMAmcSwaptionEngineBuilder final : public LGMSwaptionEngineBuilder {
118123

119124
private:
120125
string keyImpl(const string& id, const string& ccy, const std::vector<Date>& dates, const Date& maturity,
121-
const std::vector<Real>& strikes, const bool isAmerican) override {
122-
return ccy + "_" + std::to_string(isAmerican);
126+
const std::vector<Real>& strikes, const bool isAmerican, const std::string& discountCurve,
127+
const std::string& securitySpread) override {
128+
return ccy + "_" + std::to_string(isAmerican) + discountCurve + securitySpread;
123129
}
124130
boost::shared_ptr<PricingEngine> engineImpl(const string& id, const string& key, const std::vector<Date>& dates,
125131
const Date& maturity, const std::vector<Real>& strikes,
126-
const bool isAmerican) override;
132+
const bool isAmerican, const std::string& discountCurve,
133+
const std::string& securitySpread) override;
127134

128135
const boost::shared_ptr<QuantExt::CrossAssetModel> cam_;
129136
const std::vector<Date> simulationDates_;

0 commit comments

Comments
 (0)