Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
blackscholescg.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2021 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
23
26
27#include <ql/math/comparison.hpp>
28#include <ql/math/matrixutilities/choleskydecomposition.hpp>
29#include <ql/math/matrixutilities/symmetricschurdecomposition.hpp>
30
31namespace ore {
32namespace data {
33
34using namespace QuantLib;
35using namespace QuantExt;
36
37BlackScholesCG::BlackScholesCG(const Size paths, const std::string& currency, const Handle<YieldTermStructure>& curve,
38 const std::string& index, const std::string& indexCurrency,
39 const Handle<BlackScholesModelWrapper>& model, const std::set<Date>& simulationDates,
40 const IborFallbackConfig& iborFallbackConfig, const std::string& calibration,
41 const std::vector<Real>& calibrationStrikes)
42 : BlackScholesCG(paths, {currency}, {curve}, {}, {}, {}, {index}, {indexCurrency}, model, {}, simulationDates,
43 iborFallbackConfig, calibration, {{index, calibrationStrikes}}) {}
44
46 const Size paths, const std::vector<std::string>& currencies, const std::vector<Handle<YieldTermStructure>>& curves,
47 const std::vector<Handle<Quote>>& fxSpots,
48 const std::vector<std::pair<std::string, QuantLib::ext::shared_ptr<InterestRateIndex>>>& irIndices,
49 const std::vector<std::pair<std::string, QuantLib::ext::shared_ptr<ZeroInflationIndex>>>& infIndices,
50 const std::vector<std::string>& indices, const std::vector<std::string>& indexCurrencies,
51 const Handle<BlackScholesModelWrapper>& model,
52 const std::map<std::pair<std::string, std::string>, Handle<QuantExt::CorrelationTermStructure>>& correlations,
53 const std::set<Date>& simulationDates, const IborFallbackConfig& iborFallbackConfig, const std::string& calibration,
54 const std::map<std::string, std::vector<Real>>& calibrationStrikes)
55 : BlackScholesCGBase(paths, currencies, curves, fxSpots, irIndices, infIndices, indices, indexCurrencies, model,
56 correlations, simulationDates, iborFallbackConfig),
57 calibration_(calibration), calibrationStrikes_(calibrationStrikes) {}
58
59namespace {
60
61struct SqrtCovCalculator : public QuantLib::LazyObject {
62
63 SqrtCovCalculator(
64 const std::vector<IndexInfo>& indices, const std::vector<std::string>& indexCurrencies,
65 const std::map<std::pair<std::string, std::string>, Handle<QuantExt::CorrelationTermStructure>>& correlations,
66 const std::set<Date>& effectiveSimulationDates, const TimeGrid& timeGrid,
67 const std::vector<Size>& positionInTimeGrid, const Handle<BlackScholesModelWrapper>& model,
68 const std::vector<Real>& calibrationStrikes)
69 : indices_(indices), indexCurrencies_(indexCurrencies), correlations_(correlations),
70 effectiveSimulationDates_(effectiveSimulationDates), timeGrid_(timeGrid),
71 positionInTimeGrid_(positionInTimeGrid), model_(model), calibrationStrikes_(calibrationStrikes) {
72
73 // register with observables
74
75 for (auto const& c : correlations)
76 registerWith(c.second);
77
78 registerWith(model_);
79
80 // setup members
81
82 sqrtCov_ = std::vector<Matrix>(effectiveSimulationDates_.size() - 1);
84 std::vector<Matrix>(effectiveSimulationDates_.size() - 1, Matrix(indices_.size(), indices_.size()));
85 lastCovariance_ = std::vector<Matrix>(effectiveSimulationDates_.size() - 1);
86 }
87
88 // helper function to build correlation matrix
89
90 Matrix getCorrelation() const {
91 Matrix correlation(indices_.size(), indices_.size(), 0.0);
92 for (Size i = 0; i < indices_.size(); ++i)
93 correlation[i][i] = 1.0;
94 for (auto const& c : correlations_) {
95 IndexInfo inf1(c.first.first), inf2(c.first.second);
96 auto ind1 = std::find(indices_.begin(), indices_.end(), inf1);
97 auto ind2 = std::find(indices_.begin(), indices_.end(), inf2);
98 if (ind1 != indices_.end() && ind2 != indices_.end()) {
99 // EQ, FX, COMM index
100 Size i1 = std::distance(indices_.begin(), ind1);
101 Size i2 = std::distance(indices_.begin(), ind2);
102 correlation[i1][i2] = correlation[i2][i1] =
103 c.second->correlation(0.0); // we assume a constant correlation!
104 }
105 }
106 TLOG("BlackScholesBase correlation matrix:");
107 TLOGGERSTREAM(correlation);
108 return correlation;
109 }
110
111 // the main calc function
112
113 void performCalculations() const override {
114
115 // if there are no future simulation dates we are done
116
117 if (effectiveSimulationDates_.size() == 1)
118 return;
119
120 // compile the correlation matrix
121
122 Matrix correlation = getCorrelation();
123
124 // compute covariances of log spots to evolve the process; the covariance computation is done
125 // on the refined grid where we assume the volatilities to be constant
126
127 // used for dirf adjustment eq / com that are not in base ccy below
128 std::vector<Size> forCcyDaIndex(indices_.size(), Null<Size>());
129 for (Size j = 0; j < indices_.size(); ++j) {
130 if (!indices_[j].isFx()) {
131 // do we have an fx index with the desired currency?
132 for (Size jj = 0; jj < indices_.size(); ++jj) {
133 if (indices_[jj].isFx()) {
134 if (indexCurrencies_[jj] == indexCurrencies_[j])
135 forCcyDaIndex[j] = jj;
136 }
137 }
138 }
139 }
140
141 Array variance(indices_.size(), 0.0), discountRatio(indices_.size(), 1.0);
142 Size tidx = 1; // index in the refined time grid
143 for (Size i = 1; i < effectiveSimulationDates_.size(); ++i) {
144 std::fill(covariance_[i - 1].begin(), covariance_[i - 1].end(), 0.0);
145 // covariance
146 while (tidx <= positionInTimeGrid_[i]) {
147 Array d_variance(indices_.size());
148 for (Size j = 0; j < indices_.size(); ++j) {
149 Real tmp = model_->processes()[j]->blackVolatility()->blackVariance(
150 timeGrid_[tidx],
151 calibrationStrikes_[j] == Null<Real>()
152 ? atmForward(model_->processes()[j]->x0(), model_->processes()[j]->riskFreeRate(),
153 model_->processes()[j]->dividendYield(), timeGrid_[tidx])
154 : calibrationStrikes_[j]);
155 d_variance[j] = std::max(tmp - variance[j], 1E-20);
156 variance[j] = tmp;
157 }
158 for (Size j = 0; j < indices_.size(); ++j) {
159 covariance_[i - 1][j][j] += d_variance[j];
160 for (Size k = 0; k < j; ++k) {
161 Real tmp = correlation[k][j] * std::sqrt(d_variance[j] * d_variance[k]);
162 covariance_[i - 1][k][j] += tmp;
163 covariance_[i - 1][j][k] += tmp;
164 }
165 }
166 ++tidx;
167 }
168 }
169
170 // update lastCovariance and sqrt if there is a change
171
172 for (Size i = 0; i < effectiveSimulationDates_.size() - 1; ++i) {
173
174 bool covarianceHasChanged = lastCovariance_[i].rows() == 0;
175 for (Size r = 0; r < lastCovariance_[i].rows() && !covarianceHasChanged; ++r) {
176 for (Size c = 0; c < lastCovariance_[i].columns(); ++c) {
177 if (!close_enough(lastCovariance_[i](r, c), covariance_[i](r, c))) {
178 covarianceHasChanged = true;
179 break;
180 }
181 }
182 }
183
184 if (covarianceHasChanged) {
186
187 // salvage matrix using spectral method if not positive semidefinite
188
189 SymmetricSchurDecomposition jd(covariance_[i]);
190
191 bool needsSalvaging = false;
192 for (Size k = 0; k < covariance_[i].rows(); ++k) {
193 if (jd.eigenvalues()[k] < -1E-16)
194 needsSalvaging = true;
195 }
196
197 if (needsSalvaging) {
198 Matrix diagonal(covariance_[i].rows(), covariance_[i].rows(), 0.0);
199 for (Size k = 0; k < jd.eigenvalues().size(); ++k) {
200 diagonal[k][k] = std::sqrt(std::max<Real>(jd.eigenvalues()[k], 0.0));
201 }
202 covariance_[i] = jd.eigenvectors() * diagonal * diagonal * transpose(jd.eigenvectors());
203 }
204
205 // compute the _unique_ pos semidefinite square root
206
207 sqrtCov_[i] = CholeskyDecomposition(covariance_[i], true);
208 }
209 }
210 }
211
212 Real sqrtCov(Size i, Size j, Size k) {
213 calculate();
214 return sqrtCov_[i][j][k];
215 }
216
217 Real cov(Size i, Size j, Size k) {
218 calculate();
219 return covariance_[i][j][k];
220 }
221
222 mutable std::vector<Matrix> sqrtCov_;
223 mutable std::vector<Matrix> covariance_;
224 mutable std::vector<Matrix> lastCovariance_;
225
226 // refs to members of BlackScholesCG and its base classes that are needed in the calculator
227 const std::vector<IndexInfo>& indices_;
228 const std::vector<std::string>& indexCurrencies_;
229 const std::map<std::pair<std::string, std::string>, Handle<QuantExt::CorrelationTermStructure>>& correlations_;
230 const std::set<Date>& effectiveSimulationDates_;
231 const TimeGrid& timeGrid_;
232 const std::vector<Size>& positionInTimeGrid_;
233 const Handle<BlackScholesModelWrapper>& model_;
234
235 // inputs that we need to copy
236 const std::vector<Real> calibrationStrikes_;
237
238}; // SqrtCovCalculator
239
240} // namespace
241
243
245
246 // nothing to do if we do not have any indices or if underlying paths are populated already
247
248 if (indices_.empty() || !underlyingPaths_.empty())
249 return;
250
251 // exit if there are no future simulation dates (i.e. only the reference date)
252
253 if (effectiveSimulationDates_.size() == 1)
254 return;
255
256 // init underlying path where we map a date to a randomvariable representing the path values
257
258 for (auto const& d : effectiveSimulationDates_) {
259 underlyingPaths_[d] = std::vector<std::size_t>(model_->processes().size(), ComputationGraph::nan);
260 }
261
262 // determine calibration strikes
263
264 std::vector<Real> calibrationStrikes;
265 if (calibration_ == "ATM") {
266 calibrationStrikes.resize(indices_.size(), Null<Real>());
267 } else if (calibration_ == "Deal") {
268 for (Size i = 0; i < indices_.size(); ++i) {
269 auto f = calibrationStrikes_.find(indices_[i].name());
270 if (f != calibrationStrikes_.end() && !f->second.empty()) {
271 calibrationStrikes.push_back(f->second[0]);
272 TLOG("calibration strike for index '" << indices_[i] << "' is " << f->second[0]);
273 } else {
274 calibrationStrikes.push_back(Null<Real>());
275 TLOG("calibration strike for index '" << indices_[i] << "' is ATMF");
276 }
277 }
278 } else {
279 QL_FAIL("BlackScholes: calibration '" << calibration_ << "' not supported, expected ATM, Deal");
280 }
281
282 // generate computation graph of underlying paths dependend on the drift and sqrtCov between simulation
283 // dates, which we treat as model parameters
284
285 auto sqrtCovCalc =
286 QuantLib::ext::make_shared<SqrtCovCalculator>(indices_, indexCurrencies_, correlations_, effectiveSimulationDates_,
287 timeGrid_, positionInTimeGrid_, model_, calibrationStrikes);
288
289 std::vector<std::vector<std::size_t>> drift(effectiveSimulationDates_.size() - 1,
290 std::vector<std::size_t>(indices_.size(), ComputationGraph::nan));
291 std::vector<std::vector<std::vector<std::size_t>>> sqrtCov(
292 effectiveSimulationDates_.size() - 1,
293 std::vector<std::vector<std::size_t>>(indices_.size(),
294 std::vector<std::size_t>(indices_.size(), ComputationGraph::nan)));
295 std::vector<std::vector<std::vector<std::size_t>>> cov(
296 effectiveSimulationDates_.size() - 1,
297 std::vector<std::vector<std::size_t>>(indices_.size(),
298 std::vector<std::size_t>(indices_.size(), ComputationGraph::nan)));
299
300 // precompute sqrtCov nodes and add model parameters for covariance (needed in futureBarrierProbability())
301
302 for (Size i = 0; i < effectiveSimulationDates_.size() - 1; ++i) {
303 for (Size j = 0; j < indices_.size(); ++j) {
304 for (Size k = 0; k < indices_.size(); ++k) {
305 std::string id = "__sqrtCov_" + std::to_string(j) + "_" + std::to_string(k) + "_" + std::to_string(i);
306 sqrtCov[i][j][k] =
307 addModelParameter(id, [sqrtCovCalc, i, j, k] { return sqrtCovCalc->sqrtCov(i, j, k); });
308 id = "__cov_" + std::to_string(j) + "_" + std::to_string(k) + "_" + std::to_string(i);
309 cov[i][j][k] = addModelParameter(id, [sqrtCovCalc, i, j, k] { return sqrtCovCalc->cov(i, j, k); });
310 }
311 }
312 }
313
314 // precompute drift nodes
315
316 std::vector<Size> forCcyDaIndex(indices_.size(), Null<Size>());
317 for (Size j = 0; j < indices_.size(); ++j) {
318 if (!indices_[j].isFx()) {
319 // do we have an fx index with the desired currency?
320 for (Size jj = 0; jj < indices_.size(); ++jj) {
321 if (indices_[jj].isFx()) {
323 forCcyDaIndex[j] = jj;
324 }
325 }
326 }
327 }
328
329 std::vector<std::size_t> discountRatio(indices_.size(), cg_const(*g_, 1.0));
330 auto date = effectiveSimulationDates_.begin();
331 for (Size i = 0; i < effectiveSimulationDates_.size() - 1; ++i) {
332 Date d = *(++date);
333 for (Size j = 0; j < indices_.size(); ++j) {
334 auto p = model_->processes().at(j);
335 std::string id = std::to_string(j) + "_" + ore::data::to_string(d);
336 std::size_t div= addModelParameter("__div_" + id, [p, d]() { return p->dividendYield()->discount(d); });
337 std::size_t rfr = addModelParameter("__rfr_" + id, [p, d]() { return p->riskFreeRate()->discount(d); });
338 std::size_t tmp = cg_div(*g_, rfr, div);
339 drift[i][j] = cg_subtract(*g_, cg_negative(*g_, cg_log(*g_, cg_div(*g_, tmp, discountRatio[j]))),
340 cg_mult(*g_, cg_const(*g_, 0.5), cov[i][j][j]));
341 discountRatio[j] = tmp;
342 if (forCcyDaIndex[j] != Null<Size>()) {
343 drift[i][j] = cg_subtract(*g_, drift[i][j], cov[i][forCcyDaIndex[j]][j]);
344 }
345 }
346 }
347
348 // set required random variates
349
350
351 randomVariates_ = std::vector<std::vector<std::size_t>>(
352 indices_.size(), std::vector<std::size_t>(effectiveSimulationDates_.size() - 1));
353
354 for (Size j = 0; j < indices_.size(); ++j) {
355 for (Size i = 0; i < effectiveSimulationDates_.size() - 1; ++i) {
356 randomVariates_[j][i] = cg_var(*g_, "__rv_" + std::to_string(j) + "_" + std::to_string(i),
357 ComputationGraph::VarDoesntExist::Create);
358 }
359 }
360
361 // evolve the process using correlated normal variates and set the underlying path values
362
363 std::vector<std::size_t> logState(indices_.size());
364 for (Size j = 0; j < indices_.size(); ++j) {
365 std::string id = "__x0_" + std::to_string(j);
366 auto p = model_->processes().at(j);
367 logState[j] = addModelParameter(id, [p] { return std::log(p->x0()); });
368 underlyingPaths_[*effectiveSimulationDates_.begin()][j] = cg_exp(*g_, logState[j]);
369 }
370
371 date = effectiveSimulationDates_.begin();
372 for (Size i = 0; i < effectiveSimulationDates_.size() - 1; ++i) {
373 ++date;
374 for (Size j = 0; j < indices_.size(); ++j) {
375 for (Size k = 0; k < indices_.size(); ++k) {
376 logState[j] = cg_add(*g_, logState[j], cg_mult(*g_, sqrtCov[i][j][k], randomVariates_[k][i]));
377 }
378 logState[j] = cg_add(*g_, logState[j], drift[i][j]);
379 underlyingPaths_[*date][j] = cg_exp(*g_, logState[j]);
380 }
381 }
382
383 // set additional results provided by this model
384
385 for (auto const& c : correlations_) {
386 additionalResults_["BlackScholes.Correlation_" + c.first.first + "_" + c.first.second] =
387 c.second->correlation(0.0);
388 }
389
390 for (Size i = 0; i < calibrationStrikes.size(); ++i) {
391 additionalResults_["BlackScholes.CalibrationStrike_" + indices_[i].name()] =
392 (calibrationStrikes[i] == Null<Real>() ? "ATMF" : std::to_string(calibrationStrikes[i]));
393 }
394
395 for (Size i = 0; i < indices_.size(); ++i) {
396 Size timeStep = 0;
397 for (auto const& d : effectiveSimulationDates_) {
398 Real t = timeGrid_[positionInTimeGrid_[timeStep]];
399 Real forward = atmForward(model_->processes()[i]->x0(), model_->processes()[i]->riskFreeRate(),
400 model_->processes()[i]->dividendYield(), t);
401 if (timeStep > 0) {
402 Real volatility = model_->processes()[i]->blackVolatility()->blackVol(
403 t, calibrationStrikes[i] == Null<Real>() ? forward : calibrationStrikes[i]);
404 additionalResults_["BlackScholes.Volatility_" + indices_[i].name() + "_" + ore::data::to_string(d)] =
405 volatility;
406 }
407 additionalResults_["BlackScholes.Forward_" + indices_[i].name() + "_" + ore::data::to_string(d)] = forward;
408 ++timeStep;
409 }
410 }
411} // performCalculations()
412
413namespace {
414struct comp {
415 comp(const std::string& indexInput) : indexInput_(indexInput) {}
416 template <typename T> bool operator()(const std::pair<IndexInfo, QuantLib::ext::shared_ptr<T>>& p) const {
417 return p.first.name() == indexInput_;
418 }
419 const std::string indexInput_;
420};
421} // namespace
422
423std::size_t BlackScholesCG::getFutureBarrierProb(const std::string& index, const Date& obsdate1, const Date& obsdate2,
424 const std::size_t barrier, const bool above) const {
425
426 // get the underlying values at the start and end points of the period
427
428 std::size_t v1 = eval(index, obsdate1, Null<Date>());
429 std::size_t v2 = eval(index, obsdate2, Null<Date>());
430
431 // check the barrier at the two endpoints
432
433 std::size_t barrierHit = cg_const(*g_, 0.0);
434 if (above) {
435 barrierHit = cg_min(*g_, cg_const(*g_, 1.0), cg_add(*g_, barrierHit, cg_indicatorGeq(*g_, v1, barrier)));
436 barrierHit = cg_min(*g_, cg_const(*g_, 1.0), cg_add(*g_, barrierHit, cg_indicatorGeq(*g_, v2, barrier)));
437 } else {
438 barrierHit =
439 cg_min(*g_, cg_const(*g_, 1.0),
440 cg_add(*g_, barrierHit, cg_subtract(*g_, cg_const(*g_, 1.0), cg_indicatorGt(*g_, v1, barrier))));
441 barrierHit =
442 cg_min(*g_, cg_const(*g_, 1.0),
443 cg_add(*g_, barrierHit, cg_subtract(*g_, cg_const(*g_, 1.0), cg_indicatorGt(*g_, v2, barrier))));
444 }
445
446 // for IR / INF indices we have to check each date, this is fast though, since the index values are deteministic
447 // here
448
449 auto ir = std::find_if(irIndices_.begin(), irIndices_.end(), comp(index));
450 auto inf = std::find_if(infIndices_.begin(), infIndices_.end(), comp(index));
451 if (ir != irIndices_.end() || inf != infIndices_.end()) {
452 Date d = obsdate1 + 1;
453 while (d < obsdate2) {
454 std::size_t res;
455 if (ir != irIndices_.end())
456 res = getIrIndexValue(std::distance(irIndices_.begin(), ir), d);
457 else
458 res = getInfIndexValue(std::distance(infIndices_.begin(), inf), d);
459
460 if (above) {
461 barrierHit =
462 cg_min(*g_, cg_const(*g_, 1.0), cg_add(*g_, barrierHit, cg_indicatorGeq(*g_, res, barrier)));
463 } else {
464 barrierHit = cg_min(
465 *g_, cg_const(*g_, 1.0),
466 cg_add(*g_, barrierHit, cg_subtract(*g_, cg_const(*g_, 1.0), cg_indicatorGt(*g_, res, barrier))));
467 }
468 ++d;
469 }
470 }
471
472 // return the result if we have an IR / INF index
473
474 if (ir != irIndices_.end() || inf != infIndices_.end())
475 return barrierHit;
476
477 // Now handle the dynamic indices in this model
478
479 // first make sure that v1 is not a historical fixing to avoid differences to a daily MC simulation where
480 // the process starts to evolve at the market data spot (only matters if the two values differ, which they
481 // should not too much anyway)
482
483 if (obsdate1 == referenceDate()) {
484 v1 = eval(index, obsdate1, Null<Date>(), false, true);
485 }
486
487 IndexInfo indexInfo(index);
488
489 // if we have an FX index, we "normalise" the tag by GENERIC (since it does not matter for projections), this
490 // is the same as in ModelImpl::eval()
491
492 if (indexInfo.isFx())
493 indexInfo = IndexInfo("FX-GENERIC-" + indexInfo.fx()->sourceCurrency().code() + "-" +
494 indexInfo.fx()->targetCurrency().code());
495
496 // get the average volatility over the period for the underlying, this is an approximation that could be refined
497 // by sampling more points in the interval and doing the below calculation for each subinterval - this would
498 // be slower though, so probably in the end we want this to be configurable in the model settings
499
500 // We might have one or two indices contributing to the desired volatility, since FX indices might require a
501 // triangulation. We look for the indices ind1 and ind2 so that the index is the quotient of the two.
502
503 Size ind1 = Null<Size>(), ind2 = Null<Size>();
504
505 auto i = std::find(indices_.begin(), indices_.end(), indexInfo);
506 if (i != indices_.end()) {
507 // we find the index directly in the index list
508 ind1 = std::distance(indices_.begin(), i);
509 } else {
510 // we can maybe triangulate an FX index (again, this is similar to ModelImpl::eval())
511 // if not, we can only try something else for FX indices
512 QL_REQUIRE(indexInfo.isFx(), "BlackScholes::getFutureBarrierProb(): index " << index << " not handled");
513 // is it a trivial fx index (CCY-CCY) or can we triangulate it?
514 if (indexInfo.fx()->sourceCurrency() == indexInfo.fx()->targetCurrency()) {
515 // pseudo FX index FX-GENERIC-CCY-CCY, leave ind1 and ind2 both set to zero
516 } else {
517 // if not, we can only try something else for FX indices
518 QL_REQUIRE(indexInfo.isFx(), "BlackScholes::getFutureBarrierProb(): index " << index << " not handled");
519 if (indexInfo.fx()->sourceCurrency() == indexInfo.fx()->targetCurrency()) {
520 // nothing to do, leave ind1 and ind2 = null
521 } else {
522 for (Size i = 0; i < indexCurrencies_.size(); ++i) {
523 if (indices_[i].isFx()) {
524 if (indexInfo.fx()->sourceCurrency().code() == indexCurrencies_[i])
525 ind1 = i;
526 if (indexInfo.fx()->targetCurrency().code() == indexCurrencies_[i])
527 ind2 = i;
528 }
529 }
530 }
531 }
532 }
533
534 // accumulate the variance contributions over [obsdate1, obsdate2]
535
536 std::size_t variance = cg_const(*g_, 0.0);
537
538 for (Size i = 1; i < effectiveSimulationDates_.size(); ++i) {
539
540 Date d1 = *std::next(effectiveSimulationDates_.begin(), i - 1);
541 Date d2 = *std::next(effectiveSimulationDates_.begin(), i);
542
543 if (obsdate1 <= d1 && d2 <= obsdate2) {
544 if (ind1 != Null<Size>()) {
545 std::string id =
546 "__cov_" + std::to_string(ind1) + "_" + std::to_string(ind1) + "_" + std::to_string(i - 1);
547 variance = cg_add(*g_, variance, cg_var(*g_, id));
548 }
549 if (ind2 != Null<Size>()) {
550 std::string id =
551 "__cov_" + std::to_string(ind2) + "_" + std::to_string(ind2) + "_" + std::to_string(i - 1);
552 variance = cg_add(*g_, variance, cg_var(*g_, id));
553 }
554 if (ind1 != Null<Size>() && ind2 != Null<Size>()) {
555 std::string id =
556 "__cov_" + std::to_string(ind1) + "_" + std::to_string(ind2) + "_" + std::to_string(i - 1);
557 variance = cg_subtract(*g_, variance, cg_mult(*g_, cg_const(*g_, 2.0), cg_var(*g_, id)));
558 }
559 }
560 }
561
562 // compute the hit probability
563 // see e.g. formulas 2, 4 in Emmanuel Gobet, Advanced Monte Carlo methods for barrier and related exotic options
564
565 std::size_t eps = cg_const(*g_, 1E-14);
566
567 variance = cg_max(*g_, variance, eps);
568 std::size_t adjBarrier = cg_max(*g_, barrier, eps);
569
570 std::size_t hitProb = cg_min(*g_, cg_const(*g_, 1.0),
571 cg_exp(*g_, cg_mult(*g_,
572 cg_mult(*g_, cg_div(*g_, cg_const(*g_, -2.0), variance),
573 cg_log(*g_, cg_div(*g_, v1, adjBarrier))),
574 cg_log(*g_, cg_div(*g_, v2, adjBarrier)))));
575
576 barrierHit = cg_add(*g_, barrierHit, cg_mult(*g_, cg_subtract(*g_, cg_const(*g_, 1.0), barrierHit), hitProb));
577
578 return barrierHit;
579} // getFutureBarrierProb()
580
581} // namespace data
582} // namespace ore
const std::string indexInput_
const std::vector< IndexInfo > & indices_
const std::set< Date > & effectiveSimulationDates_
const std::vector< Real > calibrationStrikes_
std::vector< Matrix > lastCovariance_
std::vector< Matrix > covariance_
const std::vector< std::string > & indexCurrencies_
const std::map< std::pair< std::string, std::string >, Handle< QuantExt::CorrelationTermStructure > > & correlations_
const std::vector< Size > & positionInTimeGrid_
std::vector< Matrix > sqrtCov_
const TimeGrid & timeGrid_
black scholes model for n underlyings (fx, equity or commodity)
static std::size_t nan
void performCalculations() const override
std::map< Date, std::vector< std::size_t > > underlyingPaths_
std::size_t getInfIndexValue(const Size indexNo, const Date &d, const Date &fwd=Null< Date >()) const override
const Date & referenceDate() const override
std::size_t getIrIndexValue(const Size indexNo, const Date &d, const Date &fwd=Null< Date >()) const override
const Handle< BlackScholesModelWrapper > model_
const std::map< std::pair< std::string, std::string >, Handle< QuantExt::CorrelationTermStructure > > correlations_
std::vector< Size > positionInTimeGrid_
void performCalculations() const override
std::size_t getFutureBarrierProb(const std::string &index, const Date &obsdate1, const Date &obsdate2, const std::size_t barrier, const bool above) const override
BlackScholesCG(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 std::set< Date > &simulationDates, const IborFallbackConfig &iborFallbackConfig=IborFallbackConfig::defaultConfig(), const std::string &calibration="ATM", const std::map< std::string, std::vector< Real > > &calibrationStrikes={})
const std::map< std::string, std::vector< Real > > calibrationStrikes_
const std::string calibration_
bool isFx() const
Definition: utilities.hpp:100
QuantLib::ext::shared_ptr< FxIndex > fx() const
Definition: utilities.hpp:109
std::map< std::string, boost::any > additionalResults_
Definition: modelcg.hpp:146
QuantLib::ext::shared_ptr< QuantExt::ComputationGraph > g_
Definition: modelcg.hpp:149
std::vector< std::vector< size_t > > randomVariates_
std::size_t discount(const Date &obsdate, const Date &paydate, const std::string &currency) const override
std::vector< std::pair< IndexInfo, QuantLib::ext::shared_ptr< ZeroInflationIndex > > > infIndices_
std::vector< IndexInfo > indices_
std::vector< std::pair< IndexInfo, QuantLib::ext::shared_ptr< InterestRateIndex > > > irIndices_
const std::vector< std::string > indexCurrencies_
std::size_t eval(const std::string &index, const Date &obsdate, const Date &fwddate, const bool returnMissingMissingAsNull=false, const bool ignoreTodaysFixing=false) const override
const QuantLib::ext::shared_ptr< ModelCG > model_
@ data
Definition: log.hpp:77
#define TLOGGERSTREAM(text)
Definition: log.hpp:633
#define TLOG(text)
Logging Macro (Level = Data)
Definition: log.hpp:556
Shared utilities for model building and calibration.
std::size_t cg_min(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::size_t cg_const(ComputationGraph &g, const double value)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
std::size_t cg_indicatorGeq(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::size_t cg_subtract(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
RandomVariable variance(const RandomVariable &r)
std::size_t cg_max(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::size_t cg_exp(ComputationGraph &g, const std::size_t a, const std::string &label)
std::size_t cg_negative(ComputationGraph &g, const std::size_t a, const std::string &label)
std::size_t cg_mult(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::size_t cg_add(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::size_t cg_var(ComputationGraph &g, const std::string &name, const bool createIfNotExists)
std::size_t cg_log(ComputationGraph &g, const std::size_t a, const std::string &label)
std::size_t cg_div(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
std::size_t cg_indicatorGt(ComputationGraph &g, const std::size_t a, const std::size_t b, const std::string &label)
Size size(const ValueType &v)
Definition: value.cpp:145
Real atmForward(const Real s0, const Handle< YieldTermStructure > &r, const Handle< YieldTermStructure > &q, const Real t)
helper function that computes the atm forward
Definition: utilities.cpp:483
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
std::size_t addModelParameter(ComputationGraph &g, std::vector< std::pair< std::size_t, std::function< double(void)> > > &m, const std::string &id, std::function< double(void)> f)
Serializable Credit Default Swap.
Definition: namespaces.docs:23
string conversion utilities
string name