Skip to content

Commit fc01a8a

Browse files
pcaspersjenkins
authored andcommitted
Merge branch 'master' of gitlab.acadiasoft.net:qs/oreplus into QPR-12306
1 parent 8220744 commit fc01a8a

12 files changed

Lines changed: 481 additions & 23 deletions

File tree

Docs/ScriptedTrade/docs/models.tex

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ \subsection{Pricing Engine Configuration}\label{pricingengine_config}
2727
<Parameter name="Model_SingleAssetOptionBwd(FX)">BlackScholes</Parameter>
2828
<Parameter name="Model_SingleAssetOptionBwd(COMM)">BlackScholes</Parameter>
2929
<Parameter name="Model_SingleUnderlyingIrOption">GaussianCam</Parameter>
30+
<Parameter name="Model_SingleUnderlyingIrOptionBwd">GaussianCam</Parameter>
3031
<Parameter name="Model_MultiUnderlyingIrOption">GaussianCam</Parameter>
3132
<Parameter name="Model_IrHybrid(EQ)">GaussianCam</Parameter>
3233
<Parameter name="Model_IrHybrid(FX)">GaussianCam</Parameter>
@@ -38,6 +39,7 @@ \subsection{Pricing Engine Configuration}\label{pricingengine_config}
3839
<Parameter name="Engine">MC</Parameter>
3940
<Parameter name="Samples">10000</Parameter>
4041
<Parameter name="StateGridPoints">200</Parameter>
42+
<Parameter name="StateGridPoints_SingleUnderlyingIrOptionBwd">50</Parameter>
4143
<Parameter name="MesherEpsilon">1E-4</Parameter>
4244
<Parameter name="MesherScaling">1.5</Parameter>
4345
<Parameter name="MesherConcentration">0.1</Parameter>
@@ -54,7 +56,7 @@ \subsection{Pricing Engine Configuration}\label{pricingengine_config}
5456
<Parameter name="Engine_SingleAssetOptionBwd(EQ)">FD</Parameter>
5557
<Parameter name="Engine_SingleAssetOptionBwd(FX)">FD</Parameter>
5658
<Parameter name="Engine_SingleAssetOptionBwd(COMM)">FD</Parameter>
57-
<Parameter name="Engine_SingleUnderlyingIrOption">6</Parameter>
59+
<Parameter name="Engine_SingleUnderlyingIrOption">FD</Parameter>
5860
<Parameter name="useAD_MultiAssetOptionAD(EQ)">true</Parameter>
5961
<Parameter name="useAD_MultiAssetOptionAD(FX)">true</Parameter>
6062
<Parameter name="useAD_MultiAssetOptionAD(COMM)">true</Parameter>
@@ -188,11 +190,15 @@ \subsection{BlackScholes model}\label{blackscholes}
188190
189191
See \ref{pricingengine_config} for the impact of the model parameter FullDynamicFx on the model setup.
190192
191-
TimeStepsPerYear are ignored if the correlation structure is trivial, because then the process can be discretised
193+
For MC TimeStepsPerYear are ignored if the correlation structure is trivial, because then the process can be discretised
192194
exactly. Otherwise the given time steps per year are used to build a grid on which covariance matrices are computed
193195
assuming a constant volatility between the grid points. The actual MC paths are evoloved using these covariance matrices
194196
on the original (non-refined) time grid, i.e. taking large, exact steps again.
195197
198+
For FD TimeStepsPerYear, StateGridPoints, MesherEpsilon, MesherScaling, MesherConcentration,
199+
MesehMaxConcentrationPoints, MesherIsStatic are used, see the description of these parameters for their detailled
200+
interpretation.
201+
196202
\smallskip
197203
Available Engine types: MC, FD
198204
@@ -229,8 +235,12 @@ \subsection{GaussianCam models}
229235
230236
See \ref{pricingengine_config} for the impact of the model parameters FullDynamicFx, FullDynamicIr on the model setup.
231237
238+
The FD model variant is supported for a single underlying IR model only. A single calibration strike is supported that
239+
can be specified for any IR model index appearing in the script. TimeStepsPerYear, StateGridPoints, MesherEpsilon are
240+
used, see the description of these parameters for their detailled interpretation.
241+
232242
\smallskip
233-
Available Engine types: MC
243+
Available Engine types: MC, FD
234244
235245
\subsection{Base Currency Determination}\label{baseccy_determination}
236246
@@ -312,12 +322,12 @@ \subsection{Calibration}\label{calibration}
312322
The usage of the calibration strikes is twofold:
313323
314324
\begin{itemize}
315-
\item For the determination of calibration strikes. This is only relevant / supported by the BlackScholes model, which
316-
will use the first strike from the list to read the volatility from the market term structure (if Calibration is set
317-
to Deal in the model parameters).
325+
\item For the determination of calibration strikes. This is only relevant / supported by the BlackScholes and
326+
GaussianCam-FD model, which will use the first strike from the list to read the volatility from the market term
327+
structure (if Calibration is set to Deal in the model parameters).
318328
\item To determine concentration points for an FD mesher if \verb+Engine+ is set to \verb+FD+. The first $n$ strikes are
319329
used as concentration points where $n$ is the minimum of specified strikes and the engine parameter
320-
\verb+MesherMaxConcentrationPoints+.
330+
\verb+MesherMaxConcentrationPoints+. This is supported by the BlackScholes model only.
321331
\end{itemize}
322332
323333
\subsection{FX tags and correlation curves}\label{fxtags_correlationcurves}

