Skip to content

Commit c978481

Browse files
rolandlichtersjenkins
authored andcommitted
QPR-8562 add commodity model calibration
1 parent 52bd417 commit c978481

12 files changed

Lines changed: 143 additions & 19 deletions

Examples/Example_24/Input/simulation_wti.xml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,19 @@
7474
<CommodityModels>
7575
<CommoditySchwartz name="COMDTY_WTI_USD">
7676
<Currency>USD</Currency>
77-
<CalibrationType>None</CalibrationType>
77+
<CalibrationType>BestFit</CalibrationType>
7878
<Sigma>
79-
<Calibrate>false</Calibrate>
79+
<Calibrate>true</Calibrate>
8080
<InitialValue>0.1</InitialValue>
8181
</Sigma>
8282
<Kappa>
8383
<Calibrate>false</Calibrate>
8484
<InitialValue>0.0</InitialValue>
8585
</Kappa>
86+
<CalibrationOptions>
87+
<Expiries>1Y, 2Y, 3Y, 4Y, 5Y, 10Y</Expiries>
88+
<Strikes/>
89+
</CalibrationOptions>
8690
<DriftFreeState>true</DriftFreeState>
8791
</CommoditySchwartz>
8892
</CommodityModels>

Examples/Example_24/run.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,15 @@
1414
oreex.run("Input/ore_wti.xml")
1515
oreex.get_times("Output/log.txt")
1616

17-
oreex.print_headline("Plot exposure graph")
17+
oreex.print_headline("Plot Forward exposure graph")
1818
oreex.setup_plot("commodity_forward")
1919
oreex.plot("exposure_trade_CommodityForward.csv", 2, 3, 'b', "EPE")
2020
oreex.plot("exposure_trade_CommodityForward.csv", 2, 4, 'r', "ENE")
2121
oreex.decorate_plot(title="Example 24 - Simulated Commodity Forward Exposure")
2222
oreex.save_plot_to_file()
23+
24+
oreex.print_headline("Plot Option exposure graph")
25+
oreex.setup_plot("commodity_option")
26+
oreex.plot("exposure_trade_CommodityOption.csv", 2, 3, 'b', "EPE")
27+
oreex.decorate_plot(title="Example 24 - Simulated Commodity Option Exposure")
28+
oreex.save_plot_to_file()

OREData/ored/model/commodityschwartzmodelbuilder.cpp

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121

2222
#include <qle/models/commodityschwartzparametrization.hpp>
2323
#include <qle/models/futureoptionhelper.hpp>
24+
#include <qle/pricingengines/commodityschwartzfutureoptionengine.hpp>
2425

