27#include <ql/math/comparison.hpp>
28#include <ql/math/matrixutilities/choleskydecomposition.hpp>
29#include <ql/math/matrixutilities/symmetricschurdecomposition.hpp>
38 const std::string& index,
const std::string& indexCurrency,
39 const Handle<BlackScholesModelWrapper>& model,
const std::set<Date>& simulationDates,
41 const std::vector<Real>& calibrationStrikes)
42 :
BlackScholesCG(paths, {currency}, {curve}, {}, {}, {}, {index}, {indexCurrency}, model, {}, simulationDates,
43 iborFallbackConfig, calibration, {{index, calibrationStrikes}}) {}
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),
61struct SqrtCovCalculator :
public QuantLib::LazyObject {
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)
75 for (
auto const& c : correlations)
76 registerWith(c.second);
90 Matrix getCorrelation()
const {
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);
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);
106 TLOG(
"BlackScholesBase correlation matrix:");
113 void performCalculations()
const override {
122 Matrix correlation = getCorrelation();
128 std::vector<Size> forCcyDaIndex(
indices_.size(), Null<Size>());
129 for (Size j = 0; j <
indices_.size(); ++j) {
130 if (!indices_[j].isFx()) {
132 for (Size jj = 0; jj <
indices_.size(); ++jj) {
133 if (indices_[jj].isFx()) {
134 if (indexCurrencies_[jj] == indexCurrencies_[j])
135 forCcyDaIndex[j] = jj;
144 std::fill(covariance_[i - 1].begin(), covariance_[i - 1].end(), 0.0);
146 while (tidx <= positionInTimeGrid_[i]) {
148 for (Size j = 0; j <
indices_.size(); ++j) {
149 Real tmp =
model_->processes()[j]->blackVolatility()->blackVariance(
151 calibrationStrikes_[j] == Null<Real>()
153 model_->processes()[j]->dividendYield(), timeGrid_[tidx])
154 : calibrationStrikes_[j]);
155 d_variance[j] = std::max(tmp -
variance[j], 1E-20);
158 for (Size j = 0; j <
indices_.size(); ++j) {
160 for (Size k = 0; k < j; ++k) {
161 Real tmp = correlation[k][j] * std::sqrt(d_variance[j] * d_variance[k]);
175 for (Size r = 0; r <
lastCovariance_[i].rows() && !covarianceHasChanged; ++r) {
177 if (!
close_enough(lastCovariance_[i](r, c), covariance_[i](r, c))) {
178 covarianceHasChanged =
true;
184 if (covarianceHasChanged) {
189 SymmetricSchurDecomposition jd(covariance_[i]);
191 bool needsSalvaging =
false;
193 if (jd.eigenvalues()[k] < -1E-16)
194 needsSalvaging =
true;
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));
202 covariance_[i] = jd.eigenvectors() * diagonal * diagonal * transpose(jd.eigenvectors());
207 sqrtCov_[i] = CholeskyDecomposition(covariance_[i],
true);
212 Real sqrtCov(Size i, Size j, Size k) {
217 Real cov(Size i, Size j, Size k) {
229 const std::map<std::pair<std::string, std::string>, Handle<QuantExt::CorrelationTermStructure>>&
correlations_;
233 const Handle<BlackScholesModelWrapper>&
model_;
264 std::vector<Real> calibrationStrikes;
266 calibrationStrikes.resize(
indices_.size(), Null<Real>());
268 for (Size i = 0; i <
indices_.size(); ++i) {
271 calibrationStrikes.push_back(f->second[0]);
272 TLOG(
"calibration strike for index '" <<
indices_[i] <<
"' is " << f->second[0]);
274 calibrationStrikes.push_back(Null<Real>());
275 TLOG(
"calibration strike for index '" <<
indices_[i] <<
"' is ATMF");
279 QL_FAIL(
"BlackScholes: calibration '" <<
calibration_ <<
"' not supported, expected ATM, Deal");
291 std::vector<std::vector<std::vector<std::size_t>>> sqrtCov(
293 std::vector<std::vector<std::size_t>>(
indices_.size(),
295 std::vector<std::vector<std::vector<std::size_t>>> cov(
297 std::vector<std::vector<std::size_t>>(
indices_.size(),
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);
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); });
316 std::vector<Size> forCcyDaIndex(
indices_.size(), Null<Size>());
317 for (Size j = 0; j <
indices_.size(); ++j) {
320 for (Size jj = 0; jj <
indices_.size(); ++jj) {
323 forCcyDaIndex[j] = jj;
333 for (Size j = 0; j <
indices_.size(); ++j) {
334 auto p =
model_->processes().at(j);
338 std::size_t tmp =
cg_div(*
g_, rfr, div);
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]);
354 for (Size j = 0; j <
indices_.size(); ++j) {
357 ComputationGraph::VarDoesntExist::Create);
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);
374 for (Size j = 0; j <
indices_.size(); ++j) {
375 for (Size k = 0; k <
indices_.size(); ++k) {
378 logState[j] =
cg_add(*
g_, logState[j], drift[i][j]);
386 additionalResults_[
"BlackScholes.Correlation_" + c.first.first +
"_" + c.first.second] =
387 c.second->correlation(0.0);
390 for (Size i = 0; i < calibrationStrikes.size(); ++i) {
392 (calibrationStrikes[i] == Null<Real>() ?
"ATMF" : std::to_string(calibrationStrikes[i]));
395 for (Size i = 0; i <
indices_.size(); ++i) {
400 model_->processes()[i]->dividendYield(), t);
402 Real volatility =
model_->processes()[i]->blackVolatility()->blackVol(
403 t, calibrationStrikes[i] == Null<Real>() ? forward : calibrationStrikes[i]);
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 {
424 const std::size_t barrier,
const bool above)
const {
428 std::size_t v1 =
eval(index, obsdate1, Null<Date>());
429 std::size_t v2 =
eval(index, obsdate2, Null<Date>());
452 Date d = obsdate1 + 1;
453 while (d < obsdate2) {
484 v1 =
eval(index, obsdate1, Null<Date>(),
false,
true);
492 if (indexInfo.
isFx())
493 indexInfo =
IndexInfo(
"FX-GENERIC-" + indexInfo.
fx()->sourceCurrency().code() +
"-" +
494 indexInfo.
fx()->targetCurrency().code());
503 Size ind1 = Null<Size>(), ind2 = Null<Size>();
508 ind1 = std::distance(
indices_.begin(), i);
512 QL_REQUIRE(indexInfo.
isFx(),
"BlackScholes::getFutureBarrierProb(): index " << index <<
" not handled");
514 if (indexInfo.
fx()->sourceCurrency() == indexInfo.
fx()->targetCurrency()) {
518 QL_REQUIRE(indexInfo.
isFx(),
"BlackScholes::getFutureBarrierProb(): index " << index <<
" not handled");
519 if (indexInfo.
fx()->sourceCurrency() == indexInfo.
fx()->targetCurrency()) {
543 if (obsdate1 <= d1 && d2 <= obsdate2) {
544 if (ind1 != Null<Size>()) {
546 "__cov_" + std::to_string(ind1) +
"_" + std::to_string(ind1) +
"_" + std::to_string(i - 1);
549 if (ind2 != Null<Size>()) {
551 "__cov_" + std::to_string(ind2) +
"_" + std::to_string(ind2) +
"_" + std::to_string(i - 1);
554 if (ind1 != Null<Size>() && ind2 != Null<Size>()) {
556 "__cov_" + std::to_string(ind1) +
"_" + std::to_string(ind2) +
"_" + std::to_string(i - 1);
568 std::size_t adjBarrier =
cg_max(*
g_, barrier, eps);
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)
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::set< Date > effectiveSimulationDates_
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 > ¤cies, 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_
QuantLib::ext::shared_ptr< FxIndex > fx() const
std::map< std::string, boost::any > additionalResults_
QuantLib::ext::shared_ptr< QuantExt::ComputationGraph > g_
std::vector< std::vector< size_t > > randomVariates_
std::size_t discount(const Date &obsdate, const Date &paydate, const std::string ¤cy) 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_
#define TLOGGERSTREAM(text)
#define TLOG(text)
Logging Macro (Level = Data)
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)
Real atmForward(const Real s0, const Handle< YieldTermStructure > &r, const Handle< YieldTermStructure > &q, const Real t)
helper function that computes the atm forward
std::string to_string(const LocationInfo &l)
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.
string conversion utilities