Docs/ScriptedTrade/scriptedtrade.tex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ \section*{Document History}
135135
01-07-2021 & Peter Caspers & add case $d1>d2$ for ABOVEPROB, BELOWPROB \\
136136
13-09-2021 & Nathaniel Volfango & update Index section and add note on payment currency \\
137137
11-02-2022 & Peter Caspers & extend FWDCOMP, add FWDAVG function (breaking change) \\
138-
22-06-2023 & Peter Caspers & add config flags for external devices
138+
22-06-2023 & Peter Caspers & add config flags for external devices \\
139+
12-02-2024 & Peter Caspers & additions for FD GaussianCam model
139140
\\ \hline
140141
\end{supertabular}
141142
\end{center}

OREData/ored/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ scripting/models/blackscholesbase.cpp
317317
scripting/models/blackscholescg.cpp
318318
scripting/models/blackscholescgbase.cpp
319319
scripting/models/fdblackscholesbase.cpp
320+
scripting/models/fdgaussiancam.cpp
320321
scripting/models/gaussiancam.cpp
321322
scripting/models/gaussiancamcg.cpp
322323
scripting/models/hwcg.cpp
@@ -704,6 +705,7 @@ scripting/models/blackscholescg.hpp
704705
scripting/models/blackscholescgbase.hpp
705706
scripting/models/dummymodel.hpp
706707
scripting/models/fdblackscholesbase.hpp
708+
scripting/models/fdgaussiancam.hpp
707709
scripting/models/gaussiancam.hpp
708710
scripting/models/gaussiancamcg.hpp
709711
scripting/models/hwcg.hpp

OREData/ored/ored.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@
345345
#include <ored/scripting/models/blackscholescgbase.hpp>
346346
#include <ored/scripting/models/dummymodel.hpp>
347347
#include <ored/scripting/models/fdblackscholesbase.hpp>
348+
#include <ored/scripting/models/fdgaussiancam.hpp>
348349
#include <ored/scripting/models/gaussiancam.hpp>
349350
#include <ored/scripting/models/gaussiancamcg.hpp>
350351
#include <ored/scripting/models/hwcg.hpp>

OREData/ored/portfolio/builders/scriptedtrade.cpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <ored/scripting/models/blackscholes.hpp>
2121
#include <ored/scripting/models/blackscholescg.hpp>
2222
#include <ored/scripting/models/fdblackscholesbase.hpp>
23+
#include <ored/scripting/models/fdgaussiancam.hpp>
2324
#include <ored/scripting/models/gaussiancam.hpp>
2425
#include <ored/scripting/models/gaussiancamcg.hpp>
2526
#include <ored/scripting/models/localvol.hpp>
@@ -51,6 +52,8 @@
5152

5253
#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
5354

