Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
eqbsbuilder.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
19#include <ql/math/optimization/levenbergmarquardt.hpp>
20#include <ql/quotes/simplequote.hpp>
21
25
31
32using namespace QuantLib;
33using namespace QuantExt;
34using namespace std;
35
36namespace ore {
37namespace data {
38
39EqBsBuilder::EqBsBuilder(const QuantLib::ext::shared_ptr<ore::data::Market>& market, const QuantLib::ext::shared_ptr<EqBsData>& data,
40 const QuantLib::Currency& baseCcy, const std::string& configuration,
41 const std::string& referenceCalibrationGrid)
42 : market_(market), configuration_(configuration), data_(data), referenceCalibrationGrid_(referenceCalibrationGrid),
43 baseCcy_(baseCcy) {
44
45 optionActive_ = std::vector<bool>(data_->optionExpiries().size(), false);
46 marketObserver_ = QuantLib::ext::make_shared<MarketObserver>();
47 QuantLib::Currency ccy = ore::data::parseCurrency(data->currency());
48 string eqName = data->eqName();
49
50 LOG("Start building EqBs model for " << eqName);
51
52 // get market data
53 std::string fxCcyPair = ccy.code() + baseCcy_.code();
54 eqSpot_ = market_->equitySpot(eqName, configuration_);
55 fxSpot_ = market_->fxRate(fxCcyPair, configuration_);
56 // FIXME using the "discount curve" here instead of the equityReferenceRateCurve?
57 ytsRate_ = market_->discountCurve(ccy.code(), configuration_);
58 ytsDiv_ = market_->equityDividendCurve(eqName, configuration_);
59 eqVol_ = market_->equityVol(eqName, configuration_);
60
61 // register with market observables except vols
62 marketObserver_->registerWith(eqSpot_);
63 marketObserver_->registerWith(fxSpot_);
64 marketObserver_->registerWith(ytsRate_);
65 marketObserver_->registerWith(ytsDiv_);
66
67 // register the builder with the vol and the market observer
68 registerWith(eqVol_);
69 registerWith(marketObserver_);
70
71 // notify observers of all market data changes, not only when not calculated
72 alwaysForwardNotifications();
73
74 // build option basket and derive parametrization from it
75 if (data->calibrateSigma())
77
78 Array sigmaTimes, sigma;
79 if (data->sigmaParamType() == ParamType::Constant) {
80 QL_REQUIRE(data->sigmaTimes().size() == 0, "empty sigma time grid expected");
81 QL_REQUIRE(data->sigmaValues().size() == 1, "initial sigma grid size 1 expected");
82 sigmaTimes = Array(0);
83 sigma = Array(data_->sigmaValues().begin(), data_->sigmaValues().end());
84 } else {
85 if (data->calibrateSigma()) { // override
86 QL_REQUIRE(optionExpiries_.size() > 0, "optionExpiries is empty");
87 sigmaTimes = Array(optionExpiries_.begin(), optionExpiries_.end() - 1);
88 sigma = Array(sigmaTimes.size() + 1, data->sigmaValues()[0]);
89 } else {
90 // use input time grid and input alpha array otherwise
91 sigmaTimes = Array(data_->sigmaTimes().begin(), data_->sigmaTimes().end());
92 sigma = Array(data_->sigmaValues().begin(), data_->sigmaValues().end());
93 QL_REQUIRE(sigma.size() == sigmaTimes.size() + 1, "sigma grids do not match");
94 }
95 }
96
97 // Quotation needs to be consistent with FX spot quotation in the FX calibration basket
98 if (data->sigmaParamType() == ParamType::Piecewise)
99 parametrization_ = QuantLib::ext::make_shared<QuantExt::EqBsPiecewiseConstantParametrization>(
100 ccy, eqName, eqSpot_, fxSpot_, sigmaTimes, sigma, ytsRate_, ytsDiv_);
101 else if (data->sigmaParamType() == ParamType::Constant)
102 parametrization_ = QuantLib::ext::make_shared<QuantExt::EqBsConstantParametrization>(ccy, eqName, eqSpot_, fxSpot_,
103 sigma[0], ytsRate_, ytsDiv_);
104 else
105 QL_FAIL("interpolation type not supported for Equity");
106}
107
108Real EqBsBuilder::error() const {
109 calculate();
110 return error_;
111}
112
113QuantLib::ext::shared_ptr<QuantExt::EqBsParametrization> EqBsBuilder::parametrization() const {
114 calculate();
115 return parametrization_;
116}
117std::vector<QuantLib::ext::shared_ptr<BlackCalibrationHelper>> EqBsBuilder::optionBasket() const {
118 calculate();
119 return optionBasket_;
120}
121
123 return data_->calibrateSigma() &&
124 (volSurfaceChanged(false) || marketObserver_->hasUpdated(false) || forceCalibration_);
125}
126
128 if (requiresRecalibration()) {
129 // build option basket
131 }
132}
133
135 // reset market observer updated flag
136 marketObserver_->hasUpdated(true);
137 // update vol cache
138 volSurfaceChanged(true);
139}
140
141Real EqBsBuilder::optionStrike(const Size j) const {
142 ore::data::Strike strike = ore::data::parseStrike(data_->optionStrikes()[j]);
143 Real strikeValue;
144 // TODO: Extend strike type coverage
146 strikeValue = Null<Real>();
147 else if (strike.type == ore::data::Strike::Type::Absolute)
148 strikeValue = strike.value;
149 else
150 QL_FAIL("strike type ATMF or Absolute expected");
151 return strikeValue;
152}
153
154Date EqBsBuilder::optionExpiry(const Size j) const {
155 Date today = Settings::instance().evaluationDate();
156 std::string expiryString = data_->optionExpiries()[j];
157 bool expiryDateBased;
158 Period expiryPb;
159 Date expiryDb;
160 parseDateOrPeriod(expiryString, expiryDb, expiryPb, expiryDateBased);
161 Date expiryDate = expiryDateBased ? expiryDb : today + expiryPb;
162 return expiryDate;
163}
164
165bool EqBsBuilder::volSurfaceChanged(const bool updateCache) const {
166 bool hasUpdated = false;
167
168 // if cache doesn't exist resize vector
169 if (eqVolCache_.size() != optionBasket_.size())
170 eqVolCache_ = vector<Real>(optionBasket_.size(), Null<Real>());
171
172 Size optionCounter = 0;
173 for (Size j = 0; j < data_->optionExpiries().size(); j++) {
174 if (!optionActive_[j])
175 continue;
176 Real vol = eqVol_->blackVol(optionExpiry(j), optionStrike(j));
177 if (!close_enough(eqVolCache_[optionCounter], vol)) {
178 if (updateCache)
179 eqVolCache_[optionCounter] = vol;
180 hasUpdated = true;
181 }
182 optionCounter++;
183 }
184 return hasUpdated;
185}
186
188 QL_REQUIRE(data_->optionExpiries().size() == data_->optionStrikes().size(), "Eq option vector size mismatch");
189
190 optionActive_ = std::vector<bool>(data_->optionExpiries().size(), false);
191
192 DLOG("build reference date grid '" << referenceCalibrationGrid_ << "'");
193 Date lastRefCalDate = Date::minDate();
194 std::vector<Date> referenceCalibrationDates;
195 if (!referenceCalibrationGrid_.empty())
196 referenceCalibrationDates = DateGrid(referenceCalibrationGrid_).dates();
197
198 std::vector<Time> expiryTimes;
199 optionBasket_.clear();
200 for (Size j = 0; j < data_->optionExpiries().size(); j++) {
201 // may wish to calibrate against specific futures expiry dates...
202 Date expiryDate = optionExpiry(j);
203
204 // check if we want to keep the helper when a reference calibration grid is given
205 auto refCalDate =
206 std::lower_bound(referenceCalibrationDates.begin(), referenceCalibrationDates.end(), expiryDate);
207 if (refCalDate == referenceCalibrationDates.end() || *refCalDate > lastRefCalDate) {
208 optionActive_[j] = true;
209 Real strikeValue = optionStrike(j);
210 Handle<Quote> volQuote(QuantLib::ext::make_shared<SimpleQuote>(eqVol_->blackVol(expiryDate, strikeValue)));
211 QuantLib::ext::shared_ptr<QuantExt::FxEqOptionHelper> helper = QuantLib::ext::make_shared<QuantExt::FxEqOptionHelper>(
212 expiryDate, strikeValue, eqSpot_, volQuote, ytsRate_, ytsDiv_);
213 optionBasket_.push_back(helper);
214 helper->performCalculations();
215 expiryTimes.push_back(ytsRate_->timeFromReference(helper->option()->exercise()->date(0)));
216 DLOG("Added EquityOptionHelper " << data_->eqName() << " " << QuantLib::io::iso_date(expiryDate) << " "
217 << volQuote->value());
218 if (refCalDate != referenceCalibrationDates.end())
219 lastRefCalDate = *refCalDate;
220 } else {
221 optionActive_[j] = false;
222 }
223 }
224
225 std::sort(expiryTimes.begin(), expiryTimes.end());
226 auto itExpiryTime = unique(expiryTimes.begin(), expiryTimes.end());
227 expiryTimes.resize(distance(expiryTimes.begin(), itExpiryTime));
228
229 optionExpiries_ = Array(expiryTimes.size());
230 for (Size j = 0; j < expiryTimes.size(); j++)
231 optionExpiries_[j] = expiryTimes[j];
232}
233
235 forceCalibration_ = true;
237 forceCalibration_ = false;
238}
239
240} // namespace data
241} // namespace ore
virtual void forceRecalculate()
Simulation Date Grid.
Definition: dategrid.hpp:38
const std::vector< QuantLib::Date > & dates() const
Definition: dategrid.hpp:81
const std::string configuration_
Definition: eqbsbuilder.hpp:90
void forceRecalculate() override
void performCalculations() const override
bool volSurfaceChanged(const bool updateCache) const
QuantLib::ext::shared_ptr< QuantExt::MarketObserver > marketObserver_
Handle< BlackVolTermStructure > eqVol_
Handle< Quote > eqSpot_
const QuantLib::Currency baseCcy_
Definition: eqbsbuilder.hpp:93
std::vector< QuantLib::ext::shared_ptr< BlackCalibrationHelper > > optionBasket() const
QuantLib::ext::shared_ptr< QuantExt::EqBsParametrization > parametrization_
Definition: eqbsbuilder.hpp:97
Real optionStrike(const Size j) const
std::vector< QuantLib::ext::shared_ptr< BlackCalibrationHelper > > optionBasket_
void setCalibrationDone() const
const QuantLib::ext::shared_ptr< EqBsData > data_
Definition: eqbsbuilder.hpp:91
Handle< YieldTermStructure > ytsDiv_
std::string eqName()
Definition: eqbsbuilder.hpp:67
Date optionExpiry(const Size j) const
bool requiresRecalibration() const override
const QuantLib::ext::shared_ptr< ore::data::Market > market_
Definition: eqbsbuilder.hpp:89
EqBsBuilder(const QuantLib::ext::shared_ptr< ore::data::Market > &market, const QuantLib::ext::shared_ptr< EqBsData > &data, const QuantLib::Currency &baseCcy, const std::string &configuration=Market::defaultConfiguration, const std::string &referenceCalibrationGrid="")
Constructor.
Definition: eqbsbuilder.cpp:39
Handle< Quote > fxSpot_
const std::string referenceCalibrationGrid_
Definition: eqbsbuilder.hpp:92
Handle< YieldTermStructure > ytsRate_
QuantLib::ext::shared_ptr< QuantExt::EqBsParametrization > parametrization() const
std::vector< QuantLib::Real > eqVolCache_
Real error() const
Return calibration error.
void buildOptionBasket() const
std::vector< bool > optionActive_
The date grid class.
Builder for a Lognormal EQ model component.
Strike parseStrike(const std::string &s)
Convert text to Strike.
Definition: strike.cpp:30
boost::variant< QuantLib::Date, QuantLib::Period > parseDateOrPeriod(const string &s)
Convert text to QuantLib::Period or QuantLib::Date.
Definition: parsers.cpp:493
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
Definition: parsers.cpp:290
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
#define LOG(text)
Logging Macro (Level = Notice)
Definition: log.hpp:552
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
Size size(const ValueType &v)
Definition: value.cpp:145
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Map text representations to QuantLib/QuantExt types.
QuantLib::BootstrapHelper< QuantLib::OptionletVolatilityStructure > helper
QuantLib::Real value
Definition: strike.hpp:47
strike description