Skip to content

Commit acab1c3

Browse files
committed
Merge branch 'feature/QPR-13666' into 'master'
QPR-13666 enable different conventions per given par instrument Closes QPR-13666 See merge request qs/oreplus!3085
2 parents b1a5c92 + feec697 commit acab1c3

4 files changed

Lines changed: 78 additions & 98 deletions

File tree

Docs/UserGuide/parameterisation/sensitivity.tex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,9 @@ \subsubsection*{Par Sensitivity Analysis}
341341
ParConversion Fields:
342342
343343
\begin{itemize}
344-
\item \textbf{Instruments} a comma separated list of par instrument types, see below for the possible values.
344+
\item \textbf{Instruments} a comma separated list of par instrument types, see below for the possible values. The
345+
3-letter instrument code can be extended by an arbitrary suffix to reference different conventions. For example, you
346+
can use FRA1, FRA2 in the instrument list to build FRA instruments with different convention ids FRA1, FRA2.
345347
\item \textbf{DiscountCurve} \textit{optional}: discount curve used for pricing the par instrument.
346348
\item \textbf{RateComputationPeriod} \textit{optional}: required for OIS CapFloors, sepcify the period of the optionlet.
347349
\end{itemize}

Docs/UserGuide/pricing/ir_capfloor.tex

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,10 @@ \subsubsection{Pricing of an backward looking RFR caplet}
150150

151151
and which allows to price an RFR caplet as if it was a standard Ibor caplet with caplet volatility
152152
$\sigma_\text{eff}(t_1)$ and fixing time $t_1$. We do not use $\sigma_\text{eff}$ to represent caplet volatilities in
153-
bootstrapped or proxied caplet volatility surfaces for RFR underlyings. We also do not report $\sigma_\text{eff}$ as the
154-
pricing vol in the cashflow report. Instead we use $\sigma(t_0)$ in all cases (where we set $\sigma(t_0) := \sigma(0)$
155-
for $t_0 < 0$).
153+
bootstrapped or proxied caplet volatility surfaces for RFR underlyings. Instead we use $\sigma(t_0)$, where we set
154+
$\sigma(t_0) := \sigma(0)$ for $t_0 < 0$. However we report $\sigma_\text{eff}$ as ``EffectiveCapVolatility'',
155+
``EffectiveFloorVolatility'' in the cashflow report, in addition to ``CapVolatility'', ``FloorVolatility'' referring to
156+
$\sigma(t_0)$.
156157

157158
\subsubsection{Pricing of a SIFMA caplet}
158159

OREAnalytics/orea/engine/parsensitivityinstrumentbuilder.cpp