2526
#include <ored/model/commodityschwartzmodelbuilder.hpp>
27+
#include <ored/model/utilities.hpp>
2628
#include <ored/utilities/dategrid.hpp>
2729
#include <ored/utilities/log.hpp>
2830
#include <ored/utilities/parsers.hpp>
@@ -40,7 +42,10 @@ CommoditySchwartzModelBuilder::CommoditySchwartzModelBuilder(
4042
const QuantLib::Currency& baseCcy, const std::string& configuration,
4143
const std::string& referenceCalibrationGrid)
4244
: market_(market), configuration_(configuration), data_(data), referenceCalibrationGrid_(referenceCalibrationGrid),
43-
baseCcy_(baseCcy) {
45+
baseCcy_(baseCcy),
46+
optimizationMethod_(boost::shared_ptr<OptimizationMethod>(new LevenbergMarquardt(1E-8, 1E-8, 1E-8))),
47+
endCriteria_(EndCriteria(1000, 500, 1E-8, 1E-8, 1E-8)),
48+
calibrationErrorType_(BlackCalibrationHelper::RelativePriceError) {
4449

4550
optionActive_ = std::vector<bool>(data_->optionExpiries().size(), false);
4651
marketObserver_ = boost::make_shared<MarketObserver>();
@@ -73,6 +78,12 @@ CommoditySchwartzModelBuilder::CommoditySchwartzModelBuilder(
7378
parametrization_ = boost::make_shared<QuantExt::CommoditySchwartzParametrization>(ccy, name, curve_, fxSpot_,
7479
data->sigmaValue(), data->kappaValue(),
7580
data->driftFreeState());
81+
model_ = boost::make_shared<QuantExt::CommoditySchwartzModel>(parametrization_);
82+
}
83+
84+
boost::shared_ptr<QuantExt::CommoditySchwartzModel> CommoditySchwartzModelBuilder::model() const {
85+
calculate();
86+
return model_;
7687
}
7788

7889
Real CommoditySchwartzModelBuilder::error() const {
@@ -96,12 +107,68 @@ bool CommoditySchwartzModelBuilder::requiresRecalibration() const {
96107

97108
void CommoditySchwartzModelBuilder::performCalculations() const {
98109
if (requiresRecalibration()) {
110+
DLOG("COM model requires recalibration");
111+
99112
// reset market observer updated flag
100113
marketObserver_->hasUpdated(true);
101114
// build option basket
102115
buildOptionBasket();
103116
// update vol cache
104117
volSurfaceChanged(true);
118+
119+
// attach pricing engine to helpers
120+
boost::shared_ptr<QuantExt::CommoditySchwartzFutureOptionEngine> engine =
121+
boost::make_shared<QuantExt::CommoditySchwartzFutureOptionEngine>(model_);
122+
for (Size j = 0; j < optionBasket_.size(); j++)
123+
optionBasket_[j]->setPricingEngine(engine);
124+
125+
QL_REQUIRE(data_->calibrationType() != CalibrationType::Bootstrap, "Bootstrap COM calibration not supported yet");
126+
127+
if (data_->calibrationType() == CalibrationType::None ||
128+
(data_->calibrateSigma() == false && data_->calibrateKappa() == false)) {
129+
LOG("COM calibration is deactivated in the CommoditySchwartzModelData for name " << data_->name());
130+
return;
131+
}
132+
133+
// check which parameters are kept fixed
134+
std::vector<bool> fix(model_->parametrization()->numberOfParameters(), true);
135+
Constraint constraint;
136+
std::vector<Real> weights;
137+
Size freeParams = 0;
138+
if (data_->calibrateSigma()) {
139+
fix[0] = false;
140+
freeParams++;
141+
LOG("CommoditySchwartzModel: calibrate sigma for name " << data_->name());
142+
}
143+
if (data_->calibrateKappa()) {
144+
fix[1] = false;
145+
freeParams++;
146+
LOG("CommoditySchwartzModel: calibrate kappa for name " << data_->name());
147+
}
148+
if (freeParams == 0) {
149+
WLOG("CommoditySchwartzModel: skip calibration for name " << data_->name() << ", no free parameters");
150+
error_ = 0.0;
151+
return;
152+
}
153+
154+
LOG("CommoditySchwartzModel for name " << data_->name() << " before calibration:"
155+
<< " sigma=" << parametrization_->sigmaParameter()
156+
<< " kappa=" << parametrization_->kappaParameter());
157+
158+
model_->calibrate(optionBasket_, *optimizationMethod_, endCriteria_, constraint, weights, fix);
159+
160+
LOG("CommoditySchwartzModel for name " << data_->name() << " after calibration:"
161+
<< " sigma=" << parametrization_->sigmaParameter()
162+
<< " kappa=" << parametrization_->kappaParameter());
163+
164+
error_ = getCalibrationError(optionBasket_);
165+
LOG("CommoditySchwartzModel calibration rmse error " << error_ << " for name " << data_->name());
166+
try {
167+
auto d = getCalibrationDetails(optionBasket_, parametrization_);
168+
LOG(d);
169+
} catch (const std::exception& e) {
170+
WLOG("An error occurred: " << e.what());
171+
}
105172
}
106173
}
107174

@@ -152,7 +219,6 @@ bool CommoditySchwartzModelBuilder::volSurfaceChanged(const bool updateCache) co
152219
}
153220

154221
void CommoditySchwartzModelBuilder::buildOptionBasket() const {
155-
QL_FAIL("CommoditySchwartzModelBuilder::buildOptionBasket() not implemented yet");
156222

157223
QL_REQUIRE(data_->optionExpiries().size() == data_->optionStrikes().size(), "Commodity option vector size mismatch");
158224

@@ -178,7 +244,7 @@ void CommoditySchwartzModelBuilder::buildOptionBasket() const {
178244
Real strikeValue = optionStrike(j);
179245
Handle<Quote> volQuote(boost::make_shared<SimpleQuote>(vol_->blackVol(expiryDate, strikeValue)));
180246
boost::shared_ptr<QuantExt::FutureOptionHelper> helper = boost::make_shared<QuantExt::FutureOptionHelper>(
181-
expiryDate, strikeValue, curve_, volQuote);
247+
expiryDate, strikeValue, curve_, volQuote, calibrationErrorType_);
182248
optionBasket_.push_back(helper);
183249
helper->performCalculations();
184250
expiryTimes.push_back(curve_->timeFromReference(helper->option()->exercise()->date(0)));

OREData/ored/model/commodityschwartzmodelbuilder.hpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ namespace ore {
3838
namespace data {
3939
using namespace QuantLib;
4040

41-
//! Builder for a Lognormal COM model component
41+
//! Builder for a COM model component
4242
/*!
4343
This class is a utility to turn a COM model component's description
4444
into a COM model parametrization which can be used to ultimately
@@ -67,6 +67,7 @@ class CommoditySchwartzModelBuilder : public ModelBuilder {
6767
//@{
6868
std::string name() { return data_->name(); }
6969
boost::shared_ptr<QuantExt::CommoditySchwartzParametrization> parametrization() const;
70+
boost::shared_ptr<QuantExt::CommoditySchwartzModel> model() const;
7071
std::vector<boost::shared_ptr<BlackCalibrationHelper>> optionBasket() const;
7172
//@}
7273

@@ -92,8 +93,9 @@ class CommoditySchwartzModelBuilder : public ModelBuilder {
9293
const QuantLib::Currency baseCcy_;
9394

9495
// computed
95-
Real error_;
96+
mutable Real error_;
9697
mutable boost::shared_ptr<QuantExt::CommoditySchwartzParametrization> parametrization_;
98+
mutable boost::shared_ptr<QuantExt::CommoditySchwartzModel> model_;
9799

98100
// which options in data->optionExpiries() are actually in the basket?
99101
mutable std::vector<bool> optionActive_;
@@ -113,6 +115,12 @@ class CommoditySchwartzModelBuilder : public ModelBuilder {
113115

114116
// market observer
115117
boost::shared_ptr<MarketObserver> marketObserver_;
118+
119+
// TODO: move to data
120+
boost::shared_ptr<OptimizationMethod> optimizationMethod_;
121+
EndCriteria endCriteria_;
122+
BlackCalibrationHelper::CalibrationErrorType calibrationErrorType_;
123+
mutable std::vector<Real> calibrationErrors_;
116124
};
117125
} // namespace data
118126
} // namespace ore

OREData/ored/model/commodityschwartzmodeldata.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class CommoditySchwartzData {
7070
bool& calibrateSigma() { return calibrateSigma_; }
7171
ParamType& sigmaParamType() { return sigmaType_; }
7272
Real& sigmaValue() { return sigmaValue_; }
73-
bool& calibrateKappa() { return calibrateSigma_; }
73+
bool& calibrateKappa() { return calibrateKappa_; }
7474
ParamType& kappaParamType() { return sigmaType_; }
7575
Real& kappaValue() { return kappaValue_; }
7676
std::vector<std::string>& optionExpiries() { return optionExpiries_; }

OREData/ored/model/crossassetmodelbuilder.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -595,14 +595,8 @@ void CrossAssetModelBuilder::buildModel() const {
595595
for (Size i = 0; i < csBuilder.size(); i++) {
596596
DLOG("COM Calibration " << i);
597597
comOptionCalibrationErrors_[i] = csBuilder[i]->error();
598-
// boost::shared_ptr<CommoditySchwartzData> com = config_->comConfigs()[i];
599-
// if (!com->calibrateSigma() && !com->calibrateKappa()) {
600-
// DLOG("COM Calibration " << i << " skipped");
601-
// continue;
602-
// }
603598
}
604599

605-
606600
/*************************
607601
* Relink LGM discount curves to curves used for INF calibration
608602
*/

OREData/ored/model/utilities.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include <qle/models/cpicapfloorhelper.hpp>
2323
#include <qle/models/fxeqoptionhelper.hpp>
24+
#include <qle/models/futureoptionhelper.hpp>
2425
#include <qle/models/yoycapfloorhelper.hpp>
2526
#include <qle/models/yoyswaphelper.hpp>
2627

@@ -253,6 +254,39 @@ std::string getCalibrationDetails(const std::vector<boost::shared_ptr<BlackCalib
253254
return log.str();
254255
}
255256

257+
std::string getCalibrationDetails(const std::vector<boost::shared_ptr<BlackCalibrationHelper>>& basket,
258+
const boost::shared_ptr<CommoditySchwartzParametrization>& parametrization) {
259+
std::ostringstream log;
260+
log << std::right << std::setw(3) << "#" << std::setw(14) << "time" << std::setw(14) << "modelVol" << std::setw(14)
261+
<< "marketVol" << std::setw(14) << "(diff)" << std::setw(14) << "modelValue" << std::setw(14) << "marketValue"
262+
<< std::setw(14) << "(diff)" << std::setw(14) << "Sigma" << setw(14) << "Kappa\n";
263+
Real t = 0.0;
264+
Real modelSigma = parametrization->sigmaParameter();
265+
Real modelKappa = parametrization->kappaParameter();
266+
for (Size j = 0; j < basket.size(); j++) {
267+
Real modelValue = basket[j]->modelValue();
268+
Real marketValue = basket[j]->marketValue();
269+
Real valueDiff = modelValue - marketValue;
270+
Volatility modelVol = 0, marketVol = 0, volDiff = 0;
271+
boost::shared_ptr<FutureOptionHelper> option = boost::dynamic_pointer_cast<FutureOptionHelper>(basket[j]);
272+
if (option != nullptr && parametrization != nullptr) {
273+
t = option->priceCurve()->timeFromReference(option->option()->exercise()->date(0));
274+
//modelSigma = parametrization->sigma(t - 1E-4);
275+
}
276+
marketVol = basket[j]->volatility()->value();
277+
modelVol = impliedVolatility(basket[j]);
278+
volDiff = modelVol - marketVol;
279+
log << std::setw(3) << j << std::setprecision(6) << std::setw(14) << t << std::setw(14) << modelVol
280+
<< std::setw(14) << marketVol << std::setw(14) << volDiff << std::setw(14) << modelValue << std::setw(14)
281+
<< marketValue << std::setw(14) << valueDiff << std::setw(14) << modelSigma << " " << modelKappa << "\n";
282+
}
283+
if (parametrization != nullptr) {
284+
modelSigma = parametrization->sigma(t + 1E-4);
285+
}
286+
log << "t >= " << t << ": Sigma = " << modelSigma << ", Kappa = " << modelKappa << "\n";
287+
return log.str();
288+
}
289+
256290
std::string getCalibrationDetails(const std::vector<boost::shared_ptr<BlackCalibrationHelper>>& basket,
257291
const boost::shared_ptr<InfDkParametrization>& parametrization,
258292
bool indexIsInterpolated) {

OREData/ored/model/utilities.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <qle/models/infdkparametrization.hpp>
3131
#include <qle/models/infjyparameterization.hpp>
3232
#include <qle/models/irlgm1fparametrization.hpp>
33+
#include <qle/models/commodityschwartzparametrization.hpp>
3334
#include <qle/models/lgmcalibrationinfo.hpp>
3435

3536
#include <ql/models/calibrationhelper.hpp>
@@ -81,6 +82,10 @@ std::string getCalibrationDetails(
8182
const boost::shared_ptr<InfDkParametrization>& parametrization = boost::shared_ptr<InfDkParametrization>(),
8283
bool indexIsInterpolated = true);
8384

85+
std::string getCalibrationDetails(
86+
const std::vector<boost::shared_ptr<BlackCalibrationHelper>>& basket,
87+
const boost::shared_ptr<CommoditySchwartzParametrization>& parametrization = boost::shared_ptr<CommoditySchwartzParametrization>());
88+
8489
std::string getCalibrationDetails(const std::vector<boost::shared_ptr<CalibrationHelper>>& realRateBasket,
8590
const std::vector<boost::shared_ptr<CalibrationHelper>>& indexBasket,
8691
const boost::shared_ptr<InfJyParameterization>& parameterization,

QuantExt/qle/models/commodityschwartzmodel.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ CommoditySchwartzModel::CommoditySchwartzModel(const boost::shared_ptr<Commodity
2525
const Discretization discretization)
2626
: parametrization_(parametrization), discretization_(discretization) {
2727
QL_REQUIRE(parametrization_ != nullptr, "CommoditySchwartzModel: parametrization is null");
28+
arguments_.resize(2);
29+
arguments_[0] = parametrization_->parameter(0);
30+
arguments_[1] = parametrization_->parameter(1);
2831
stateProcess_ = boost::make_shared<CommoditySchwartzStateProcess>(parametrization_, discretization_);
2932
}
3033

QuantExt/qle/models/commodityschwartzmodel.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323

2424
#pragma once
2525

26+
#include <qle/models/hwparametrization.hpp>
27+
#include <qle/models/irmodel.hpp>
2628
#include <qle/models/commodityschwartzparametrization.hpp>
2729
#include <qle/models/commoditymodel.hpp>
2830

@@ -71,11 +73,10 @@ class CommoditySchwartzModel : public CommodityModel {
7173
const QuantLib::Handle<QuantExt::PriceTermStructure>& priceCurve
7274
= QuantLib::Handle<QuantExt::PriceTermStructure>()) const override;
7375

74-
// Schwartz model specific methods
75-
76+
//! Schwartz model specific methods
7677
const boost::shared_ptr<CommoditySchwartzParametrization> parametrization() const { return parametrization_; }
7778

78-
/*! observer and linked calibrated model interface */
79+
//! observer and linked calibrated model interface
7980
void update() override;
8081
void generateArguments() override;
8182

0 commit comments

Comments
 (0)