Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
blackscholesbase.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2019 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
20
22
25
29
30#include <ql/math/comparison.hpp>
31#include <ql/quotes/simplequote.hpp>
32
33namespace ore {
34namespace data {
35
36using namespace QuantLib;
37using namespace QuantExt;
38
39BlackScholesBase::BlackScholesBase(const Size paths, const std::string& currency,
40 const Handle<YieldTermStructure>& curve, const std::string& index,
41 const std::string& indexCurrency, const Handle<BlackScholesModelWrapper>& model,
42 const Model::McParams& mcParams, const std::set<Date>& simulationDates,
43 const IborFallbackConfig& iborFallbackConfig)
44 : BlackScholesBase(paths, {currency}, {curve}, {}, {}, {}, {index}, {indexCurrency}, model, {}, mcParams,
45 simulationDates, iborFallbackConfig) {}
46
48 const Size paths, const std::vector<std::string>& currencies, const std::vector<Handle<YieldTermStructure>>& curves,
49 const std::vector<Handle<Quote>>& fxSpots,
50 const std::vector<std::pair<std::string, QuantLib::ext::shared_ptr<InterestRateIndex>>>& irIndices,
51 const std::vector<std::pair<std::string, QuantLib::ext::shared_ptr<ZeroInflationIndex>>>& infIndices,
52 const std::vector<std::string>& indices, const std::vector<std::string>& indexCurrencies,
53 const Handle<BlackScholesModelWrapper>& model,
54 const std::map<std::pair<std::string, std::string>, Handle<QuantExt::CorrelationTermStructure>>& correlations,
55 const Model::McParams& mcParams, const std::set<Date>& simulationDates,
56 const IborFallbackConfig& iborFallbackConfig)
57 : ModelImpl(curves.at(0)->dayCounter(), paths, currencies, irIndices, infIndices, indices, indexCurrencies,
58 simulationDates, iborFallbackConfig),
59 curves_(curves), fxSpots_(fxSpots), model_(model), correlations_(correlations), mcParams_(mcParams) {
60
61 // check inputs
62
63 QL_REQUIRE(!model_.empty(), "model is empty");
64 QL_REQUIRE(!curves_.empty(), "no curves given");
65 QL_REQUIRE(currencies_.size() == curves_.size(), "number of currencies (" << currencies_.size()
66 << ") does not match number of curves ("
67 << curves_.size() << ")");
68 QL_REQUIRE(currencies_.size() == fxSpots_.size() + 1,
69 "number of currencies (" << currencies_.size() << ") does not match number of fx spots ("
70 << fxSpots_.size() << ") + 1");
71
72 QL_REQUIRE(indices_.size() == model_->processes().size(),
73 "mismatch of processes size (" << model_->processes().size() << ") and number of indices ("
74 << indices_.size() << ")");
75
76 // register with observables
77
78 for (auto const& o : fxSpots_)
79 registerWith(o);
80 for (auto const& o : correlations_)
81 registerWith(o.second);
82
83 registerWith(model_);
84
85} // BlackScholesBase ctor
86
88 Matrix correlation(indices_.size(), indices_.size(), 0.0);
89 for (Size i = 0; i < indices_.size(); ++i)
90 correlation[i][i] = 1.0;
91 for (auto const& c : correlations_) {
92 IndexInfo inf1(c.first.first), inf2(c.first.second);
93 auto ind1 = std::find(indices_.begin(), indices_.end(), inf1);
94 auto ind2 = std::find(indices_.begin(), indices_.end(), inf2);
95 if (ind1 != indices_.end() && ind2 != indices_.end()) {
96 // EQ, FX, COMM index
97 Size i1 = std::distance(indices_.begin(), ind1);
98 Size i2 = std::distance(indices_.begin(), ind2);
99 correlation[i1][i2] = correlation[i2][i1] = c.second->correlation(0.0); // we assume a constant correlation!
100 }
101 }
102 DLOG("BlackScholesBase correlation matrix:");
103 DLOGGERSTREAM(correlation);
104 return correlation;
105}
106
108 calculate();
109 return referenceDate_;
110}
111
113
114 QL_REQUIRE(!inTrainingPhase_,
115 "BlackScholesBase::performCalculations(): state inTrainingPhase should be false, this was "
116 "not resetted appropriately.");
117
118 referenceDate_ = curves_.front()->referenceDate();
119
120 // set up time grid
121
122 effectiveSimulationDates_ = model_->effectiveSimulationDates();
123
124 std::vector<Real> times;
125 for (auto const& d : effectiveSimulationDates_) {
126 times.push_back(timeFromReference(d));
127 }
128
129 timeGrid_ = model_->discretisationTimeGrid();
130 positionInTimeGrid_.resize(times.size());
131 for (Size i = 0; i < positionInTimeGrid_.size(); ++i)
132 positionInTimeGrid_[i] = timeGrid_.index(times[i]);
133
134 underlyingPaths_.clear();
136}
137
138RandomVariable BlackScholesBase::getIndexValue(const Size indexNo, const Date& d, const Date& fwd) const {
139 Date effFwd = fwd;
140 if (indices_[indexNo].isComm()) {
141 Date expiry = indices_[indexNo].comm(d)->expiryDate();
142 // if a future is referenced we set the forward date effectively used below to the future's expiry date
143 if (expiry != Date())
144 effFwd = expiry;
145 // if the future expiry is past the obsdate, we return the spot as of the obsdate, i.e. we
146 // freeze the future value after its expiry, but keep it available for observation
147 // TOOD should we throw an exception instead?
148 effFwd = std::max(effFwd, d);
149 }
150 QL_REQUIRE(underlyingPaths_.find(d) != underlyingPaths_.end(), "did not find path for " << d);
151 auto res = underlyingPaths_.at(d).at(indexNo);
152 // compute forwarding factor
153 if (effFwd != Null<Date>()) {
154 auto p = model_->processes().at(indexNo);
155 res *= RandomVariable(size(), p->dividendYield()->discount(effFwd) / p->dividendYield()->discount(d) /
156 (p->riskFreeRate()->discount(effFwd) / p->riskFreeRate()->discount(d)));
157 }
158 return res;
159}
160
161RandomVariable BlackScholesBase::getIrIndexValue(const Size indexNo, const Date& d, const Date& fwd) const {
162 Date effFixingDate = d;
163 if (fwd != Null<Date>())
164 effFixingDate = fwd;
165 // ensure a valid fixing date
166 effFixingDate = irIndices_.at(indexNo).second->fixingCalendar().adjust(effFixingDate);
167 return RandomVariable(size(), irIndices_.at(indexNo).second->fixing(effFixingDate));
168}
169
170RandomVariable BlackScholesBase::getInfIndexValue(const Size indexNo, const Date& d, const Date& fwd) const {
171 Date effFixingDate = d;
172 if (fwd != Null<Date>())
173 effFixingDate = fwd;
174 return RandomVariable(size(), infIndices_.at(indexNo).second->fixing(effFixingDate));
175}
176
177namespace {
178struct comp {
179 comp(const std::string& indexInput) : indexInput_(indexInput) {}
180 template <typename T> bool operator()(const std::pair<IndexInfo, QuantLib::ext::shared_ptr<T>>& p) const {
181 return p.first.name() == indexInput_;
182 }
183 const std::string indexInput_;
184};
185} // namespace
186
187RandomVariable BlackScholesBase::fwdCompAvg(const bool isAvg, const std::string& indexInput, const Date& obsdate,
188 const Date& start, const Date& end, const Real spread, const Real gearing,
189 const Integer lookback, const Natural rateCutoff, const Natural fixingDays,
190 const bool includeSpread, const Real cap, const Real floor,
191 const bool nakedOption, const bool localCapFloor) const {
192 calculate();
193 auto index = std::find_if(irIndices_.begin(), irIndices_.end(), comp(indexInput));
194 QL_REQUIRE(index != irIndices_.end(),
195 "BlackScholesBase::fwdCompAvg(): did not find ir index " << indexInput << " - this is unexpected.");
196 auto on = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(index->second);
197 QL_REQUIRE(on, "BlackScholesBase::fwdCompAvg(): expected on index for " << indexInput);
198 // if we want to support cap / floors we need the OIS CF surface
199 QL_REQUIRE(cap > 999998.0 && floor < -999998.0,
200 "BlackScholesBase:fwdCompAvg(): cap (" << cap << ") / floor (" << floor << ") not supported");
201 QuantLib::ext::shared_ptr<QuantLib::FloatingRateCoupon> coupon;
202 QuantLib::ext::shared_ptr<QuantLib::FloatingRateCouponPricer> pricer;
203 if (isAvg) {
204 coupon = QuantLib::ext::make_shared<QuantExt::AverageONIndexedCoupon>(
205 end, 1.0, start, end, on, gearing, spread, rateCutoff, on->dayCounter(), lookback * Days, fixingDays);
206 pricer = QuantLib::ext::make_shared<AverageONIndexedCouponPricer>();
207 } else {
208 coupon = QuantLib::ext::make_shared<QuantExt::OvernightIndexedCoupon>(end, 1.0, start, end, on, gearing, spread, Date(),
209 Date(), on->dayCounter(), false, includeSpread,
210 lookback * Days, rateCutoff, fixingDays);
211 pricer = QuantLib::ext::make_shared<OvernightIndexedCouponPricer>();
212 }
213 coupon->setPricer(pricer);
214 return RandomVariable(size(), coupon->rate());
215}
216
217RandomVariable BlackScholesBase::getDiscount(const Size idx, const Date& s, const Date& t) const {
218 return RandomVariable(size(), curves_.at(idx)->discount(t) / curves_.at(idx)->discount(s));
219}
220
222 return RandomVariable(size(), 1.0 / curves_.at(0)->discount(s));
223}
224
225Real BlackScholesBase::getFxSpot(const Size idx) const { return fxSpots_.at(idx)->value(); }
226
227RandomVariable BlackScholesBase::npv(const RandomVariable& amount, const Date& obsdate, const Filter& filter,
228 const boost::optional<long>& memSlot, const RandomVariable& addRegressor1,
229 const RandomVariable& addRegressor2) const {
230
231 calculate();
232
233 // short cut, if amount is deterministic and no memslot is given
234
235 if (amount.deterministic() && !memSlot)
236 return amount;
237
238 // if obsdate is today, take a plain expectation
239
240 if (obsdate == referenceDate())
241 return expectation(amount);
242
243 // build the state
244
245 std::vector<const RandomVariable*> state;
246
247 if (!underlyingPaths_.empty()) {
248 for (auto const& r : underlyingPaths_.at(obsdate))
249 state.push_back(&r);
250 }
251
252 Size nModelStates = state.size();
253
254 if (addRegressor1.initialised() && (memSlot || !addRegressor1.deterministic()))
255 state.push_back(&addRegressor1);
256 if (addRegressor2.initialised() && (memSlot || !addRegressor2.deterministic()))
257 state.push_back(&addRegressor2);
258
259 Size nAddReg = state.size() - nModelStates;
260
261 // if the state is empty, return the plain expectation (no conditioning)
262
263 if (state.empty()) {
264 return expectation(amount);
265 }
266
267 // the regression model is given by coefficients and an optional coordinate transform
268
269 Array coeff;
270 Matrix coordinateTransform;
271
272 // if a memSlot is given and coefficients / coordinate transform are stored, we use them
273
274 bool haveStoredModel = false;
275
276 if (memSlot) {
277 if (auto it = storedRegressionModel_.find(*memSlot); it != storedRegressionModel_.end()) {
278 coeff = std::get<0>(it->second);
279 coordinateTransform = std::get<2>(it->second);
280 QL_REQUIRE(std::get<1>(it->second) == state.size(),
281 "BlackScholesBase::npv(): stored regression coefficients at mem slot "
282 << *memSlot << " are for state size " << std::get<1>(it->second) << ", actual state size is "
283 << state.size() << " (before possible coordinate transform).");
284 haveStoredModel = true;
285 }
286 }
287
288 // if we do not have retrieved a model in the previous step, we create it now
289
290 std::vector<RandomVariable> transformedState;
291
292 if(!haveStoredModel) {
293
294 // factor reduction to reduce dimensionalitty and handle collinearity
295
296 if (mcParams_.regressionVarianceCutoff != Null<Real>()) {
297 coordinateTransform = pcaCoordinateTransform(state, mcParams_.regressionVarianceCutoff);
298 transformedState = applyCoordinateTransform(state, coordinateTransform);
299 state = vec2vecptr(transformedState);
300 }
301
302 // train coefficients
303
304 coeff = regressionCoefficients(amount, state,
305 multiPathBasisSystem(state.size(), mcParams_.regressionOrder,
306 mcParams_.polynomType, std::min(size(), trainingSamples())),
307 filter, RandomVariableRegressionMethod::QR);
308 DLOG("BlackScholesBase::npv(" << ore::data::to_string(obsdate) << "): regression coefficients are " << coeff
309 << " (got model state size " << nModelStates << " and " << nAddReg
310 << " additional regressors, coordinate transform "
311 << coordinateTransform.columns() << " -> " << coordinateTransform.rows() << ")");
312
313 // store model if requried
314
315 if (memSlot) {
316 storedRegressionModel_[*memSlot] = std::make_tuple(coeff, nModelStates, coordinateTransform);
317 }
318
319 } else {
320
321 // apply the stored coordinate transform to the state
322
323 if(!coordinateTransform.empty()) {
324 transformedState = applyCoordinateTransform(state, coordinateTransform);
325 state = vec2vecptr(transformedState);
326 }
327 }
328
329 // compute conditional expectation and return the result
330
331 return conditionalExpectation(state,
332 multiPathBasisSystem(state.size(), mcParams_.regressionOrder, mcParams_.polynomType,
333 std::min(size(), trainingSamples())),
334 coeff);
335}
336
338 underlyingPaths_.clear();
340}
341
343
347}
348
350
354 else
355 return Model::size();
356}
357
358} // namespace data
359} // namespace ore
const std::string indexInput_
black scholes model base class for n underlyings (fx, equity or commodity)
const std::map< std::pair< std::string, std::string >, Handle< QuantExt::CorrelationTermStructure > > & correlations_
RandomVariable getInfIndexValue(const Size indexNo, const Date &d, const Date &fwd=Null< Date >()) const override
void performCalculations() const override
Size size() const override
RandomVariable getIrIndexValue(const Size indexNo, const Date &d, const Date &fwd=Null< Date >()) const override
RandomVariable npv(const RandomVariable &amount, const Date &obsdate, const Filter &filter, const boost::optional< long > &memSlot, const RandomVariable &addRegressor1, const RandomVariable &addRegressor2) const override
Real getFxSpot(const Size idx) const override
const Date & referenceDate() const override
std::map< long, std::tuple< Array, Size, Matrix > > storedRegressionModel_
std::map< Date, std::vector< RandomVariable > > underlyingPathsTraining_
RandomVariable getNumeraire(const Date &s) const override
std::map< Date, std::vector< RandomVariable > > underlyingPaths_
std::set< Date > effectiveSimulationDates_
BlackScholesBase(const Size paths, const std::vector< std::string > &currencies, const std::vector< Handle< YieldTermStructure > > &curves, const std::vector< Handle< Quote > > &fxSpots, const std::vector< std::pair< std::string, QuantLib::ext::shared_ptr< InterestRateIndex > > > &irIndices, const std::vector< std::pair< std::string, QuantLib::ext::shared_ptr< ZeroInflationIndex > > > &infIndices, const std::vector< std::string > &indices, const std::vector< std::string > &indexCurrencies, const Handle< BlackScholesModelWrapper > &model, const std::map< std::pair< std::string, std::string >, Handle< QuantExt::CorrelationTermStructure > > &correlations, const McParams &mcParams, const std::set< Date > &simulationDates, const IborFallbackConfig &iborFallbackConfig)
const std::vector< Handle< Quote > > fxSpots_
const Handle< BlackScholesModelWrapper > model_
RandomVariable getDiscount(const Size idx, const Date &s, const Date &t) const override
RandomVariable getIndexValue(const Size indexNo, const Date &d, const Date &fwd=Null< Date >()) const override
Size trainingSamples() const override
void toggleTrainingPaths() const override
const std::map< std::pair< std::string, std::string >, Handle< QuantExt::CorrelationTermStructure > > correlations_
RandomVariable fwdCompAvg(const bool isAvg, const std::string &index, const Date &obsdate, const Date &start, const Date &end, const Real spread, const Real gearing, const Integer lookback, const Natural rateCutoff, const Natural fixingDays, const bool includeSpread, const Real cap, const Real floor, const bool nakedOption, const bool localCapFloor) const override
const std::vector< Handle< YieldTermStructure > > curves_
std::vector< Size > positionInTimeGrid_
Real timeFromReference(const Date &d) const
Definition: model.hpp:92
virtual Size size() const
Definition: model.hpp:71
const std::vector< std::string > currencies_
Definition: modelimpl.hpp:107
std::vector< std::pair< IndexInfo, QuantLib::ext::shared_ptr< ZeroInflationIndex > > > infIndices_
Definition: modelimpl.hpp:113
std::vector< IndexInfo > indices_
Definition: modelimpl.hpp:114
std::vector< std::pair< IndexInfo, QuantLib::ext::shared_ptr< InterestRateIndex > > > irIndices_
Definition: modelimpl.hpp:112
SafeStack< Filter > filter
const QuantLib::ext::shared_ptr< ModelCG > model_
Map text representations to QuantLib/QuantExt types.
@ data
Definition: log.hpp:77
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
#define DLOGGERSTREAM(text)
Definition: log.hpp:632
Array regressionCoefficients(RandomVariable r, const std::vector< const RandomVariable * > &regressor, const std::vector< std::function< RandomVariable(const std::vector< const RandomVariable * > &)> > &basisFn, const Filter &filter, const RandomVariableRegressionMethod regressionMethod, const std::string &debugLabel)
RandomVariable expectation(const RandomVariable &r)
RandomVariable conditionalExpectation(const std::vector< const RandomVariable * > &regressor, const std::vector< std::function< RandomVariable(const std::vector< const RandomVariable * > &)> > &basisFn, const Array &coefficients)
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
Serializable Credit Default Swap.
Definition: namespaces.docs:23
bool deterministic() const
QuantLib::Real regressionVarianceCutoff
Definition: model.hpp:61
QuantLib::LsmBasisSystem::PolynomialType polynomType
Definition: model.hpp:58
string conversion utilities