Lines changed: 62 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,42 @@ using boost::numeric::ublas::element_prod;
8181
namespace ore {
8282
namespace analytics {
8383

84+
std::pair<QuantLib::ext::shared_ptr<Instrument>, Date> ParSensitivityInstrumentBuilder::makeInstrument(
85+
const std::string& instType, const QuantLib::Date& asof, const QuantLib::ext::shared_ptr<Market>& market,
86+
string ccy, string otherCcy, string curveName, string yieldCurveName, string equityForecastCurveName, Period term,
87+
const QuantLib::ext::shared_ptr<Convention>& convention, bool singleCurve,
88+
std::set<ore::analytics::RiskFactorKey>& parHelperDependencies, std::set<std::string>& removeTodaysFixingIndices,
89+
const string& expDiscountCurve, const string& marketConfiguration) const {
90+
string instType3 = instType.substr(0, 3);
91+
if (instType3 == "IRS")
92+
return makeSwap(market, ccy, curveName, yieldCurveName, equityForecastCurveName, term, convention, singleCurve,
93+
parHelperDependencies, removeTodaysFixingIndices, expDiscountCurve, marketConfiguration);
94+
else if (instType3 == "DEP")
95+
return makeDeposit(asof, market, ccy, curveName, yieldCurveName, equityForecastCurveName, term, convention,
96+
marketConfiguration);
97+
else if (instType3 == "FRA")
98+
return makeFRA(asof, market, ccy, curveName, yieldCurveName, equityForecastCurveName, term, convention,
99+
marketConfiguration);
100+
else if (instType3 == "OIS")
101+
return makeOIS(market, ccy, curveName, yieldCurveName, equityForecastCurveName, term, convention, singleCurve,
102+
parHelperDependencies, removeTodaysFixingIndices, expDiscountCurve, marketConfiguration);
103+
else if (instType3 == "XBS")
104+
return makeCrossCcyBasisSwap(market, otherCcy, ccy, term, convention, parHelperDependencies,
105+
removeTodaysFixingIndices, marketConfiguration);
106+
else if (instType3 == "FXF")
107+
return makeFxForward(market, otherCcy, ccy, term, convention, parHelperDependencies, marketConfiguration);
108+
else if (instType3 == "TBS")
109+
return makeTenorBasisSwap(asof, market, ccy, std::string(), std::string(), std::string(), std::string(), term,
110+
convention, singleCurve, parHelperDependencies, removeTodaysFixingIndices,
111+
expDiscountCurve, marketConfiguration);
112+
else if (instType3 == "BMA")
113+
return makeBMABasisSwap(asof, market, ccy, std::string(), std::string(), std::string(), std::string(), term,
114+
convention, singleCurve, parHelperDependencies, removeTodaysFixingIndices,
115+
expDiscountCurve, marketConfiguration);
116+
else
117+
return std::make_pair(nullptr, Date());
118+
}
119+
84120
void ParSensitivityInstrumentBuilder::createParInstruments(
85121
ParSensitivityInstrumentBuilder::Instruments& instruments, const QuantLib::Date& asof,
86122
const QuantLib::ext::shared_ptr<ore::analytics::ScenarioSimMarketParameters>& simMarketParams,
@@ -166,39 +202,13 @@ void ParSensitivityInstrumentBuilder::createParInstruments(
166202
QuantLib::ext::shared_ptr<Convention> convention = conventions->get(conventionsMap[instType]);
167203
QL_REQUIRE(convention != nullptr,
168204
"ParSensitivityInstrumentBuilder::createParInstruments(): convention is empty");
169-
if (instType == "IRS")
170-
ret = makeSwap(simMarket, ccy, indexName, yieldCurveName, equityForecastCurveName, term,
171-
convention, singleCurve, parHelperDependencies[key],
172-
instruments.removeTodaysFixingIndices_, data.discountCurve, marketConfiguration);
173-
else if (instType == "DEP")
174-
ret = makeDeposit(asof, simMarket, ccy, indexName, yieldCurveName, equityForecastCurveName,
175-
term, convention, marketConfiguration);
176-
else if (instType == "FRA")
177-
ret = makeFRA(asof, simMarket, ccy, indexName, yieldCurveName, equityForecastCurveName, term,
178-
convention, marketConfiguration);
179-
else if (instType == "OIS")
180-
ret = makeOIS(simMarket, ccy, indexName, yieldCurveName, equityForecastCurveName, term,
181-
convention, singleCurve, parHelperDependencies[key],
182-
instruments.removeTodaysFixingIndices_, data.discountCurve, marketConfiguration);
183-
else if (instType == "XBS")
184-
ret = makeCrossCcyBasisSwap(
185-
simMarket, data.otherCurrency.empty() ? simMarketParams->baseCcy() : data.otherCurrency,
186-
ccy, term, convention, parHelperDependencies[key], instruments.removeTodaysFixingIndices_,
187-
marketConfiguration);
188-
else if (instType == "FXF")
189-
ret = makeFxForward(
190-
simMarket, data.otherCurrency.empty() ? simMarketParams->baseCcy() : data.otherCurrency,
191-
ccy, term, convention, parHelperDependencies[key], marketConfiguration);
192-
else if (instType == "TBS")
193-
ret = makeTenorBasisSwap(asof, simMarket, ccy, "", "", "", "", term, convention, singleCurve,
194-
parHelperDependencies[key], instruments.removeTodaysFixingIndices_,
195-
data.discountCurve, marketConfiguration);
196-
else if (instType == "BMA")
197-
ret = makeBMABasisSwap(asof, simMarket, ccy, "", "", "", "", term, convention, singleCurve,
198-
parHelperDependencies[key], instruments.removeTodaysFixingIndices_,
199-
data.discountCurve, marketConfiguration);
200-
else
201-
recognised = false;
205+
ret =
206+
makeInstrument(instType, asof, simMarket, ccy,
207+
data.otherCurrency.empty() ? simMarketParams->baseCcy() : data.otherCurrency,
208+
indexName, yieldCurveName, equityForecastCurveName, term, convention,
209+
singleCurve, parHelperDependencies[key], instruments.removeTodaysFixingIndices_,
210+
data.discountCurve, marketConfiguration);
211+
recognised = ret.first != nullptr;
202212
} catch (const std::exception& e) {
203213
skipped = true;
204214
if (continueOnError) {
@@ -268,37 +278,12 @@ void ParSensitivityInstrumentBuilder::createParInstruments(
268278
"ParSensitivityInstrumentBuilder::createParInstruments(): conventions not found for ccy "
269279
<< ccy << " and instrument type " << instType);
270280
QuantLib::ext::shared_ptr<Convention> convention = conventions->get(conventionsMap[instType]);
271-
272-
if (instType == "IRS")
273-
ret = makeSwap(simMarket, ccy, "", curveName, equityForecastCurveName, term, convention,
274-
singleCurve, parHelperDependencies[key], instruments.removeTodaysFixingIndices_,
275-
data.discountCurve, marketConfiguration);
276-
else if (instType == "DEP")
277-
ret = makeDeposit(asof, simMarket, ccy, "", curveName, equityForecastCurveName, term,
278-
convention, marketConfiguration);
279-
else if (instType == "FRA")
280-
ret = makeFRA(asof, simMarket, ccy, "", curveName, equityForecastCurveName, term, convention,
281-
marketConfiguration);
282-
else if (instType == "OIS")
283-
ret = makeOIS(simMarket, ccy, "", curveName, equityForecastCurveName, term, convention,
284-
singleCurve, parHelperDependencies[key], instruments.removeTodaysFixingIndices_,
285-
data.discountCurve, marketConfiguration);
286-
else if (instType == "TBS")
287-
ret = makeTenorBasisSwap(asof, simMarket, ccy, "", "", curveName, "", term, convention,
288-
singleCurve, parHelperDependencies[key],
289-
instruments.removeTodaysFixingIndices_, data.discountCurve,
290-
marketConfiguration);
291-
else if (instType == "XBS")
292-
ret = makeCrossCcyBasisSwap(
293-
simMarket, data.otherCurrency.empty() ? simMarketParams->baseCcy() : data.otherCurrency,
294-
ccy, term, convention, parHelperDependencies[key], instruments.removeTodaysFixingIndices_,
295-
marketConfiguration);
296-
else if (instType == "BMA")
297-
ret = makeBMABasisSwap(asof, simMarket, ccy, "", "", "", "", term, convention, singleCurve,
298-
parHelperDependencies[key], instruments.removeTodaysFixingIndices_,
299-
data.discountCurve, marketConfiguration);
300-
else
301-
recognised = false;
281+
ret = makeInstrument(
282+
instType, asof, simMarket, ccy,
283+
data.otherCurrency.empty() ? simMarketParams->baseCcy() : data.otherCurrency, std::string(),
284+
curveName, equityForecastCurveName, term, convention, singleCurve, parHelperDependencies[key],
285+
instruments.removeTodaysFixingIndices_, data.discountCurve, marketConfiguration);
286+
recognised = ret.first != nullptr;
302287
} catch (const std::exception& e) {
303288
skipped = true;
304289
if (continueOnError) {
@@ -365,31 +350,13 @@ void ParSensitivityInstrumentBuilder::createParInstruments(
365350
"ParSensitivityInstrumentBuilder::createParInstruments(): conventions not found for ccy "
366351
<< ccy << " and instrument type " << instType);
367352
QuantLib::ext::shared_ptr<Convention> convention = conventions->get(conventionsMap[instType]);
368-
369-
if (instType == "IRS")
370-
ret = makeSwap(simMarket, ccy, indexName, yieldCurveName, equityForecastCurveName, term,
371-
convention, singleCurve, parHelperDependencies[key],
372-
instruments.removeTodaysFixingIndices_, data.discountCurve, marketConfiguration);
373-
else if (instType == "DEP")
374-
ret = makeDeposit(asof, simMarket, ccy, indexName, yieldCurveName, equityForecastCurveName,
375-
term, convention, marketConfiguration);
376-
else if (instType == "FRA")
377-
ret = makeFRA(asof, simMarket, ccy, indexName, yieldCurveName, equityForecastCurveName, term,
378-
convention, marketConfiguration);
379-
else if (instType == "OIS")
380-
ret = makeOIS(simMarket, ccy, indexName, yieldCurveName, equityForecastCurveName, term,
381-
convention, singleCurve, parHelperDependencies[key],
382-
instruments.removeTodaysFixingIndices_, data.discountCurve, marketConfiguration);
383-
else if (instType == "TBS")
384-
ret = makeTenorBasisSwap(asof, simMarket, ccy, "", "", "", "", term, convention, singleCurve,
385-
parHelperDependencies[key], instruments.removeTodaysFixingIndices_,
386-
data.discountCurve, marketConfiguration);
387-
else if (instType == "BMA")
388-
ret = makeBMABasisSwap(asof, simMarket, ccy, "", "", "", "", term, convention, singleCurve,
389-
parHelperDependencies[key], instruments.removeTodaysFixingIndices_,
390-
data.discountCurve, marketConfiguration);
391-
else
392-
recognised = false;
353+
ret =
354+
makeInstrument(instType, asof, simMarket, ccy,
355+
data.otherCurrency.empty() ? simMarketParams->baseCcy() : data.otherCurrency,
356+
indexName, yieldCurveName, equityForecastCurveName, term, convention,
357+
singleCurve, parHelperDependencies[key], instruments.removeTodaysFixingIndices_,
358+
data.discountCurve, marketConfiguration);
359+
recognised = ret.first != nullptr;
393360
} catch (const std::exception& e) {
394361
skipped = true;
395362
if (continueOnError) {
@@ -615,8 +582,8 @@ void ParSensitivityInstrumentBuilder::createParInstruments(
615582
"zero inflation curve "
616583
<< indexName << " and instrument type " << instType);
617584
QuantLib::ext::shared_ptr<Convention> convention = conventions->get(conventionsMap[instType]);
618-
619-
if (instType == "ZIS") {
585+
string instType3 = instType.substr(0, 3);
586+
if (instType3 == "ZIS") {
620587
auto tmp =
621588
makeYoyInflationSwap(simMarket, indexName, term, convention, singleCurve, true,
622589
parHelperDependencies[key], data.discountCurve, marketConfiguration);
@@ -627,7 +594,7 @@ void ParSensitivityInstrumentBuilder::createParInstruments(
627594
Date latestRelevantDate = std::max(helper->maturityDate(), lastCoupon->fixingDate());
628595
instruments.yoyInflationPillars_[indexName].push_back((latestRelevantDate - asof) * Days);
629596
parHelpers[key] = tmp;
630-
} else if (instType == "YYS") {
597+
} else if (instType3 == "YYS") {
631598
auto tmp =
632599
makeYoyInflationSwap(simMarket, indexName, term, convention, singleCurve, false,
633600
parHelperDependencies[key], data.discountCurve, marketConfiguration);
@@ -690,10 +657,11 @@ void ParSensitivityInstrumentBuilder::createParInstruments(
690657
<< indexName << " and instrument type " << instType);
691658
QuantLib::ext::shared_ptr<Convention> convention = conventions->get(conventionsMap[instType]);
692659
Period term = data.shiftExpiries[k];
693-
if (instType == "ZIS") {
660+
string instType3 = instType.substr(0, 3);
661+
if (instType3 == "ZIS") {
694662
makeYoYCapFloor(instruments, simMarket, indexName, term, strike, convention, singleCurve,
695663
true, data.discountCurve, key, marketConfiguration);
696-
} else if (instType == "YYS") {
664+
} else if (instType3 == "YYS") {
697665
makeYoYCapFloor(instruments, simMarket, indexName, term, strike, convention, singleCurve,
698666
false, data.discountCurve, key, marketConfiguration);
699667
} else

OREAnalytics/orea/engine/parsensitivityinstrumentbuilder.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,15 @@ class ParSensitivityInstrumentBuilder {
7575
const QuantLib::ext::shared_ptr<ore::analytics::Market>& simMarket = nullptr) const;
7676

7777
private:
78+
//! Dispatcher into the rate curve related makeXXX() methods below
79+
std::pair<QuantLib::ext::shared_ptr<Instrument>, Date>
80+
makeInstrument(const std::string& instType, const QuantLib::Date& asof,
81+
const QuantLib::ext::shared_ptr<Market>& market, string ccy, string otherCcy, string curveName,
82+
string yieldCurveName, string equityForecastCurveName, Period term,
83+
const QuantLib::ext::shared_ptr<Convention>& convention, bool singleCurve,
84+
std::set<ore::analytics::RiskFactorKey>& parHelperDependencies,
85+
std::set<std::string>& removeTodaysFixingIndices, const string& expDiscountCurve,
86+
const string& marketConfiguration) const;
7887
//! Create Deposit for implying par rate sensitivity from zero rate sensitivity
7988
std::pair<QuantLib::ext::shared_ptr<QuantLib::Instrument>, Date>
8089
makeDeposit(const QuantLib::Date& asof, const QuantLib::ext::shared_ptr<ore::data::Market>& market, std::string ccy,

0 commit comments

Comments
 (0)