55+
#include <boost/lexical_cast.hpp>
56+
5457
namespace ore {
5558
namespace data {
5659

@@ -233,6 +236,8 @@ ScriptedTradeEngineBuilder::engine(const std::string& id, const ScriptedTrade& s
233236
} else {
234237
buildGaussianCam(id, iborFallbackConfig, script.conditionalExpectationModelStates());
235238
}
239+
} else if (modelParam_ == "GaussianCam" && engineParam_ == "FD") {
240+
buildFdGaussianCam(id, iborFallbackConfig);
236241
} else {
237242
QL_FAIL("model '" << modelParam_ << "' / engine '" << engineParam_
238243
<< "' not recognised, expected BlackScholes/[MC|FD], LocalVolDupire/MC, "
@@ -1511,6 +1516,104 @@ void ScriptedTradeEngineBuilder::buildGaussianCam(const std::string& id, const I
15111516
modelBuilders_.insert(std::make_pair(id, camBuilder));
15121517
}
15131518

1519+
void ScriptedTradeEngineBuilder::buildFdGaussianCam(const std::string& id,
1520+
const IborFallbackConfig& iborFallbackConfig) {
1521+
1522+
Date referenceDate = modelCurves_.front()->referenceDate();
1523+
std::vector<Date> calibrationDates;
1524+
std::vector<std::string> calibrationExpiries, calibrationTerms;
1525+
1526+
for (auto const& d : simulationDates_) {
1527+
if (d > referenceDate) {
1528+
calibrationDates.push_back(d);
1529+
calibrationExpiries.push_back(ore::data::to_string(d));
1530+
// make sure the underlying swap has at least 1M to run, QL will throw otherwise during calibration
1531+
calibrationTerms.push_back(ore::data::to_string(std::max(d + 1 * Months, lastRelevantDate_)));
1532+
}
1533+
}
1534+
1535+
// calibration times (need one less than calibration dates)
1536+
std::vector<Real> calibrationTimes;
1537+
for (Size i = 0; i + 1 < calibrationDates.size(); ++i) {
1538+
calibrationTimes.push_back(modelCurves_.front()->timeFromReference(calibrationDates[i]));
1539+
}
1540+
1541+
// determine calibration strike
1542+
std::string calibrationStrike = "ATM";
1543+
if(calibration_ == "Deal") {
1544+
// first model index found in calibration strike spec and first specified strike therein is used
1545+
for (auto const& m : modelIrIndices_) {
1546+
if (auto f = calibrationStrikes_.find(m.first); f != calibrationStrikes_.end()) {
1547+
if(!f->second.empty()) {
1548+
calibrationStrike = boost::lexical_cast<std::string>(f->second.front());
1549+
}
1550+
}
1551+
}
1552+
}
1553+
1554+
// IR config
1555+
QL_REQUIRE(modelCcys_.size() == 1,
1556+
"ScriptedTradeEngineBuilder::buildFdGaussianCam(): only one ccy is supported, got "
1557+
<< modelCcys_.size());
1558+
1559+
auto config = boost::make_shared<IrLgmData>();
1560+
config->qualifier() = getFirstIrIndexOrCcy(modelCcys_.front(), irIndices_);
1561+
config->reversionType() = LgmData::ReversionType::HullWhite;
1562+
config->volatilityType() = LgmData::VolatilityType::Hagan;
1563+
config->calibrateH() = false;
1564+
config->hParamType() = ParamType::Constant;
1565+
config->hTimes() = std::vector<Real>();
1566+
config->shiftHorizon() =
1567+
modelCurves_.front()->timeFromReference(lastRelevantDate_) * 0.5; // TODO hardcode 0.5 here?
1568+
config->scaling() = 1.0;
1569+
std::string ccy = modelCcys_.front();
1570+
if (zeroVolatility_ ) {
1571+
DLOG("set up zero vol IrLgmData for currency '" << modelCcys_.front() << "'");
1572+
// zero vol
1573+
config->calibrationType() = CalibrationType::None;
1574+
config->hValues() = {0.0};
1575+
config->calibrateA() = false;
1576+
config->aParamType() = ParamType::Constant;
1577+
config->aTimes() = std::vector<Real>();
1578+
config->aValues() = {0.0};
1579+
} else {
1580+
DLOG("set up IrLgmData for currency '" << modelCcys_.front() << "'");
1581+
auto rev = irReversions_.find(modelCcys_.front());
1582+
QL_REQUIRE(rev != irReversions_.end(), "reversion for ccy " << modelCcys_.front() << " not found");
1583+
config->calibrationType() = CalibrationType::Bootstrap;
1584+
config->hValues() = {rev->second};
1585+
config->calibrateA() = true;
1586+
config->aParamType() = ParamType::Piecewise;
1587+
config->aTimes() = calibrationTimes;
1588+
config->aValues() = std::vector<Real>(calibrationTimes.size() + 1, 0.0030); // start value for optimiser
1589+
config->optionExpiries() = calibrationExpiries;
1590+
config->optionTerms() = calibrationTerms;
1591+
config->optionStrikes() = std::vector<std::string>(calibrationExpiries.size(), calibrationStrike);
1592+
}
1593+
1594+
std::string configurationInCcy = configuration(MarketContext::irCalibration);
1595+
std::string configurationXois = configuration(MarketContext::pricing);
1596+
1597+
auto camBuilder = boost::make_shared<CrossAssetModelBuilder>(
1598+
market_,
1599+
boost::make_shared<CrossAssetModelData>(
1600+
std::vector<boost::shared_ptr<IrModelData>>{config}, std::vector<boost::shared_ptr<FxBsData>>{},
1601+
std::vector<boost::shared_ptr<EqBsData>>{}, std::vector<boost::shared_ptr<InflationModelData>>{},
1602+
std::vector<boost::shared_ptr<CrLgmData>>{}, std::vector<boost::shared_ptr<CrCirData>>{},
1603+
std::vector<boost::shared_ptr<CommoditySchwartzData>>{}, 0,
1604+
std::map<CorrelationKey, QuantLib::Handle<QuantLib::Quote>>{}, bootstrapTolerance_, "LGM",
1605+
CrossAssetModel::Discretization::Exact),
1606+
configurationInCcy, configurationXois, configurationXois, configurationInCcy, configurationInCcy,
1607+
configurationXois, !calibrate_ || zeroVolatility_, continueOnCalibrationError_, referenceCalibrationGrid_,
1608+
SalvagingAlgorithm::Spectral, id);
1609+
1610+
model_ = boost::make_shared<FdGaussianCam>(camBuilder->model(), modelCcys_.front(), modelCurves_.front(),
1611+
modelIrIndices_, simulationDates_, modelSize_, timeStepsPerYear_,
1612+
mesherEpsilon_, iborFallbackConfig);
1613+
1614+
modelBuilders_.insert(std::make_pair(id, camBuilder));
1615+
}
1616+
15141617
void ScriptedTradeEngineBuilder::buildAMCCGModel(const std::string& id, const IborFallbackConfig& iborFallbackConfig,
15151618
const std::vector<std::string>& conditionalExpectationModelStates) {
15161619
// nothing to build really, the resulting model is exactly the input model

OREData/ored/portfolio/builders/scriptedtrade.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ class ScriptedTradeEngineBuilder : public EngineBuilder {
9191
void buildLocalVol(const std::string& id, const IborFallbackConfig& iborFallbackConfig);
9292
void buildGaussianCam(const std::string& id, const IborFallbackConfig& iborFallbackConfig,
9393
const std::vector<std::string>& conditionalExpectationModelStates);
94+
void buildFdGaussianCam(const std::string& id, const IborFallbackConfig& iborFallbackConfig);
9495
void buildGaussianCamAMC(const std::string& id, const IborFallbackConfig& iborFallbackConfig,
9596
const std::vector<std::string>& conditionalExpectationModelStates);
9697
void buildAMCCGModel(const std::string& id, const IborFallbackConfig& iborFallbackConfig,

OREData/ored/scripting/models/blackscholesbase.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ void BlackScholesBase::performCalculations() const {
123123

124124
std::vector<Real> times;
125125
for (auto const& d : effectiveSimulationDates_) {
126-
times.push_back(curves_.front()->timeFromReference(d));
126+
times.push_back(timeFromReference(d));
127127
}
128128

129129
timeGrid_ = model_->discretisationTimeGrid();

OREData/ored/scripting/models/fdblackscholesbase.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ void FdBlackScholesBase::performCalculations() const {
180180

181181
std::vector<Real> times;
182182
for (auto const& d : effectiveSimulationDates_) {
183-
times.push_back(curves_.front()->timeFromReference(d));
183+
times.push_back(timeFromReference(d));
184184
}
185185

186186
timeGrid_ = model_->discretisationTimeGrid();
@@ -330,7 +330,7 @@ RandomVariable FdBlackScholesBase::getIndexValue(const Size indexNo, const Date&
330330
}
331331

332332
// set the observation time in the result random variable
333-
res.setTime(curves_.front()->timeFromReference(d));
333+
res.setTime(timeFromReference(d));
334334

335335
// return the result
336336

@@ -407,7 +407,7 @@ RandomVariable FdBlackScholesBase::npv(const RandomVariable& amount, const Date&
407407
calculate();
408408

409409
Real t1 = amount.time();
410-
Real t0 = curves_.front()->timeFromReference(obsdate);
410+
Real t0 = timeFromReference(obsdate);
411411

412412
// handle case when amount is deterministic
413413

@@ -421,7 +421,6 @@ RandomVariable FdBlackScholesBase::npv(const RandomVariable& amount, const Date&
421421

422422
QL_REQUIRE(t1 != Null<Real>(),
423423
"FdBlackScholesBase::npv(): can not roll back amount wiithout time attached (to t0=" << t0 << ")");
424-
calculate();
425424

426425
// might throw if t0, t1 are not found in timeGrid_
427426

@@ -491,7 +490,7 @@ RandomVariable FdBlackScholesBase::pay(const RandomVariable& amount, const Date&
491490

492491
if (!applyQuantoAdjustment_) {
493492
auto res = ModelImpl::pay(amount, obsdate, paydate, currency);
494-
res.setTime(curves_.front()->timeFromReference(obsdate));
493+
res.setTime(timeFromReference(obsdate));
495494
return res;
496495
}
497496

@@ -502,7 +501,7 @@ RandomVariable FdBlackScholesBase::pay(const RandomVariable& amount, const Date&
502501
Date effectiveDate = std::max(obsdate, referenceDate());
503502

504503
auto res = amount * getDiscount(quantoTargetCcyIndex_, effectiveDate, paydate) / getNumeraire(effectiveDate);
505-
res.setTime(curves_.front()->timeFromReference(obsdate));
504+
res.setTime(timeFromReference(obsdate));
506505
return res;
507506
}
508507

0 commit comments

Comments
 (0)