Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
gaussiancam.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
21
24
29
37
38#include <ql/math/comparison.hpp>
39#include <ql/quotes/simplequote.hpp>
40
41namespace ore {
42namespace data {
43
44using namespace QuantLib;
45using namespace QuantExt;
46
47GaussianCam::GaussianCam(const Handle<CrossAssetModel>& cam, const Size paths,
48 const std::vector<std::string>& currencies,
49 const std::vector<Handle<YieldTermStructure>>& curves,
50 const std::vector<Handle<Quote>>& fxSpots,
51 const std::vector<std::pair<std::string, QuantLib::ext::shared_ptr<InterestRateIndex>>>& irIndices,
52 const std::vector<std::pair<std::string, QuantLib::ext::shared_ptr<ZeroInflationIndex>>>& infIndices,
53 const std::vector<std::string>& indices, const std::vector<std::string>& indexCurrencies,
54 const std::set<Date>& simulationDates, const McParams& mcParams, const Size timeStepsPerYear,
55 const IborFallbackConfig& iborFallbackConfig,
56 const std::vector<Size>& projectedStateProcessIndices,
57 const std::vector<std::string>& conditionalExpectationModelStates)
58 : ModelImpl(curves.front()->dayCounter(), paths, currencies, irIndices, infIndices, indices, indexCurrencies,
59 simulationDates, iborFallbackConfig),
60 cam_(cam), curves_(curves), fxSpots_(fxSpots), mcParams_(mcParams), timeStepsPerYear_(timeStepsPerYear),
61 projectedStateProcessIndices_(projectedStateProcessIndices) {
62
63 // check inputs
64
65 QL_REQUIRE(!cam_.empty(), "model is empty");
66
67 // register with observables
68
69 for (auto const& o : curves_)
70 registerWith(o);
71 for (auto const& o : fxSpots_)
72 registerWith(o);
73
74 registerWith(cam_);
75
76 // set conditional expectation model states
77
79 conditionalExpectationModelStates.empty() ||
80 std::find(conditionalExpectationModelStates.begin(), conditionalExpectationModelStates.end(), "IR") !=
81 conditionalExpectationModelStates.end();
83 conditionalExpectationModelStates.empty() ||
84 std::find(conditionalExpectationModelStates.begin(), conditionalExpectationModelStates.end(), "INF") !=
85 conditionalExpectationModelStates.end();
87 conditionalExpectationModelStates.empty() ||
88 std::find(conditionalExpectationModelStates.begin(), conditionalExpectationModelStates.end(), "Asset") !=
89 conditionalExpectationModelStates.end();
90
91} // GaussianCam ctor
92
93Size GaussianCam::size() const {
94 if (injectedPathTimes_ == nullptr)
97 else
98 return Model::size();
99 else {
100 return overwriteModelSize_;
101 }
102}
103
105 underlyingPaths_.clear();
107 irStates_.clear();
108 infStates_.clear();
109 irStatesTraining_.clear();
110 infStatesTraining_.clear();
111 irIndexValueCache_.clear();
112}
113
115
116const Date& GaussianCam::referenceDate() const {
117 calculate();
118 return referenceDate_;
119}
120
122
123 QL_REQUIRE(!inTrainingPhase_, "GaussianCam::performCalculations(): state inTrainingPhase should be false, this was "
124 "not resetted appropriately.");
125
126 referenceDate_ = curves_.front()->referenceDate();
127
128 // set up time grid
129
132 for (auto const& d : simulationDates_) {
133 if (d >= referenceDate())
135 }
136
137 std::vector<Real> times;
138 for (auto const& d : effectiveSimulationDates_) {
139 times.push_back(timeFromReference(d));
140 }
141
142 Size steps = std::max(std::lround(timeStepsPerYear_ * times.back() + 0.5), 1l);
143 timeGrid_ = TimeGrid(times.begin(), times.end(), steps);
144 positionInTimeGrid_.resize(times.size());
145 for (Size i = 0; i < positionInTimeGrid_.size(); ++i)
146 positionInTimeGrid_[i] = timeGrid_.index(times[i]);
147
148 // clear underlying paths
149
150 underlyingPaths_.clear();
152 irStates_.clear();
153 infStates_.clear();
154
155 // init underlying path where we map a date to a randomvariable representing the path values
156
157 for (auto const& d : effectiveSimulationDates_) {
158 underlyingPaths_[d] = std::vector<RandomVariable>(indices_.size(), RandomVariable(size(), 0.0));
159 irStates_[d] = std::vector<RandomVariable>(currencies_.size(), RandomVariable(size(), 0.0));
160 infStates_[d] = std::vector<std::pair<RandomVariable, RandomVariable>>(
161 infIndices_.size(), std::make_pair(RandomVariable(size(), 0.0), RandomVariable(size(), 0.0)));
162
163 if(trainingSamples() != Null<Size>() && injectedPathTimes_ == nullptr) {
165 std::vector<RandomVariable>(indices_.size(), RandomVariable(trainingSamples(), 0.0));
167 std::vector<RandomVariable>(currencies_.size(), RandomVariable(trainingSamples(), 0.0));
168 infStatesTraining_[d] = std::vector<std::pair<RandomVariable, RandomVariable>>(
169 infIndices_.size(),
170 std::make_pair(RandomVariable(trainingSamples(), 0.0), RandomVariable(trainingSamples(), 0.0)));
171 }
172
173 }
174
175 // populate index mappings
176
179 for (Size i = 0; i < currencies_.size(); ++i) {
181 cam_->pIdx(CrossAssetModel::AssetType::IR, cam_->ccyIndex(parseCurrency(currencies_[i]))));
182 currencyPositionInCam_.push_back(
183 cam_->idx(CrossAssetModel::AssetType::IR, cam_->ccyIndex(parseCurrency(currencies_[i]))));
184 }
185
186 irIndexPositionInCam_.clear();
187 for (Size i = 0; i < irIndices_.size(); ++i) {
188 irIndexPositionInCam_.push_back(cam_->ccyIndex(irIndices_[i].second->currency()));
189 }
190
193 for (Size i = 0; i < infIndices_.size(); ++i) {
194 Size infIdx = cam_->infIndex(infIndices_[i].first.infName());
195 infIndexPositionInProcess_.push_back(cam_->pIdx(CrossAssetModel::AssetType::INF, infIdx));
196 infIndexPositionInCam_.push_back(infIdx);
197 }
198
200 eqIndexInCam_.resize(indices_.size());
201 comIndexInCam_.resize(indices_.size());
202 std::fill(eqIndexInCam_.begin(), eqIndexInCam_.end(), Null<Size>());
203 std::fill(comIndexInCam_.begin(), comIndexInCam_.end(), Null<Size>());
204 for (Size i = 0; i < indices_.size(); ++i) {
205 if (indices_[i].isFx()) {
206 // FX
207 Size ccyIdx = cam_->ccyIndex(parseCurrency(indexCurrencies_[i]));
208 QL_REQUIRE(ccyIdx > 0, "fx index '" << indices_[i] << "' has unexpected for ccy = base ccy");
209 indexPositionInProcess_.push_back(cam_->pIdx(CrossAssetModel::AssetType::FX, ccyIdx - 1));
210 } else if (indices_[i].isEq()) {
211 // EQ
212 Size eqIdx = cam_->eqIndex(indices_[i].eq()->name());
213 indexPositionInProcess_.push_back(cam_->pIdx(CrossAssetModel::AssetType::EQ, eqIdx));
214 eqIndexInCam_[i] = eqIdx;
215 } else if(indices_[i].isComm()) {
216 // COM
217 Size comIdx = cam_->comIndex(indices_[i].commName());
218 indexPositionInProcess_.push_back(cam_->pIdx(CrossAssetModel::AssetType::COM, comIdx));
219 comIndexInCam_[i] = comIdx;
220 } else {
221 QL_FAIL("GuassianCam::performCalculations(): index '" << indices_[i].name()
222 << "' expected to be FX, EQ, COMM");
223 }
224 }
225
226 // populate path values
227
229 if (trainingSamples() != Null<Size>() && injectedPathTimes_ == nullptr) {
231 true);
232 }
233}
234
235void GaussianCam::populatePathValues(const Size nSamples, std::map<Date, std::vector<RandomVariable>>& paths,
236 std::map<Date, std::vector<RandomVariable>>& irStates,
237 std::map<Date, std::vector<std::pair<RandomVariable, RandomVariable>>>& infStates,
238 const std::vector<Real>& times, const bool isTraining) const {
239 // get state process
240
241 auto process = cam_->stateProcess();
242
243 // set reference date values, if there are no future simulation dates, we are done
244
245 // FX, EQ, COMM indcies
246 for (Size k = 0; k < indices_.size(); ++k) {
247 paths[referenceDate_][k].setAll(process->initialValues().at(indexPositionInProcess_[k]));
248 }
249
250 // IR states per currency (they are all just 0)
251 for (Size k = 0; k < currencies_.size(); ++k) {
252 irStates[referenceDate()][k].setAll(0.0);
253 }
254
255 // INF DK or JY state, we happen to have two components (x,y) for each, so no case distinction needed
256 for (Size k = 0; k < infIndices_.size(); ++k) {
257 infStates[referenceDate()].push_back(
258 std::make_pair(RandomVariable(nSamples, process->initialValues().at(infIndexPositionInProcess_[k])),
259 RandomVariable(nSamples, process->initialValues().at(infIndexPositionInProcess_[k] + 1))));
260 }
261
262 if (effectiveSimulationDates_.size() == 1)
263 return;
264
265 // populate path values
266
267 if (process->size() == 1 && injectedPathTimes_ == nullptr) {
268
269 // we treat the case of a one dimensional process separately for optimisation reasons; we know that in
270 // this case we have a single, driftless LGM process for currency 0
271
272 auto lgmProcess = QuantLib::ext::dynamic_pointer_cast<StochasticProcess1D>(cam_->lgm(0)->stateProcess());
273
274 std::vector<Real> stdDevs(times.size() - 1);
275 for (Size i = 0; i < times.size() - 1; ++i) {
276 stdDevs[i] = lgmProcess->stdDeviation(times[i], 0.0, times[i + 1] - times[i]);
277 }
278
279 // generate paths using own variate generator
280 auto gen =
282 times.size() - 1, isTraining ? mcParams_.trainingSeed : mcParams_.seed,
284
285 for (auto s = std::next(irStates.begin(), 1); s != irStates.end(); ++s)
286 for (auto& r : s->second)
287 r.expand();
288
289 for (Size path = 0; path < nSamples; ++path) {
290 auto p = gen->next();
291 Real state = 0.0;
292 auto date = effectiveSimulationDates_.begin();
293 for (Size i = 0; i < times.size() - 1; ++i) {
294 state += stdDevs[i] * p.value[i][0];
295 ++date;
296 irStates[*date][0].data()[path] = state;
297 }
298 }
299
300 } else {
301
302 // case process size > 1 or we have injected paths, we use the normal process interface to evolve the process
303
304 QuantLib::ext::shared_ptr<MultiPathGeneratorBase> pathGen;
305
306 // build a temporary repository of the state prcess values, since we want to access them not path by path
307 // below - for efficiency reasons the loop over the paths should be the innermost loop there!
308
309 std::vector<std::vector<std::vector<Real>>> pathValues(
310 effectiveSimulationDates_.size() - 1,
311 std::vector<std::vector<Real>>(process->size(), std::vector<Real>(nSamples)));
312
313 if (injectedPathTimes_ == nullptr) {
314
315 // the usual path generator
316
317 if (auto tmp = QuantLib::ext::dynamic_pointer_cast<CrossAssetStateProcess>(process)) {
318 tmp->resetCache(timeGrid_.size() - 1);
319 }
320
321 auto pathGen =
325 for (Size i = 0; i < nSamples; ++i) {
326 MultiPath path = pathGen->next().value;
327 for (Size j = 0; j < effectiveSimulationDates_.size() - 1; ++j) {
328 for (Size k = 0; k < process->size(); ++k) {
329 pathValues[j][k][i] = path[k][positionInTimeGrid_[j + 1]];
330 }
331 }
332 }
333 } else {
334
335 // simple linear interpolation of injected paths, TODO explore the usage of Brownian Bridges here
336
337 std::vector<Real> relevantPathTimes;
338 std::vector<Size> relevantPathIndices;
339
340 for (Size j = 0; j < injectedPathRelevantTimeIndexes_->size(); ++j) {
341 size_t pathIdx = (*injectedPathRelevantPathIndexes_)[j];
342 size_t timeIdx = (*injectedPathRelevantTimeIndexes_)[j];
343 relevantPathTimes.push_back((*injectedPathTimes_)[timeIdx]);
344 relevantPathIndices.push_back(pathIdx);
345 }
346 Array y(relevantPathTimes.size());
347 auto pathInterpolator =
348 LinearFlat().interpolate(relevantPathTimes.begin(), relevantPathTimes.end(), y.begin());
349 pathInterpolator.enableExtrapolation();
350 for (Size i = 0; i < nSamples; ++i) {
351 for (Size k = 0; k < process->size(); ++k) {
352 for (Size j = 0; j < relevantPathTimes.size(); ++j) {
353 y[j] = (*injectedPaths_)[relevantPathIndices[j]][projectedStateProcessIndices_[k]][i];
354 }
355 pathInterpolator.update();
356 for (Size j = 0; j < times.size() - 1; ++j) {
357 pathValues[j][k][i] = pathInterpolator(times[j + 1]);
358 }
359 }
360 }
361 }
362
363 // FX, EQ, COMM indices
364 std::vector<std::vector<RandomVariable*>> rvs(
365 indices_.size(), std::vector<RandomVariable*>(effectiveSimulationDates_.size() - 1));
366 auto date = effectiveSimulationDates_.begin();
367 for (Size i = 0; i < effectiveSimulationDates_.size() - 1; ++i) {
368 ++date;
369 for (Size j = 0; j < indices_.size(); ++j) {
370 rvs[j][i] = &paths[*date][j];
371 rvs[j][i]->expand();
372 }
373 }
374 for (Size k = 0; k < indices_.size(); ++k) {
375 for (Size j = 1; j < effectiveSimulationDates_.size(); ++j) {
376 for (Size i = 0; i < nSamples; ++i) {
377 rvs[k][j - 1]->data()[i] = std::exp(pathValues[j - 1][indexPositionInProcess_[k]][i]);
378 }
379 }
380 }
381
382 // IR states per currency
383 std::vector<std::vector<RandomVariable*>> rvs2(
384 currencies_.size(), std::vector<RandomVariable*>(effectiveSimulationDates_.size() - 1));
385 auto date2 = effectiveSimulationDates_.begin();
386 for (Size i = 0; i < effectiveSimulationDates_.size() - 1; ++i) {
387 ++date2;
388 for (Size j = 0; j < currencies_.size(); ++j) {
389 rvs2[j][i] = &irStates[*date2][j];
390 rvs2[j][i]->expand();
391 }
392 }
393 for (Size k = 0; k < currencies_.size(); ++k) {
394 for (Size j = 1; j < effectiveSimulationDates_.size(); ++j) {
395 for (Size i = 0; i < nSamples; ++i) {
396 rvs2[k][j - 1]->data()[i] = pathValues[j - 1][currencyPositionInProcess_[k]][i];
397 }
398 }
399 }
400
401 // INF states per index, again we do not need to distinguish DK and JY here
402 std::vector<std::vector<RandomVariable*>> rvs3a(
403 infIndices_.size(), std::vector<RandomVariable*>(effectiveSimulationDates_.size() - 1));
404 std::vector<std::vector<RandomVariable*>> rvs3b(
405 infIndices_.size(), std::vector<RandomVariable*>(effectiveSimulationDates_.size() - 1));
406 auto date3 = effectiveSimulationDates_.begin();
407 for (Size i = 0; i < effectiveSimulationDates_.size() - 1; ++i) {
408 ++date3;
409 for (Size j = 0; j < infIndices_.size(); ++j) {
410 rvs3a[j][i] = &infStates[*date3][j].first;
411 rvs3b[j][i] = &infStates[*date3][j].second;
412 rvs3a[j][i]->expand();
413 rvs3b[j][i]->expand();
414 }
415 }
416 for (Size k = 0; k < infIndices_.size(); ++k) {
417 for (Size j = 1; j < effectiveSimulationDates_.size(); ++j) {
418 for (Size i = 0; i < nSamples; ++i) {
419 rvs3a[k][j - 1]->data()[i] = pathValues[j - 1][infIndexPositionInProcess_[k]][i];
420 rvs3b[k][j - 1]->data()[i] = pathValues[j - 1][infIndexPositionInProcess_[k] + 1][i];
421 }
422 }
423 }
424 }
425}
426
427RandomVariable GaussianCam::getIndexValue(const Size indexNo, const Date& d, const Date& fwd) const {
428 auto res = underlyingPaths_.at(d).at(indexNo);
429 if (comIndexInCam_[indexNo] != Null<Size>()) {
430 // handle com (TODO: performace optimization via vectorized version of com model)
431 RandomVariable tmp(res.size());
432 for (Size i = 0; i < tmp.size(); ++i) {
433 tmp.set(i, cam_->comModel(comIndexInCam_[indexNo])
434 ->forwardPrice(timeFromReference(d), timeFromReference(fwd != Null<Date>() ? fwd : d),
435 Array(1, std::log(res[i]))));
436 }
437 return tmp;
438 } else if (fwd != Null<Date>()) {
439 // handle fx, eq -> incorporate forwarding factor if applicable
440 auto ccy = std::find(currencies_.begin(), currencies_.end(), indexCurrencies_[indexNo]);
441 QL_REQUIRE(ccy != currencies_.end(), "GaussianCam::getIndexValue(): can not get currency for index #"
442 << indexNo << "(" << indices_.at(indexNo) << ")");
443 if (indices_[indexNo].isFx()) {
444 res *= getDiscount(std::distance(currencies_.begin(), ccy), d, fwd) / getDiscount(0, d, fwd);
445 } else if (eqIndexInCam_[indexNo] != Null<Size>()) {
446 // the CAM assumes a deterministic dividend curve for EQ
447 auto div = cam_->eqbs(eqIndexInCam_[indexNo])->equityDivYieldCurveToday();
448 res *= RandomVariable(size(), div->discount(fwd) / div->discount(d)) /
449 getDiscount(std::distance(currencies_.begin(), ccy), d, fwd,
450 cam_->eqbs(eqIndexInCam_[indexNo])->equityIrCurveToday());
451 } else if (comIndexInCam_[indexNo] != Null<Size>()) {
452
453 } else {
454 QL_FAIL("GaussianGam::getIndexValue(): did not recognise index #" << indexNo << "("
455 << indices_.at(indexNo));
456 }
457 }
458 return res;
459}
460
461RandomVariable GaussianCam::getIrIndexValue(const Size indexNo, const Date& d, const Date& fwd) const {
462 Date fixingDate = d;
463 if (fwd != Null<Date>())
464 fixingDate = fwd;
465 // ensure a valid fixing date
466 fixingDate = irIndices_[indexNo].second->fixingCalendar().adjust(fixingDate);
467 // look up required fixing in cache and return it if found
468 if (auto cacheValue = irIndexValueCache_.find(std::make_tuple(indexNo, d, fixingDate));
469 cacheValue != irIndexValueCache_.end()) {
470 return cacheValue->second;
471 }
472 // compute value, add to cache and return it
473 Size currencyIdx = irIndexPositionInCam_[indexNo];
474 LgmVectorised lgmv(cam_->irlgm1f(currencyIdx));
475 auto result =
476 lgmv.fixing(irIndices_[indexNo].second, fixingDate, timeFromReference(d), irStates_.at(d).at(currencyIdx));
477 irIndexValueCache_[std::make_tuple(indexNo, d, fixingDate)] = result;
478 return result;
479}
480
481RandomVariable GaussianCam::getInfIndexValue(const Size indexNo, const Date& d, const Date& fwd) const {
482 Date fixingDate = d;
483 Date obsDate = d;
484 if (fwd != Null<Date>())
485 fixingDate = fwd;
486 Size lag = getInflationSimulationLag(infIndices_[indexNo].second);
487 auto const& state = infStates_.at(obsDate + lag).at(indexNo);
488 Size camIndex = infIndexPositionInCam_[indexNo];
489 Real t = infIndices_[indexNo].second->zeroInflationTermStructure()->timeFromReference(obsDate + lag);
490 Real T = infIndices_[indexNo].second->zeroInflationTermStructure()->timeFromReference(fixingDate + lag);
491 QL_DEPRECATED_DISABLE_WARNING
492 bool isInterpolated = infIndices_[indexNo].second->interpolated();
493 QL_DEPRECATED_ENABLE_WARNING
494 Real baseFixing =
495 infIndices_[indexNo].second->fixing(infIndices_[indexNo].second->zeroInflationTermStructure()->baseDate());
496 RandomVariable result(size());
497
498 if (cam_->modelType(CrossAssetModel::AssetType::INF, camIndex) == CrossAssetModel::ModelType::DK) {
499 InfDkVectorised infdkv(*cam_);
500 RandomVariable baseFixingVec(size(), baseFixing);
501 QL_REQUIRE(t < T || close_enough(t, T), "infdkI: t (" << t << ") <= T (" << T << ") required");
502 auto dk = infdkv.infdkI(camIndex, t, T, state.first, state.second, isInterpolated);
503 result = baseFixingVec * dk.first * (fixingDate != obsDate ? dk.second : RandomVariable(size(), 1.0));
504 } else if (cam_->modelType(CrossAssetModel::AssetType::INF, camIndex) == CrossAssetModel::ModelType::JY) {
505 result = exp(state.second);
506 if (fixingDate != obsDate) {
507 // we need a forward cpi, TODO vectorise this computation
508 RandomVariable growthFactor(size());
509 growthFactor.expand();
510 for (Size p = 0; p < size(); ++p) {
511 growthFactor.data()[p] =
512 inflationGrowth(*cam_, indexNo, t, T, state.first[p], state.second[p], isInterpolated);
513 }
514 result *= growthFactor;
515 }
516 } else {
517 QL_FAIL("GaussianCam::getInfIndexValue(): unknown model type for inflation index "
518 << infIndices_[indexNo].first.name());
519 }
520 return result;
521}
522
523RandomVariable GaussianCam::fwdCompAvg(const bool isAvg, const std::string& indexInput, const Date& obsdate,
524 const Date& start, const Date& end, const Real spread, const Real gearing,
525 const Integer lookback, const Natural rateCutoff, const Natural fixingDays,
526 const bool includeSpread, const Real cap, const Real floor,
527 const bool nakedOption, const bool localCapFloor) const {
528 calculate();
529 auto ir = std::find_if(irIndices_.begin(), irIndices_.end(),
530 [&indexInput](const std::pair<IndexInfo, QuantLib::ext::shared_ptr<InterestRateIndex>>& p) {
531 return p.first.name() == indexInput;
532 });
533 QL_REQUIRE(ir != irIndices_.end(),
534 "GaussianCam::fwdComp() ir index " << indexInput << " not found, this is unexpected");
535 Size irIndexPos = irIndexPositionInCam_[std::distance(irIndices_.begin(), ir)];
536 LgmVectorised lgmv(cam_->lgm(irIndexPos)->parametrization());
537 auto on = QuantLib::ext::dynamic_pointer_cast<OvernightIndex>(ir->second);
538 QL_REQUIRE(on, "GaussianCam::fwdCompAvg(): expected on index for " << indexInput);
539 // only used to extract fixing and value dates
540 auto coupon = QuantLib::ext::make_shared<QuantExt::OvernightIndexedCoupon>(
541 end, 1.0, start, end, on, gearing, spread, Date(), Date(), DayCounter(), false, includeSpread, lookback * Days,
542 rateCutoff, fixingDays);
543 // get model time and state
544 Date effobsdate = std::max(referenceDate(), obsdate);
545 const auto& modelState = irStates_.at(effobsdate)[irIndexPos];
546 const auto& modelTime = timeFromReference(effobsdate);
547 if (isAvg) {
548 return lgmv.averagedOnRate(on, coupon->fixingDates(), coupon->valueDates(), coupon->dt(), rateCutoff,
549 includeSpread, spread, gearing, lookback * Days, cap, floor, localCapFloor,
550 nakedOption, modelTime, modelState);
551 } else {
552 return lgmv.compoundedOnRate(on, coupon->fixingDates(), coupon->valueDates(), coupon->dt(), rateCutoff,
553 includeSpread, spread, gearing, lookback * Days, cap, floor, localCapFloor,
554 nakedOption, modelTime, modelState);
555 }
556}
557
558RandomVariable GaussianCam::getDiscount(const Size idx, const Date& s, const Date& t) const {
559 return getDiscount(idx, s, t, Handle<YieldTermStructure>());
560}
561
562RandomVariable GaussianCam::getDiscount(const Size idx, const Date& s, const Date& t,
563 const Handle<YieldTermStructure>& targetCurve) const {
564 LgmVectorised lgmv(cam_->lgm(currencyPositionInCam_[idx])->parametrization());
565 return lgmv.discountBond(timeFromReference(s), curves_.front()->timeFromReference(t), irStates_.at(s)[idx],
566 targetCurve);
567}
568
570 LgmVectorised lgmv(cam_->lgm(currencyPositionInCam_[0])->parametrization());
571 return lgmv.numeraire(timeFromReference(s), irStates_.at(s)[0]);
572}
573
574Real GaussianCam::getFxSpot(const Size idx) const { return fxSpots_.at(idx)->value(); }
575
576RandomVariable GaussianCam::npv(const RandomVariable& amount, const Date& obsdate, const Filter& filter,
577 const boost::optional<long>& memSlot, const RandomVariable& addRegressor1,
578 const RandomVariable& addRegressor2) const {
579
580 calculate();
581
582 // short cut, if amount is deterministic and no memslot is given
583
584 if (amount.deterministic() && !memSlot)
585 return amount;
586
587 // if obsdate is today, take a plain expectation
588
589 if (obsdate == referenceDate())
590 return expectation(amount);
591
592 // build the state
593
594 std::vector<const RandomVariable*> state;
595
597 for (auto const& r : underlyingPaths_.at(obsdate))
598 state.push_back(&r);
599 }
600
601 // TODO we include zero vol ir states here, we could exclude them
603 for (auto const& r : irStates_.at(obsdate))
604 state.push_back(&r);
605 }
606
607 // valid for both DK and JY
609 for (auto const& r : infStates_.at(obsdate)) {
610 state.push_back(&r.first);
611 state.push_back(&r.second);
612 }
613 }
614
615 Size nModelStates = state.size();
616
617 // if memSlot is given we have to make sure the state always has the same size
618 if (addRegressor1.initialised() && (memSlot || !addRegressor1.deterministic()))
619 state.push_back(&addRegressor1);
620 if (addRegressor2.initialised() && (memSlot || !addRegressor2.deterministic()))
621 state.push_back(&addRegressor2);
622
623 Size nAddReg = state.size() - nModelStates;
624
625 // if the state is empty, return the plain expectation (no conditioning)
626
627 if (state.empty()) {
628 return expectation(amount);
629 }
630
631 // the regression model is given by coefficients and an optional coordinate transform
632
633 Array coeff;
634 Matrix coordinateTransform;
635
636 // if a memSlot is given and coefficients / coordinate transform are stored, we use them
637
638 bool haveStoredModel = false;
639
640 if (memSlot) {
641 if (auto it = storedRegressionModel_.find(*memSlot); it != storedRegressionModel_.end()) {
642 coeff = std::get<0>(it->second);
643 coordinateTransform = std::get<2>(it->second);
644 QL_REQUIRE(std::get<1>(it->second) == state.size(),
645 "GaussianCam::npv(): stored regression coefficients at mem slot "
646 << *memSlot << " are for state size " << std::get<1>(it->second) << ", actual state size is "
647 << state.size() << " (before possible coordinate transform).");
648 haveStoredModel = true;
649 }
650 }
651
652 // if we do not have retrieved a model in the previous step, we create it now
653
654 std::vector<RandomVariable> transformedState;
655
656 if(!haveStoredModel) {
657
658 // factor reduction to reduce dimensionalitty and handle collinearity
659
660 if (mcParams_.regressionVarianceCutoff != Null<Real>()) {
661 coordinateTransform = pcaCoordinateTransform(state, mcParams_.regressionVarianceCutoff);
662 transformedState = applyCoordinateTransform(state, coordinateTransform);
663 state = vec2vecptr(transformedState);
664 }
665
666 // train coefficients
667
668 coeff = regressionCoefficients(amount, state,
669 multiPathBasisSystem(state.size(), mcParams_.regressionOrder,
670 mcParams_.polynomType, std::min(size(), trainingSamples())),
671 filter, RandomVariableRegressionMethod::QR);
672 DLOG("GaussianCam::npv(" << ore::data::to_string(obsdate) << "): regression coefficients are " << coeff
673 << " (got model state size " << nModelStates << " and " << nAddReg
674 << " additional regressors, coordinate transform " << coordinateTransform.columns()
675 << " -> " << coordinateTransform.rows() << ")");
676
677 // store model if requried
678
679 if (memSlot) {
680 storedRegressionModel_[*memSlot] = std::make_tuple(coeff, nModelStates + nAddReg, coordinateTransform);
681 }
682
683 } else {
684
685 // apply the stored coordinate transform to the state
686
687 if(!coordinateTransform.empty()) {
688 transformedState = applyCoordinateTransform(state, coordinateTransform);
689 state = vec2vecptr(transformedState);
690 }
691 }
692
693 // compute conditional expectation and return the result
694
695 return conditionalExpectation(state,
696 multiPathBasisSystem(state.size(), mcParams_.regressionOrder, mcParams_.polynomType,
697 std::min(size(), trainingSamples())),
698 coeff);
699}
700
703 std::swap(irStates_, irStatesTraining_);
704 std::swap(infStates_, infStatesTraining_);
706 irIndexValueCache_.clear();
707}
708
710
711void GaussianCam::injectPaths(const std::vector<QuantLib::Real>* pathTimes,
712 const std::vector<std::vector<QuantExt::RandomVariable>>* paths,
713 const std::vector<size_t>* pathIndexes, const std::vector<size_t>* timeIndexes) {
714
715 if (pathTimes == nullptr) {
716 // reset injected path data
717 injectedPathTimes_ = nullptr;
718 injectedPaths_ = nullptr;
721 return;
722 }
723
724 QL_REQUIRE(!pathTimes->empty(), "GaussianCam::injectPaths(): injected path times empty");
725
726 QL_REQUIRE(pathTimes->size() == paths->size(), "GaussianCam::injectPaths(): path times ("
727 << pathTimes->size() << ") must match path size ("
728 << paths->size() << ")");
729
730 QL_REQUIRE(pathIndexes->size() == timeIndexes->size(),
731 "GaussianCam::injectPaths(): path indexes size (" << pathIndexes->size() << ") must match time indexes size ("
732 << timeIndexes->size() << ")");
733
734 QL_REQUIRE(projectedStateProcessIndices_.size() == cam_->dimension(),
735 "GaussianCam::injectPaths(): number of projected state process indices ("
736 << projectedStateProcessIndices_.size() << ") must match model dimension (" << cam_->dimension());
737
738 Size maxProjectedStateProcessIndex =
739 *std::max_element(projectedStateProcessIndices_.begin(), projectedStateProcessIndices_.end());
740
741 for (auto const& v : *paths) {
742 QL_REQUIRE(v.size() > maxProjectedStateProcessIndex, "GaussianCam::injectPaths(): dimension of variates ("
743 << v.size()
744 << ") must cover max projected state process index ("
745 << maxProjectedStateProcessIndex << ")");
746 overwriteModelSize_ = v.front().size();
747 }
748
749 injectedPathTimes_ = pathTimes;
750 injectedPaths_ = paths;
753 update();
754}
755
756} // namespace data
757} // namespace ore
std::pair< RandomVariable, RandomVariable > infdkI(const Size i, const Time t, const Time T, const RandomVariable &z, const RandomVariable &y, bool indexIsInterpolated) const
RandomVariable discountBond(const Time t, const Time T, const RandomVariable &x, const Handle< YieldTermStructure > &discountCurve=Handle< YieldTermStructure >()) const
RandomVariable fixing(const boost::shared_ptr< InterestRateIndex > &index, const Date &fixingDate, const Time t, const RandomVariable &x) const
RandomVariable averagedOnRate(const boost::shared_ptr< OvernightIndex > &index, const std::vector< Date > &fixingDates, const std::vector< Date > &valueDates, const std::vector< Real > &dt, const Natural rateCutoff, const bool includeSpread, const Real spread, const Real gearing, const Period lookback, Real cap, Real floor, const bool localCapFloor, const bool nakedOption, const Time t, const RandomVariable &x) const
RandomVariable numeraire(const Time t, const RandomVariable &x, const Handle< YieldTermStructure > &discountCurve=Handle< YieldTermStructure >()) const
RandomVariable compoundedOnRate(const boost::shared_ptr< OvernightIndex > &index, const std::vector< Date > &fixingDates, const std::vector< Date > &valueDates, const std::vector< Real > &dt, const Natural rateCutoff, const bool includeSpread, const Real spread, const Real gearing, const Period lookback, Real cap, Real floor, const bool localCapFloor, const bool nakedOption, const Time t, const RandomVariable &x) const
Interpolation interpolate(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin) const
GaussianCam(const Handle< CrossAssetModel > &cam, 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 std::set< Date > &simulationDates, const McParams &mcParams, const Size timeStepsPerYear=1, const IborFallbackConfig &iborFallbackConfig=IborFallbackConfig::defaultConfig(), const std::vector< Size > &projectedStateProcessIndices={}, const std::vector< std::string > &conditionalExpectationModelStates={})
Definition: gaussiancam.cpp:47
RandomVariable getInfIndexValue(const Size indexNo, const Date &d, const Date &fwd=Null< Date >()) const override
void performCalculations() const override
void releaseMemory() override
std::map< Date, std::vector< std::pair< RandomVariable, RandomVariable > > > infStates_
const std::vector< size_t > * injectedPathRelevantPathIndexes_
Size size() const override
Definition: gaussiancam.cpp:93
std::vector< Size > infIndexPositionInCam_
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
std::map< std::tuple< Size, Date, Date >, RandomVariable > irIndexValueCache_
std::vector< Size > infIndexPositionInProcess_
std::vector< Size > indexPositionInProcess_
void populatePathValues(const Size nSamples, std::map< Date, std::vector< RandomVariable > > &paths, std::map< Date, std::vector< RandomVariable > > &irStates, std::map< Date, std::vector< std::pair< RandomVariable, RandomVariable > > > &infStates, const std::vector< Real > &times, const bool isTraining) const
std::vector< Size > eqIndexInCam_
const std::vector< std::vector< QuantExt::RandomVariable > > * injectedPaths_
const std::vector< QuantLib::Real > * injectedPathTimes_
const Date & referenceDate() const override
std::map< long, std::tuple< Array, Size, Matrix > > storedRegressionModel_
std::map< Date, std::vector< RandomVariable > > underlyingPathsTraining_
std::map< Date, std::vector< std::pair< RandomVariable, RandomVariable > > > infStatesTraining_
std::vector< Size > currencyPositionInProcess_
std::vector< Size > comIndexInCam_
RandomVariable getNumeraire(const Date &s) const override
std::vector< Size > irIndexPositionInCam_
const McParams mcParams_
std::vector< Size > currencyPositionInCam_
const Handle< CrossAssetModel > cam_
std::map< Date, std::vector< RandomVariable > > underlyingPaths_
std::set< Date > effectiveSimulationDates_
std::map< Date, std::vector< RandomVariable > > irStates_
const std::vector< Handle< Quote > > fxSpots_
std::map< Date, std::vector< RandomVariable > > irStatesTraining_
RandomVariable getDiscount(const Size idx, const Date &s, const Date &t) const override
const std::vector< Size > projectedStateProcessIndices_
RandomVariable getIndexValue(const Size indexNo, const Date &d, const Date &fwd=Null< Date >()) const override
Size trainingSamples() const override
void resetNPVMem() override
const Size timeStepsPerYear_
void toggleTrainingPaths() const override
const std::vector< size_t > * injectedPathRelevantTimeIndexes_
void injectPaths(const std::vector< QuantLib::Real > *pathTimes, const std::vector< std::vector< QuantExt::RandomVariable > > *paths, const std::vector< size_t > *pathIndexes, const std::vector< size_t > *timeIndexes) override
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
const std::vector< std::string > indexCurrencies_
Definition: modelimpl.hpp:108
const std::set< Date > simulationDates_
Definition: modelimpl.hpp:109
SafeStack< Filter > filter
gaussian cross asset model for ir, fx, eq, com
Currency parseCurrency(const string &s)
Convert text to QuantLib::Currency.
Definition: parsers.cpp:290
Map text representations to QuantLib/QuantExt types.
Classes and functions for log message handling.
@ data
Definition: log.hpp:77
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
QuantLib::Date fixingDate(const QuantLib::Date &d, const QuantLib::Period obsLag, const QuantLib::Frequency freq, bool interpolated)
RandomVariable exp(RandomVariable x)
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
boost::shared_ptr< MultiPathGeneratorBase > makeMultiPathGenerator(const SequenceType s, const boost::shared_ptr< StochasticProcess > &process, const TimeGrid &timeGrid, const BigNatural seed, const SobolBrownianGenerator::Ordering ordering=SobolBrownianGenerator::Steps, const SobolRsg::DirectionIntegers directionIntegers=SobolRsg::JoeKuoD7)
Real inflationGrowth(const boost::shared_ptr< CrossAssetModel > &model, Size index, Time S, Time T, Real irState, Real rrState, bool indexIsInterpolated)
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)
boost::shared_ptr< MultiPathVariateGeneratorBase > makeMultiPathVariateGenerator(const SequenceType s, const Size dimension, const Size timeSteps, const BigNatural seed, const SobolBrownianGenerator::Ordering ordering, const SobolRsg::DirectionIntegers directionIntegers)
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
Size getInflationSimulationLag(const QuantLib::ext::shared_ptr< ZeroInflationIndex > &index)
Definition: utilities.cpp:660
Serializable Credit Default Swap.
Definition: namespaces.docs:23
Map text representations to QuantLib/QuantExt types.
bool deterministic() const
void set(const Size i, const Real v)
QuantExt::SequenceType sequenceType
Definition: model.hpp:54
QuantLib::SobolBrownianGenerator::Ordering sobolOrdering
Definition: model.hpp:59
QuantExt::SequenceType trainingSequenceType
Definition: model.hpp:55
QuantLib::SobolRsg::DirectionIntegers sobolDirectionIntegers
Definition: model.hpp:60
QuantLib::Real regressionVarianceCutoff
Definition: model.hpp:61
QuantLib::LsmBasisSystem::PolynomialType polynomType
Definition: model.hpp:58
std::vector< Size > steps
string conversion utilities
string name