Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
fxbsbuilder.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/experimental/fx/blackdeltacalculator.hpp>
20#include <ql/math/optimization/levenbergmarquardt.hpp>
21#include <ql/quotes/simplequote.hpp>
22
27
33
34using namespace QuantLib;
35using namespace QuantExt;
36using namespace std;
37
38namespace ore {
39namespace data {
40
41FxBsBuilder::FxBsBuilder(const QuantLib::ext::shared_ptr<ore::data::Market>& market, const QuantLib::ext::shared_ptr<FxBsData>& data,
42 const std::string& configuration, const std::string& referenceCalibrationGrid)
43 : market_(market), configuration_(configuration), data_(data), referenceCalibrationGrid_(referenceCalibrationGrid) {
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->foreignCcy());
48 QuantLib::Currency domesticCcy = ore::data::parseCurrency(data->domesticCcy());
49 std::string ccyPair = ccy.code() + domesticCcy.code();
50
51 LOG("Start building FxBs model for " << ccyPair);
52
53 // get market data
54 fxSpot_ = market_->fxSpot(ccyPair, configuration_);
55 ytsDom_ = market_->discountCurve(domesticCcy.code(), configuration_);
56 ytsFor_ = market_->discountCurve(ccy.code(), configuration_);
57
58 // register with market observables except vols
59 marketObserver_->addObservable(fxSpot_);
60 marketObserver_->addObservable(market_->discountCurve(domesticCcy.code()));
61 marketObserver_->addObservable(market_->discountCurve(ccy.code()));
62
63 // register the builder with the market observer
64 registerWith(marketObserver_);
65
66 // notify observers of all market data changes, not only when not calculated
67 alwaysForwardNotifications();
68
69 // build option basket and derive parametrization from it
70 if (data->calibrateSigma()) {
71 fxVol_ = market_->fxVol(ccyPair, configuration_);
72 registerWith(fxVol_);
74 }
75
76 Array sigmaTimes, sigma;
77 if (data->sigmaParamType() == ParamType::Constant) {
78 QL_REQUIRE(data->sigmaTimes().size() == 0, "empty sigma tme grid expected");
79 QL_REQUIRE(data->sigmaValues().size() == 1, "initial sigma grid size 1 expected");
80 sigmaTimes = Array(0);
81 sigma = Array(data_->sigmaValues().begin(), data_->sigmaValues().end());
82 } else {
83 if (data->calibrateSigma() && data->calibrationType() == CalibrationType::Bootstrap) { // override
84 QL_REQUIRE(optionExpiries_.size() > 0, "optionExpiries is empty");
85 sigmaTimes = Array(optionExpiries_.begin(), optionExpiries_.end() - 1);
86 sigma = Array(sigmaTimes.size() + 1, data->sigmaValues()[0]);
87 } else {
88 // use input time grid and input alpha array otherwise
89 sigmaTimes = Array(data_->sigmaTimes().begin(), data_->sigmaTimes().end());
90 sigma = Array(data_->sigmaValues().begin(), data_->sigmaValues().end());
91 QL_REQUIRE(sigma.size() == sigmaTimes.size() + 1, "sigma grids do not match");
92 }
93 }
94
95 DLOG("sigmaTimes before calibration: " << sigmaTimes);
96 DLOG("sigma before calibration: " << sigma);
97
98 if (data->sigmaParamType() == ParamType::Piecewise)
100 QuantLib::ext::make_shared<QuantExt::FxBsPiecewiseConstantParametrization>(ccy, fxSpot_, sigmaTimes, sigma);
101 else if (data->sigmaParamType() == ParamType::Constant)
102 parametrization_ = QuantLib::ext::make_shared<QuantExt::FxBsConstantParametrization>(ccy, fxSpot_, sigma[0]);
103 else
104 QL_FAIL("interpolation type not supported for FX");
105}
106
107Real FxBsBuilder::error() const {
108 calculate();
109 return error_;
110}
111
112QuantLib::ext::shared_ptr<QuantExt::FxBsParametrization> FxBsBuilder::parametrization() const {
113 calculate();
114 return parametrization_;
115}
116std::vector<QuantLib::ext::shared_ptr<BlackCalibrationHelper>> FxBsBuilder::optionBasket() const {
117 calculate();
118 return optionBasket_;
119}
120
122 return data_->calibrateSigma() &&
123 (volSurfaceChanged(false) || marketObserver_->hasUpdated(false) || forceCalibration_);
124}
125
127 if (requiresRecalibration()) {
128 // build option basket
130 }
131}
132
134 // reset market observer updated flag
135 marketObserver_->hasUpdated(true);
136 // update vol cache
137 volSurfaceChanged(true);
138}
139
140Real FxBsBuilder::optionStrike(const Size j) const {
141 Date expiryDate = optionExpiry(j);
142 ore::data::Strike strike = ore::data::parseStrike(data_->optionStrikes()[j]);
143 Real strikeValue;
144 BlackDeltaCalculator bdc(Option::Type::Call, DeltaVolQuote::DeltaType::Spot, fxSpot_->value(),
145 ytsDom_->discount(expiryDate), ytsFor_->discount(expiryDate),
146 fxVol_->blackVol(expiryDate, Null<Real>()) * sqrt(fxVol_->timeFromReference(expiryDate)));
147
148 // TODO: Extend strike type coverage
150 strikeValue = bdc.atmStrike(DeltaVolQuote::AtmFwd);
151 else if (strike.type == ore::data::Strike::Type::Absolute)
152 strikeValue = strike.value;
153 else
154 QL_FAIL("strike type ATMF or Absolute expected");
155 Handle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(fxVol_->blackVol(expiryDate, strikeValue)));
156 return strikeValue;
157}
158
159Date FxBsBuilder::optionExpiry(const Size j) const {
160 Date today = Settings::instance().evaluationDate();
161 std::string expiryString = data_->optionExpiries()[j];
162 bool expiryDateBased;
163 Period expiryPb;
164 Date expiryDb;
165 parseDateOrPeriod(expiryString, expiryDb, expiryPb, expiryDateBased);
166 Date expiryDate = expiryDateBased ? expiryDb : today + expiryPb;
167 return expiryDate;
168}
169
170bool FxBsBuilder::volSurfaceChanged(const bool updateCache) const {
171 bool hasUpdated = false;
172
173 // if cache doesn't exist resize vector
174 if (fxVolCache_.size() != optionBasket_.size())
175 fxVolCache_ = vector<Real>(optionBasket_.size(), Null<Real>());
176
177 Size optionCounter = 0;
178 for (Size j = 0; j < data_->optionExpiries().size(); j++) {
179 if (!optionActive_[j])
180 continue;
181 Real vol = fxVol_->blackVol(optionExpiry(j), optionStrike(j));
182 if (!close_enough(fxVolCache_[optionCounter], vol)) {
183 if (updateCache)
184 fxVolCache_[optionCounter] = vol;
185 hasUpdated = true;
186 }
187 optionCounter++;
188 }
189 return hasUpdated;
190}
191
193 QL_REQUIRE(data_->optionExpiries().size() == data_->optionStrikes().size(), "fx option vector size mismatch");
194
195 DLOG("build reference date grid '" << referenceCalibrationGrid_ << "'");
196 Date lastRefCalDate = Date::minDate();
197 std::vector<Date> referenceCalibrationDates;
198 if (!referenceCalibrationGrid_.empty())
199 referenceCalibrationDates = DateGrid(referenceCalibrationGrid_).dates();
200
201 optionBasket_.clear();
202 std::vector<Time> expiryTimes;
203 for (Size j = 0; j < data_->optionExpiries().size(); j++) {
204 Date expiryDate = optionExpiry(j);
205
206 // check if we want to keep the helper when a reference calibration grid is given
207 auto refCalDate =
208 std::lower_bound(referenceCalibrationDates.begin(), referenceCalibrationDates.end(), expiryDate);
209 if (refCalDate == referenceCalibrationDates.end() || *refCalDate > lastRefCalDate) {
210 optionActive_[j] = true;
211 Real strikeValue = optionStrike(j);
212 Handle<Quote> quote(QuantLib::ext::make_shared<SimpleQuote>(fxVol_->blackVol(expiryDate, strikeValue)));
213 QuantLib::ext::shared_ptr<QuantExt::FxEqOptionHelper> helper = QuantLib::ext::make_shared<QuantExt::FxEqOptionHelper>(
214 expiryDate, strikeValue, fxSpot_, quote, ytsDom_, ytsFor_);
215 optionBasket_.push_back(helper);
216 helper->performCalculations();
217 expiryTimes.push_back(ytsDom_->timeFromReference(helper->option()->exercise()->date(0)));
218 DLOG("Added FxEqOptionHelper " << (data_->foreignCcy() + data_->domesticCcy()) << " "
219 << QuantLib::io::iso_date(expiryDate) << " " << helper->strike() << " "
220 << quote->value());
221 if (refCalDate != referenceCalibrationDates.end())
222 lastRefCalDate = *refCalDate;
223 } else {
224 optionActive_[j] = false;
225 }
226 }
227
228 std::sort(expiryTimes.begin(), expiryTimes.end());
229 auto itExpiryTime = unique(expiryTimes.begin(), expiryTimes.end());
230 expiryTimes.resize(distance(expiryTimes.begin(), itExpiryTime));
231
232 optionExpiries_ = Array(expiryTimes.size());
233 for (Size j = 0; j < expiryTimes.size(); j++)
234 optionExpiries_[j] = expiryTimes[j];
235}
236
238 forceCalibration_ = true;
240 forceCalibration_ = false;
241}
242} // namespace data
243} // namespace ore
virtual void forceRecalculate()
Simulation Date Grid.
Definition: dategrid.hpp:38
const std::vector< QuantLib::Date > & dates() const
Definition: dategrid.hpp:81
QuantLib::ext::shared_ptr< QuantExt::FxBsParametrization > parametrization_
Definition: fxbsbuilder.hpp:94
const std::string configuration_
Definition: fxbsbuilder.hpp:88
void forceRecalculate() override
void performCalculations() const override
bool volSurfaceChanged(const bool updateCache) const
Handle< YieldTermStructure > ytsFor_
QuantLib::ext::shared_ptr< QuantExt::MarketObserver > marketObserver_
std::vector< QuantLib::ext::shared_ptr< BlackCalibrationHelper > > optionBasket() const
Handle< BlackVolTermStructure > fxVol_
const QuantLib::ext::shared_ptr< FxBsData > data_
Definition: fxbsbuilder.hpp:89
Real optionStrike(const Size j) const
std::vector< QuantLib::ext::shared_ptr< BlackCalibrationHelper > > optionBasket_
Definition: fxbsbuilder.hpp:98
void setCalibrationDone() const
std::vector< QuantLib::Real > fxVolCache_
Handle< YieldTermStructure > ytsDom_
QuantLib::ext::shared_ptr< QuantExt::FxBsParametrization > parametrization() const
Date optionExpiry(const Size j) const
bool requiresRecalibration() const override
const QuantLib::ext::shared_ptr< ore::data::Market > market_
Definition: fxbsbuilder.hpp:87
Handle< Quote > fxSpot_
const std::string referenceCalibrationGrid_
Definition: fxbsbuilder.hpp:90
Real error() const
Return calibration error.
void buildOptionBasket() const
std::vector< bool > optionActive_
Definition: fxbsbuilder.hpp:97
FxBsBuilder(const QuantLib::ext::shared_ptr< ore::data::Market > &market, const QuantLib::ext::shared_ptr< FxBsData > &data, const std::string &configuration=Market::defaultConfiguration, const std::string &referenceCalibrationGrid="")
Constructor.
Definition: fxbsbuilder.cpp:41
The date grid class.
Builder for a Lognormal FX 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
RandomVariable sqrt(RandomVariable x)
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